Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
c84d01c
forge integration
da-roth Dec 12, 2025
a3605f9
Revert "forge integration"
da-roth Dec 15, 2025
98c276b
forge integration
da-roth Dec 15, 2025
6c331dc
up
da-roth Dec 15, 2025
15e8808
use setBackend
da-roth Dec 19, 2025
b08fd88
updates due to xad-jit
da-roth Dec 19, 2025
33e3d69
updated backend naming
da-roth Dec 19, 2025
8494c87
graph updates with jitnode
da-roth Dec 19, 2025
36eacb8
first refactor
da-roth Dec 22, 2025
311d387
revert changes on fork
da-roth Dec 22, 2025
2591bb5
update path
da-roth Dec 22, 2025
41dc182
update ci
da-roth Dec 22, 2025
a4021b2
remove compatibility layer
da-roth Dec 22, 2025
66b8ea2
updates
da-roth Dec 22, 2025
7771b93
update backend namespaces
da-roth Dec 25, 2025
b68d6f6
update c api workflow
da-roth Dec 25, 2025
7c75cd1
update benchmark yaml
da-roth Dec 25, 2025
8e1ce66
fix benchmark yaml
da-roth Dec 25, 2025
f7202e4
update ci.yaml
da-roth Dec 30, 2025
fbda9f6
updates
da-roth Dec 31, 2025
b8ca808
updated ci.yaml
da-roth Dec 31, 2025
95e634a
update ci.yaml
da-roth Dec 31, 2025
98d8b86
updated win build
da-roth Dec 31, 2025
c17f541
fix benchmark baseline
da-roth Jan 2, 2026
ed68306
updates
da-roth Jan 2, 2026
cf5030b
added benchmark_utils
da-roth Jan 2, 2026
8467808
temporarily use xad-jit
da-roth Jan 5, 2026
58267e1
up
da-roth Jan 5, 2026
3642cfa
up
da-roth Jan 7, 2026
7cda13d
use double forgebackend
da-roth Jan 9, 2026
3b5eb06
remove git ignore
da-roth Jan 10, 2026
ed5f877
update ci to use original xad
da-roth Jan 10, 2026
ad5dead
update reference in benchmark yaml
da-roth Jan 10, 2026
329aa79
new benchmarks.yaml
da-roth Jan 10, 2026
6bdd4d6
updated workflows
da-roth Jan 10, 2026
6275901
updates
da-roth Jan 10, 2026
6b74c54
up
da-roth Jan 10, 2026
608625e
udpated benchmark workflows
da-roth Jan 10, 2026
277b889
upda
da-roth Jan 10, 2026
9216536
udpates on benchmarks
da-roth Jan 10, 2026
814366a
added benchmark
da-roth Jan 10, 2026
c1e7296
added win and renamed a bit
da-roth Jan 10, 2026
ee695f8
up
da-roth Jan 10, 2026
86bfb8f
up
da-roth Jan 10, 2026
aa4a7da
update
da-roth Jan 10, 2026
43dfda7
remove unneeded workflows
da-roth Jan 10, 2026
957b93a
cleaning
da-roth Jan 10, 2026
1c6b0f9
udpates
da-roth Jan 10, 2026
c426d8e
re-route to original
da-roth Jan 10, 2026
9bc49c7
up
da-roth Jan 11, 2026
caae75e
Revert "up"
da-roth Jan 11, 2026
73cc721
remove copying around stuff
da-roth Jan 11, 2026
8e0d3ad
fixed config
da-roth Jan 14, 2026
2bbd67a
fix QL
da-roth Jan 14, 2026
f430020
up
da-roth Jan 14, 2026
cf239ff
up readme
da-roth Jan 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
916 changes: 616 additions & 300 deletions .github/workflows/ci.yaml

Large diffs are not rendered by default.

1,555 changes: 1,555 additions & 0 deletions .github/workflows/ql-benchmarks.yaml

Large diffs are not rendered by default.

57 changes: 55 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
##############################################################################
#
#
#
# This file is part of QuantLib-Risks, an adaptor module to enable using XAD with
# QuantLib. XAD is a fast and comprehensive C++ library for
Expand All @@ -19,16 +19,69 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
##############################################################################

option(QLRISKS_DISABLE_AAD "Disable using XAD for QuantLib's Real, allowing to run samples with double" OFF)
option(QLRISKS_ENABLE_FORGE "Enable Forge JIT backend via xad-forge" OFF)
option(QLRISKS_USE_FORGE_CAPI "Use Forge C API instead of C++ API for binary compatibility" OFF)

add_subdirectory(ql)
if(MSVC)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
add_subdirectory(Examples)

##############################################################################
# QLRisks-Forge integration via xad-forge
# NOTE: This must be defined BEFORE test-suite so tests can link to it
##############################################################################

if(QLRISKS_ENABLE_FORGE)
message(STATUS "QLRisks-Forge: Looking for xad-forge...")

# Pass through the C API option to xad-forge
set(XAD_FORGE_USE_CAPI ${QLRISKS_USE_FORGE_CAPI} CACHE BOOL "" FORCE)

# Option 1: Check if xad-forge was added as subdirectory
if(TARGET xad-forge)
message(STATUS "QLRisks-Forge: Found xad-forge target (subdirectory mode)")
set(XAD_FORGE_FOUND TRUE)
endif()

# Option 2: Try find_package for pre-built xad-forge
if(NOT XAD_FORGE_FOUND)
find_package(xad-forge CONFIG QUIET)
if(xad-forge_FOUND)
message(STATUS "QLRisks-Forge: Found xad-forge package (pre-built mode)")
set(XAD_FORGE_FOUND TRUE)
endif()
endif()

if(XAD_FORGE_FOUND)
# Create qlrisks-forge as an INTERFACE library wrapping xad-forge
add_library(qlrisks-forge INTERFACE)
add_library(QLRisks::forge ALIAS qlrisks-forge)

target_link_libraries(qlrisks-forge INTERFACE
XADForge::xad-forge
)

target_compile_definitions(qlrisks-forge INTERFACE QLRISKS_HAS_FORGE=1)

message(STATUS "QLRisks-Forge: Configured with xad-forge")
else()
message(STATUS "QLRisks-Forge: xad-forge not found - ForgeBackend will not be available")
message(STATUS "QLRisks-Forge: To enable, either:")
message(STATUS " 1. Add xad-forge as subdirectory")
message(STATUS " 2. Set CMAKE_PREFIX_PATH to xad-forge installation")
endif()
endif()

##############################################################################
# Test suite (after QLRisks::forge is defined)
##############################################################################

if(NOT QLRISKS_DISABLE_AAD)
# the test suite is not supporting double
add_subdirectory(test-suite)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Real priceMulticurveBootstrappingSwap(const std::vector<Real>& depos,
auto endDate = q.first.second;
auto quote = q.second;
auto helper =
ext::make_shared<DatedOISRateHelper>(startDate, endDate, Handle<Quote>(quote), eonia);
ext::make_shared<OISRateHelper>(startDate, endDate, Handle<Quote>(quote), eonia);
eoniaInstruments.push_back(helper);
}

Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ This repository contains integration headers, examples, and tests required
for this integration.
It is not usable stand-alone.

## JIT Compilation Support

XAD is optimized for computing sensitivities efficiently in a single evaluation pass using adjoint mode. For workflows that require repeated evaluation across many scenarios—such as Monte Carlo simulations, XVA calculations, regulatory stress testing, or scenario-based risk analysis—XAD also supports recording computations into a [`JITGraph`](https://auto-differentiation.github.io/xad/ref/jit/) that can be compiled and re-evaluated efficiently using a JIT backend. The [xad-forge](https://github.com/da-roth/xad-forge) library provides Forge-based backends for this purpose, including vectorized AVX execution. See the [xad-forge README](https://github.com/da-roth/xad-forge#when-to-use-jit) for guidance on when JIT compilation is beneficial.

The repository includes a [swaption benchmark](.github/workflows/ql-benchmarks.yaml) that demonstrates a hybrid workflow: curve bootstrapping with XAD's tape, followed by Monte Carlo pricing with JIT-compiled evaluation, comparing tape-based, JIT, and JIT-AVX performance.

## Getting Started

For detailed build instructions with [XAD](https://auto-differentiation.github.io) and [QuantLib](https://www.quantlib.org), please refer to the [XAD documentation site](https://auto-differentiation.github.io/quantlib-risks/cxx/).
Expand Down
122 changes: 120 additions & 2 deletions test-suite/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
option(QLRISKS_BUILD_TEST_SUITE "Build the QuantLib-Risks test suite" OFF)
option(QLRISKS_ENABLE_FORGE_TESTS "Enable Forge JIT tests (requires Forge)" OFF)
option(QLRISKS_BUILD_BENCHMARK "Build the QuantLib-Risks benchmark suite (Boost.Test)" OFF)
option(QLRISKS_BUILD_BENCHMARK_STANDALONE "Build standalone benchmark executable (no Boost.Test)" OFF)

set(QLRISKS_TEST_SOURCES
americanoption_xad.cpp
barrieroption_xad.cpp
Expand All @@ -9,14 +14,47 @@ set(QLRISKS_TEST_SOURCES
forwardrateagreement_xad.cpp
hestonmodel_xad.cpp
swap_xad.cpp

utilities_xad.cpp
quantlibtestsuite_xad.cpp
)

# XAD JIT tests - uses C++ interpreter backend, does NOT require Forge
# These test XAD's JIT compilation infrastructure with the built-in interpreter
option(QLRISKS_ENABLE_JIT_TESTS "Enable XAD JIT tests (uses interpreter backend, no Forge required)" OFF)
if(QLRISKS_ENABLE_JIT_TESTS)
message(STATUS "QLRisks test-suite: Adding XAD JIT tests (interpreter backend)")
list(APPEND QLRISKS_TEST_SOURCES jit_xad.cpp)
endif()

# Forge JIT tests - require Forge's native code backend
# These tests require xad-forge for native machine code generation
set(QLRISKS_FORGE_TESTS_ENABLED FALSE)
if(QLRISKS_ENABLE_FORGE_TESTS AND TARGET QLRisks::forge)
message(STATUS "QLRisks test-suite: Adding Forge JIT tests (native code backend)")
list(APPEND QLRISKS_TEST_SOURCES forgebackend_xad.cpp swaption_jit_pipeline_xad.cpp)
set(QLRISKS_FORGE_TESTS_ENABLED TRUE)
elseif(QLRISKS_ENABLE_FORGE_TESTS)
message(WARNING "QLRisks test-suite: QLRISKS_ENABLE_FORGE_TESTS=ON but QLRisks::forge not available")
endif()

# Benchmark sources - Boost.Test based (requires Forge for JIT benchmarks)
set(QLRISKS_BENCHMARK_SOURCES
quantlibrisks_benchmark.cpp
swaption_benchmark.cpp
utilities_xad.cpp
)

# Baseline benchmark sources - Boost.Test based (no Forge required)
set(QLRISKS_BENCHMARK_BASELINE_SOURCES
quantlibrisks_benchmark.cpp
swaption_benchmark_baseline.cpp
utilities_xad.cpp
)

set(QLRISKS_TEST_HEADERS utilities_xad.hpp)

if(QL_BUILD_TEST_SUITE)
if(QL_BUILD_TEST_SUITE OR QLRISKS_BUILD_TEST_SUITE)
add_executable(QuantLib-Risks_test_suite ${QLRISKS_TEST_SOURCES} ${QLRISKS_TEST_HEADERS})
set_target_properties(QuantLib-Risks_test_suite PROPERTIES OUTPUT_NAME "quantlib-risks-test-suite")
if (NOT Boost_USE_STATIC_LIBS)
Expand All @@ -25,8 +63,88 @@ if(QL_BUILD_TEST_SUITE)
target_link_libraries(QuantLib-Risks_test_suite PRIVATE
ql_library
${QL_THREAD_LIBRARIES})

# ONLY link to Forge if Forge tests are actually enabled
# This is important because linking Forge brings in AVX2-compiled code
# which can cause ODR violations if not properly isolated
if(QLRISKS_FORGE_TESTS_ENABLED)
message(STATUS "QLRisks test-suite: Linking QLRisks::forge (Forge tests enabled)")
target_link_libraries(QuantLib-Risks_test_suite PRIVATE QLRisks::forge)
target_compile_definitions(QuantLib-Risks_test_suite PRIVATE QLRISKS_HAS_FORGE=1)
else()
message(STATUS "QLRisks test-suite: NOT linking Forge (Forge tests disabled)")
endif()

if (QL_INSTALL_TEST_SUITE)
install(TARGETS QuantLib-Risks_test_suite RUNTIME DESTINATION ${QL_INSTALL_BINDIR})
endif()
add_test(NAME QuantLib-Risks_test_suite COMMAND QuantLib-Risks_test_suite --log_level=message)
endif()

# Benchmark suite with Forge JIT - Boost.Test based
# Requires Forge for JIT scalar and JIT-AVX benchmarks
if(QLRISKS_BUILD_BENCHMARK AND TARGET QLRisks::forge)
message(STATUS "QLRisks: Building benchmark suite (with Forge JIT)")
add_executable(QuantLib-Risks_benchmark ${QLRISKS_BENCHMARK_SOURCES} ${QLRISKS_TEST_HEADERS})
set_target_properties(QuantLib-Risks_benchmark PROPERTIES OUTPUT_NAME "quantlib-risks-benchmark")
if (NOT Boost_USE_STATIC_LIBS)
target_compile_definitions(QuantLib-Risks_benchmark PRIVATE BOOST_ALL_DYN_LINK)
endif()
target_link_libraries(QuantLib-Risks_benchmark PRIVATE
ql_library
QLRisks::forge
${QL_THREAD_LIBRARIES})
target_compile_definitions(QuantLib-Risks_benchmark PRIVATE QLRISKS_HAS_FORGE=1)

if (QL_INSTALL_TEST_SUITE)
install(TARGETS QuantLib-Risks_benchmark RUNTIME DESTINATION ${QL_INSTALL_BINDIR})
endif()
add_test(NAME QuantLib-Risks_benchmark COMMAND QuantLib-Risks_benchmark --log_level=message)
elseif(QLRISKS_BUILD_BENCHMARK)
message(WARNING "QLRisks: QLRISKS_BUILD_BENCHMARK=ON but QLRisks::forge not available. Use QLRISKS_BUILD_BENCHMARK_BASELINE for non-Forge benchmarks.")
endif()

# Baseline benchmark suite - Boost.Test based, NO Forge required
# Compares XAD tape-based AD only (useful for baseline measurements)
option(QLRISKS_BUILD_BENCHMARK_BASELINE "Build baseline benchmark suite (Boost.Test, no Forge)" OFF)
if(QLRISKS_BUILD_BENCHMARK_BASELINE)
message(STATUS "QLRisks: Building baseline benchmark suite (XAD tape only)")
add_executable(QuantLib-Risks_benchmark_baseline ${QLRISKS_BENCHMARK_BASELINE_SOURCES} ${QLRISKS_TEST_HEADERS})
set_target_properties(QuantLib-Risks_benchmark_baseline PROPERTIES OUTPUT_NAME "quantlib-risks-benchmark-baseline")
if (NOT Boost_USE_STATIC_LIBS)
target_compile_definitions(QuantLib-Risks_benchmark_baseline PRIVATE BOOST_ALL_DYN_LINK)
endif()
target_link_libraries(QuantLib-Risks_benchmark_baseline PRIVATE
ql_library
${QL_THREAD_LIBRARIES})

if (QL_INSTALL_TEST_SUITE)
install(TARGETS QuantLib-Risks_benchmark_baseline RUNTIME DESTINATION ${QL_INSTALL_BINDIR})
endif()
add_test(NAME QuantLib-Risks_benchmark_baseline COMMAND QuantLib-Risks_benchmark_baseline --log_level=message)
endif()

# Standalone benchmark executable - no Boost.Test dependency
# Can be built with or without Forge
if(QLRISKS_BUILD_BENCHMARK_STANDALONE)
message(STATUS "QLRisks: Building standalone benchmark executable")
add_executable(QuantLib-Risks_benchmark_standalone benchmark_main.cpp)
set_target_properties(QuantLib-Risks_benchmark_standalone PROPERTIES
OUTPUT_NAME "quantlib-risks-benchmark-standalone")
target_link_libraries(QuantLib-Risks_benchmark_standalone PRIVATE
ql_library
${QL_THREAD_LIBRARIES})

# Optionally link Forge if available
if(TARGET QLRisks::forge)
message(STATUS "QLRisks standalone benchmark: Linking QLRisks::forge")
target_link_libraries(QuantLib-Risks_benchmark_standalone PRIVATE QLRisks::forge)
target_compile_definitions(QuantLib-Risks_benchmark_standalone PRIVATE QLRISKS_HAS_FORGE=1)
else()
message(STATUS "QLRisks standalone benchmark: Building without Forge (XAD tape only)")
endif()

if (QL_INSTALL_TEST_SUITE)
install(TARGETS QuantLib-Risks_benchmark_standalone RUNTIME DESTINATION ${QL_INSTALL_BINDIR})
endif()
endif()
Loading