From 735063766dd8cc1449536a997eeda55b8144817a Mon Sep 17 00:00:00 2001 From: Auto Differentiation Dev Team <107129969+auto-differentiation-dev@users.noreply.github.com> Date: Sun, 25 Oct 2026 18:40:42 +0000 Subject: [PATCH 1/2] build: link libxad from the xad 2.2 package Build QuantLib and the extension against the installed xad package (find_package(XAD CONFIG) via xad.get_cmake_dir()) instead of lib/xad, with thread-local tapes and the dynamic MSVC runtime. The extension finds libxad through an rpath to xad/lib; wheel repair excludes it. --- .github/dependabot.yml | 6 ++-- .gitmodules | 3 -- CHANGELOG.md | 3 +- CMakeLists.txt | 8 +++-- CONTRIBUTING.md | 14 ++++----- Python/QuantLib_Risks/CMakeLists.txt | 8 ++++- Python/QuantLib_Risks/__init__.py | 2 ++ cmake/PrebuiltQuantLib.cmake | 24 +++++---------- cmake/QLRXad.cmake | 45 ++++++++++++++++++++++++++++ cmake/QuantLibFromSource.cmake | 23 ++++++-------- lib/xad | 1 - pyproject.toml | 32 +++++++++++++------- tools/build-quantlib.cmake | 20 ++++++++++++- 13 files changed, 128 insertions(+), 61 deletions(-) create mode 100644 cmake/QLRXad.cmake delete mode 160000 lib/xad diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3585ab83..692c7ae4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,6 @@ # Weekly grouped updates for Python deps (pyproject.toml) and GitHub Actions. -# Submodules (QuantLib, XAD, QuantLibAAD, pybind11) are bumped manually: XAD and -# pybind11 must match the xad package exactly. +# Submodules (QuantLib, QuantLibAAD, pybind11) are bumped manually: pybind11 must +# match the xad package exactly. # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file version: 2 @@ -10,7 +10,7 @@ updates: schedule: interval: "weekly" ignore: - # pinned to the XAD in lib/xad + # one major.minor series, bumped manually - dependency-name: "xad" groups: python: diff --git a/.gitmodules b/.gitmodules index 32309105..ad4d6c9a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "lib/QuantLib"] path = lib/QuantLib url = https://github.com/lballabio/QuantLib.git -[submodule "lib/xad"] - path = lib/xad - url = https://github.com/auto-differentiation/xad.git [submodule "lib/QuantLibAAD"] path = lib/QuantLibAAD url = https://github.com/auto-differentiation/QuantLibAAD.git diff --git a/CHANGELOG.md b/CHANGELOG.md index c87c9cf7..48ab61a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.41.0] - Unreleased - Matches QuantLib and QuantLib Python bindings version 1.41 -- Builds against XAD 2.1 and QuantLibAAD 1.41 (formerly QuantLib-Risks-Cpp), requires xad 2.1 +- Builds against QuantLibAAD 1.41 (formerly QuantLib-Risks-Cpp) and the shared libxad of the installed xad 2.2 package (`xad>=2.2,<2.3`), which is not bundled +- Tapes are shared with xad through libxad, thread-local per Python thread; QuantLib no longer patches `xad.adj_1st.Tape` - Builds the unmodified QuantLib 1.41 release, no QuantLib patches - One-step build with scikit-build-core; wheels for Python 3.10-3.14 on Linux, macOS and Windows - `tools/sync-swig.sh` syncs `SWIG/` with a QuantLib-SWIG release, re-applying the XAD changes in `SWIG/patches/` diff --git a/CMakeLists.txt b/CMakeLists.txt index 56b0fc6e..8c2bf7a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,17 +35,19 @@ endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -# static MSVC runtime everywhere (QuantLib, XAD, the module), as in xad-py -set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +# dynamic MSVC runtime everywhere (QuantLib, the module), as libxad is a DLL +set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") option(QLR_QUANTLIB_ONLY - "Only build and install QuantLib + XAD + QuantLibAAD, for reuse via QLR_QUANTLIB_PREFIX" + "Only build and install QuantLib + QuantLibAAD, for reuse via QLR_QUANTLIB_PREFIX" OFF) set(QLR_QUANTLIB_PREFIX "" CACHE PATH "Install prefix of a QLR_QUANTLIB_ONLY build to reuse (empty: build QuantLib from lib/)") list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") +include(QLRXad) + if(QLR_QUANTLIB_PREFIX AND NOT QLR_QUANTLIB_ONLY) include(PrebuiltQuantLib) else() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e9929364..13f55960 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,9 +52,9 @@ accepted: The package is built in one step by [scikit-build-core](https://scikit-build-core.readthedocs.io) (see `pyproject.toml`): CMake builds QuantLib as a static library together with -[XAD](https://github.com/auto-differentiation/xad) and [QuantLibAAD](https://github.com/auto-differentiation/QuantLibAAD) from the `lib/` submodules, -then the SWIG module from `SWIG/` and `Python/QuantLib_Risks/`. +against [XAD](https://github.com/auto-differentiation/xad) from the installed +[xad](https://pypi.org/project/xad/) package (`xad.get_cmake_dir()`), then the SWIG module from `SWIG/` and `Python/QuantLib_Risks/`. Requirements: Python 3.10+, and a C++17 compiler. CMake, Ninja and SWIG are installed as build dependencies. @@ -72,16 +72,16 @@ cd /tmp && python -m pytest --import-mode=importlib /path/to/QuantLib-Risks-Py/P Notes: -- The binary module shares XAD's tape and `Real` type with the - [xad](https://pypi.org/project/xad/) package, so `lib/xad` and `lib/pybind11` must be the - exact versions xad is built with, and the `xad` requirement is pinned to that XAD - major.minor series. Update them together. +- The binary module links the xad package's shared libxad (found at runtime through the + rpath `../xad/lib`, never bundled in the wheel) and shares its `Real` type, so + `lib/pybind11` must be the exact version xad is built with, and the `xad` requirement is + pinned to one major.minor series. Update them together. - `lib/QuantLib` is an unmodified QuantLib release; AAD support comes from QuantLibAAD. - Building QuantLib takes most of the time. To reuse it across builds (e.g. several Python versions), install it once and point the package build at it: ```bash - cmake -DPREFIX=$PWD/build/quantlib-prefix -P tools/build-quantlib.cmake + cmake -DPREFIX=$PWD/build/quantlib-prefix -DPYTHON=$(which python) -P tools/build-quantlib.cmake pip install -v . -Ccmake.define.QLR_QUANTLIB_PREFIX=$PWD/build/quantlib-prefix ``` diff --git a/Python/QuantLib_Risks/CMakeLists.txt b/Python/QuantLib_Risks/CMakeLists.txt index 841352ab..f31cc43e 100644 --- a/Python/QuantLib_Risks/CMakeLists.txt +++ b/Python/QuantLib_Risks/CMakeLists.txt @@ -35,13 +35,19 @@ swig_add_library(QuantLib_Risks LANGUAGE python OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${QLR_SWIG_SOURCE} converters.cpp) -target_link_libraries(QuantLib_Risks PRIVATE QuantLib::QuantLib pybind11::module) +target_link_libraries(QuantLib_Risks PRIVATE QuantLib::QuantLib XAD::xad pybind11::module) target_compile_definitions(QuantLib_Risks PRIVATE QL_XAD=1 QLR_VERSION=\"${QLR_VERSION}\" QLR_HEX_VERSION=${QLR_HEX_VERSION}) target_include_directories(QuantLib_Risks PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) set_target_properties(QuantLib_Risks PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}") +# libxad from the installed xad package (on Windows, importing xad adds its DLL directory) +if(APPLE) + set_target_properties(QuantLib_Risks PROPERTIES INSTALL_RPATH "@loader_path/../xad/lib") +elseif(UNIX) + set_target_properties(QuantLib_Risks PROPERTIES INSTALL_RPATH "$ORIGIN/../xad/lib") +endif() if(NOT CMAKE_BUILD_TYPE MATCHES "Debug|RelWithDebInfo") pybind11_strip(QuantLib_Risks) endif() diff --git a/Python/QuantLib_Risks/__init__.py b/Python/QuantLib_Risks/__init__.py index d1cd69d8..397f4a8e 100644 --- a/Python/QuantLib_Risks/__init__.py +++ b/Python/QuantLib_Risks/__init__.py @@ -31,6 +31,8 @@ FOR A PARTICULAR PURPOSE. See the license for more details. """ +import xad # loads libxad, which the binary module links against + from .QuantLib_Risks import * if XAD_ENABLED: diff --git a/cmake/PrebuiltQuantLib.cmake b/cmake/PrebuiltQuantLib.cmake index b65edf99..f3d22cdc 100644 --- a/cmake/PrebuiltQuantLib.cmake +++ b/cmake/PrebuiltQuantLib.cmake @@ -1,22 +1,14 @@ -# Uses QuantLib, XAD and QuantLibAAD installed by a QLR_QUANTLIB_ONLY build in -# QLR_QUANTLIB_PREFIX. Defines QuantLib::QuantLib. +# Uses QuantLib and QuantLibAAD installed by a QLR_QUANTLIB_ONLY build in +# QLR_QUANTLIB_PREFIX, with XAD::xad from QLRXad. Defines QuantLib::QuantLib. file(TO_CMAKE_PATH "${QLR_QUANTLIB_PREFIX}" QLR_QUANTLIB_PREFIX) list(PREPEND CMAKE_PREFIX_PATH "${QLR_QUANTLIB_PREFIX}") find_package(QuantLibAAD CONFIG REQUIRED NO_DEFAULT_PATH PATHS "${QLR_QUANTLIB_PREFIX}") -# guard against a stale prefix: XAD must match lib/xad and share the tape with xad-py -file(READ "${PROJECT_SOURCE_DIR}/lib/xad/VERSION" xad_src_version) -string(STRIP "${xad_src_version}" xad_src_version) -get_target_property(xad_includes XAD::xad INTERFACE_INCLUDE_DIRECTORIES) -set(xad_no_tls) -foreach(dir IN LISTS xad_includes) - if(EXISTS "${dir}/XAD/Config.hpp") - file(STRINGS "${dir}/XAD/Config.hpp" xad_no_tls REGEX "^#define XAD_NO_THREADLOCAL") - endif() -endforeach() -if(NOT XAD_VERSION STREQUAL xad_src_version OR NOT xad_no_tls) - message(FATAL_ERROR "XAD in ${QLR_QUANTLIB_PREFIX} (${XAD_VERSION}) does not match " - "lib/xad (${xad_src_version}) with XAD_NO_THREADLOCAL; rebuild it") +# guard against a stale prefix: QuantLib must be built with the XAD found by QLRXad +include("${QuantLibAAD_DIR}/QLRXadVersion.cmake" OPTIONAL) +if(NOT QLR_QUANTLIB_XAD_VERSION STREQUAL XAD_VERSION) + message(FATAL_ERROR "QuantLib in ${QLR_QUANTLIB_PREFIX} was built with XAD " + "'${QLR_QUANTLIB_XAD_VERSION}', not ${XAD_VERSION} (${XAD_DIR}); rebuild it") endif() -message(STATUS "Using prebuilt QuantLib ${QuantLib_VERSION} and XAD ${XAD_VERSION} from ${QLR_QUANTLIB_PREFIX}") +message(STATUS "Using prebuilt QuantLib ${QuantLib_VERSION} from ${QLR_QUANTLIB_PREFIX}") diff --git a/cmake/QLRXad.cmake b/cmake/QLRXad.cmake new file mode 100644 index 00000000..fe29a387 --- /dev/null +++ b/cmake/QLRXad.cmake @@ -0,0 +1,45 @@ +############################################################################## +# +# This file is part of QuantLib-Risks, a Python wrapper for QuantLib enabled +# for risk computation using automatic differentiation. It uses XAD, +# a fast and comprehensive C++ library for automatic differentiation. +# +# Copyright (C) 2010-2026 Xcelerit Computing Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +# XAD from the installed xad Python package (shared libxad, tape shared with xad-py). +# XAD_DIR overrides the package lookup. Defines XAD::xad. + +if(NOT XAD_DIR) + find_package(Python 3.10 REQUIRED COMPONENTS Interpreter) + execute_process( + COMMAND "${Python_EXECUTABLE}" -c "import xad; print(xad.get_cmake_dir())" + OUTPUT_VARIABLE xad_cmake_dir OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE xad_rc) + if(NOT xad_rc EQUAL 0) + message(FATAL_ERROR "xad not importable by ${Python_EXECUTABLE}: pip install \"xad>=2.2,<2.3\"") + endif() + file(TO_CMAKE_PATH "${xad_cmake_dir}" xad_cmake_dir) + set(XAD_DIR "${xad_cmake_dir}" CACHE PATH "XAD CMake package directory" FORCE) +endif() + +find_package(XAD 2.2 CONFIG REQUIRED) +if(XAD_VERSION VERSION_GREATER_EQUAL 2.3 OR NOT XAD_SHARED_LIBS OR XAD_NO_THREADLOCAL) + message(FATAL_ERROR "XAD ${XAD_VERSION} in ${XAD_DIR}: need xad 2.2.x with shared libxad and " + "thread-local tapes") +endif() +message(STATUS "XAD ${XAD_VERSION}: ${XAD_DIR}") diff --git a/cmake/QuantLibFromSource.cmake b/cmake/QuantLibFromSource.cmake index 9fdd2cb3..87ababab 100644 --- a/cmake/QuantLibFromSource.cmake +++ b/cmake/QuantLibFromSource.cmake @@ -1,21 +1,10 @@ -# Builds QuantLib as a static library with XAD and QuantLibAAD from lib/. -# Defines QuantLib::QuantLib. With QLR_QUANTLIB_ONLY, builds and installs only these. +# Builds QuantLib as a static library with QuantLibAAD from lib/, against XAD::xad +# (QLRXad). Defines QuantLib::QuantLib. With QLR_QUANTLIB_ONLY, builds and installs only these. set(QLR_LIB_DIR "${PROJECT_SOURCE_DIR}/lib") include(QLRBoost) -# XAD: the tape is shared with xad-py, so this must match its configuration -set(XAD_ENABLE_TESTS OFF CACHE BOOL "" FORCE) -set(XAD_NO_THREADLOCAL ON CACHE BOOL "" FORCE) -set(XAD_STATIC_MSVC_RUNTIME ON CACHE BOOL "" FORCE) -if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$") - set(XAD_SIMD_OPTION AVX CACHE STRING "XAD SIMD instruction set") -else() - # no arch flags, XAD would pick -mavx on aarch64 Linux - set(XAD_SIMD_OPTION NONE CACHE STRING "XAD SIMD instruction set") -endif() - # QuantLib: static, no tests/examples/benchmark set(BUILD_SHARED_LIBS OFF) foreach(opt QL_BUILD_TEST_SUITE QL_BUILD_EXAMPLES QL_BUILD_BENCHMARK @@ -23,7 +12,7 @@ foreach(opt QL_BUILD_TEST_SUITE QL_BUILD_EXAMPLES QL_BUILD_BENCHMARK set(${opt} OFF CACHE BOOL "" FORCE) endforeach() set(QL_NULL_AS_FUNCTIONS ON CACHE BOOL "" FORCE) -set(QL_EXTERNAL_SUBDIRECTORIES "${QLR_LIB_DIR}/xad;${QLR_LIB_DIR}/QuantLibAAD" CACHE STRING "" FORCE) +set(QL_EXTERNAL_SUBDIRECTORIES "${QLR_LIB_DIR}/QuantLibAAD" CACHE STRING "" FORCE) set(QL_EXTRA_LINK_LIBRARIES QuantLibAAD CACHE STRING "" FORCE) # QuantLib's find_package(Boost) (FindBoost module) uses the headers from QLRBoost set(CMAKE_POLICY_DEFAULT_CMP0167 OLD) @@ -65,3 +54,9 @@ if(QLR_BOOST_DOWNLOADED) install(DIRECTORY "${QLR_BOOST_INCLUDE_DIR}/boost" DESTINATION "${QL_INSTALL_INCLUDEDIR}") endif() endif() + +if(QLR_QUANTLIB_ONLY) + # checked by PrebuiltQuantLib: the extension must use the same XAD + file(WRITE "${CMAKE_BINARY_DIR}/QLRXadVersion.cmake" "set(QLR_QUANTLIB_XAD_VERSION ${XAD_VERSION})\n") + install(FILES "${CMAKE_BINARY_DIR}/QLRXadVersion.cmake" DESTINATION "${QLAAD_INSTALL_CMAKEDIR}") +endif() diff --git a/lib/xad b/lib/xad deleted file mode 160000 index 71b381ad..00000000 --- a/lib/xad +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 71b381ad4a322d7ed5eb8b2b4b79a20c92c646c6 diff --git a/pyproject.toml b/pyproject.toml index 71cbb300..8c0359ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ [build-system] # swig 4.5 drops the PyInt_* macros used in SWIG/ -requires = ["scikit-build-core>=0.11", "swig>=4.4,<4.5"] +requires = ["scikit-build-core>=0.11", "swig>=4.4,<4.5", "xad>=2.2,<2.3"] build-backend = "scikit_build_core.build" [project] @@ -70,8 +70,8 @@ classifiers = [ "Topic :: Scientific/Engineering", "Topic :: Software Development", ] -# the binary module shares xad's tape and Real type: same XAD/pybind11 build -dependencies = ["xad==2.1.*"] +# the binary module links xad's libxad and shares its Real type (same pybind11) +dependencies = ["xad>=2.2,<2.3"] [project.urls] Homepage = "https://auto-differentiation.github.io" @@ -99,9 +99,6 @@ sdist.exclude = [ "lib/QuantLib/Examples", "lib/QuantLib/test-suite", "lib/QuantLib/fuzz-test-suite", - "lib/xad/docs", - "lib/xad/samples", - "lib/xad/test", "lib/pybind11/docs", "lib/pybind11/tests", ] @@ -111,16 +108,29 @@ build = ["cp310-*", "cp311-*", "cp312-*", "cp313-*", "cp314-*"] skip = ["cp3??t-*"] # no free-threaded builds archs = ["native"] # one arch per runner build-verbosity = 1 -# QuantLib (with XAD and QuantLibAAD) is built once per platform and reused -# by all wheels through QLR_QUANTLIB_PREFIX -before-all = "cmake -DPREFIX={project}/build/quantlib-prefix -P {project}/tools/build-quantlib.cmake" +# QuantLib (with QuantLibAAD) is built once per platform and reused by all +# wheels through QLR_QUANTLIB_PREFIX, against the XAD of a xad package that +# build-quantlib.cmake installs (XAD_REQUIREMENT) +before-all = "cmake \"-DXAD_REQUIREMENT=xad @ git+https://github.com/auto-differentiation/xad-py.git@3a69afb69feb70e8965e924f4c13711bab2404e5\" -DPREFIX={project}/build/quantlib-prefix -P {project}/tools/build-quantlib.cmake" config-settings = { "cmake.define.QLR_QUANTLIB_PREFIX" = "{project}/build/quantlib-prefix" } test-groups = ["test"] -# xad 2.1 is released together with this package: until then, test against xad-py main -before-test = "pip install \"xad @ git+https://github.com/auto-differentiation/xad-py.git@7961b377f2cbbfec994193f55c23d7be34a72b5c\"" +# xad 2.2 is released together with this package: until then, build and test against +# xad-py from git (drop before-build, build-frontend and before-test, and the +# XAD_REQUIREMENT above, once it is on PyPI) +before-build = "pip install \"scikit-build-core>=0.11\" \"swig>=4.4,<4.5\" cmake ninja \"xad @ git+https://github.com/auto-differentiation/xad-py.git@3a69afb69feb70e8965e924f4c13711bab2404e5\"" +build-frontend = { name = "build", args = ["--no-isolation"] } +before-test = "pip install \"xad @ git+https://github.com/auto-differentiation/xad-py.git@3a69afb69feb70e8965e924f4c13711bab2404e5\"" # importlib mode tests the installed package; Python/test would clash with the stdlib test package test-command = "pytest -p no:cacheprovider --import-mode=importlib {project}/Python/test" +# libxad comes from the xad package (rpath to xad/lib), never bundled +[tool.cibuildwheel.linux] +repair-wheel-command = "auditwheel repair --exclude libxad.so -w {dest_dir} {wheel}" + +[tool.cibuildwheel.windows] +repair-wheel-command = "pip install delvewheel && delvewheel repair --exclude xad.dll -w {dest_dir} {wheel}" + [tool.cibuildwheel.macos] +repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} --exclude libxad.dylib -w {dest_dir} -v {wheel}" # one target for the QuantLib build (before-all) and all wheels (raised to 11.0 on arm64) environment = { MACOSX_DEPLOYMENT_TARGET = "10.15" } diff --git a/tools/build-quantlib.cmake b/tools/build-quantlib.cmake index dee64314..bfab1caf 100644 --- a/tools/build-quantlib.cmake +++ b/tools/build-quantlib.cmake @@ -40,6 +40,24 @@ foreach(dir PREFIX BUILD_DIR) get_filename_component(${dir} "${${dir}}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}") endforeach() +if(NOT XAD_DIR) + if(NOT PYTHON) + find_program(PYTHON NAMES python3.14 python3.13 python3.12 python3.11 python3.10 python3 python + REQUIRED) + endif() + if(XAD_REQUIREMENT) + set(venv "${BUILD_DIR}/xad-venv") + execute_process(COMMAND "${PYTHON}" -m venv "${venv}" COMMAND_ERROR_IS_FATAL ANY) + find_program(venv_python NAMES python python3 PATHS "${venv}/bin" "${venv}/Scripts" + NO_DEFAULT_PATH NO_CACHE REQUIRED) + set(PYTHON "${venv_python}") + execute_process(COMMAND "${PYTHON}" -m pip install "${XAD_REQUIREMENT}" COMMAND_ERROR_IS_FATAL ANY) + endif() + execute_process(COMMAND "${PYTHON}" -c "import xad; print(xad.get_cmake_dir())" + OUTPUT_VARIABLE XAD_DIR OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY) +endif() +file(TO_CMAKE_PATH "${XAD_DIR}" XAD_DIR) + if(DEFINED ENV{CMAKE_BUILD_PARALLEL_LEVEL}) set(jobs $ENV{CMAKE_BUILD_PARALLEL_LEVEL}) else() @@ -47,7 +65,7 @@ else() endif() foreach(step - "-S;${source_dir};-B;${BUILD_DIR};-DQLR_QUANTLIB_ONLY=ON;-DCMAKE_BUILD_TYPE=Release;-DCMAKE_INSTALL_PREFIX=${PREFIX}" + "-S;${source_dir};-B;${BUILD_DIR};-DQLR_QUANTLIB_ONLY=ON;-DCMAKE_BUILD_TYPE=Release;-DCMAKE_INSTALL_PREFIX=${PREFIX};-DXAD_DIR=${XAD_DIR}" "--build;${BUILD_DIR};--config;Release;--parallel;${jobs}" "--install;${BUILD_DIR};--config;Release") execute_process(COMMAND ${CMAKE_COMMAND} ${step} COMMAND_ERROR_IS_FATAL ANY) From 58cbb057b729fa818984b99217708caf48f17c88 Mon Sep 17 00:00:00 2001 From: Auto Differentiation Dev Team <107129969+auto-differentiation-dev@users.noreply.github.com> Date: Sun, 25 Oct 2026 18:40:42 +0000 Subject: [PATCH 2/2] remove the tape activation monkey-patch QuantLib and xad share the active tape through libxad, so xad.Tape no longer needs wrapping. --- Python/QuantLib_Risks/__init__.py | 35 ------------------------------- SWIG/tape.i | 14 +------------ 2 files changed, 1 insertion(+), 48 deletions(-) diff --git a/Python/QuantLib_Risks/__init__.py b/Python/QuantLib_Risks/__init__.py index 397f4a8e..9263c3cc 100644 --- a/Python/QuantLib_Risks/__init__.py +++ b/Python/QuantLib_Risks/__init__.py @@ -54,41 +54,6 @@ def PairDoubleVector( ) -> Tuple[List[Real], List[Real]]: return ([Real(x) for x in x1], [Real(x) for x in x2]) - # monkey-patch the tape activation function to register this tape with QuantLib's internal - # global tape pointer (we have 2 due to double static linking) - def _wrap_tape_activation(): - original_enter = Tape.__enter__ - - def _tape_enter(t: Tape): - t = original_enter(t) - _QuantLib_Risks._activate_tape(t) - return t - - original_exit = Tape.__exit__ - - def _tape_exit(t: Tape, *args, **kwargs): - original_exit(t, *args, **kwargs) - _QuantLib_Risks._deactivate_tape() - - original_activate = Tape.activate - - def _tape_activate(t: Tape): - original_activate(t) - _QuantLib_Risks._activate_tape(t) - - original_deactivate = Tape.deactivate - - def _tape_deactivate(t: Tape): - original_deactivate(t) - _QuantLib_Risks._deactivate_tape() - - Tape.__enter__ = _tape_enter - Tape.__exit__ = _tape_exit - Tape.activate = _tape_activate - Tape.deactivate = _tape_deactivate - - _wrap_tape_activation() - if hasattr(_QuantLib_Risks, "__version__"): __version__ = _QuantLib_Risks.__version__ elif hasattr(_QuantLib_Risks.cvar, "__version__"): diff --git a/SWIG/tape.i b/SWIG/tape.i index 634870ae..b0a2fc96 100644 --- a/SWIG/tape.i +++ b/SWIG/tape.i @@ -1,6 +1,5 @@ /****************************************************************************** - * Tape activation functions to ensure an xad global tape instance - * matches the one in QuantLib-Risks. + * Conversion of xad.adj_1st.Tape arguments to xad::Tape&. * * This file is part of QuantLib-Risks, a Python wrapper for QuantLib enabled * for risk computation using automatic differentiation. It uses XAD, @@ -30,14 +29,6 @@ #include "converters.hpp" #include - void _activate_tape(xad::Tape& t) { - xad::Tape::setActive(&t); - } - - void _deactivate_tape() { - xad::Tape::deactivateAll(); - } - typedef xad::Tape Tape; %} @@ -59,7 +50,4 @@ typedef xad::Tape Tape; $1 = check_Tape($input) ? 1 : 0; } -void _activate_tape(xad::Tape& t); -void _deactivate_tape(); - #endif