yabi-torch 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- yabi_torch-0.1.0/.clang-format +10 -0
- yabi_torch-0.1.0/.github/workflows/ci.yml +53 -0
- yabi_torch-0.1.0/.gitignore +24 -0
- yabi_torch-0.1.0/BASELINE.md +121 -0
- yabi_torch-0.1.0/BUILD.bazel +8 -0
- yabi_torch-0.1.0/CMakeLists.txt +425 -0
- yabi_torch-0.1.0/LICENSE +126 -0
- yabi_torch-0.1.0/NOTICE +7 -0
- yabi_torch-0.1.0/PKG-INFO +1054 -0
- yabi_torch-0.1.0/README.md +1034 -0
- yabi_torch-0.1.0/benchmarks/TELEMETRY.md +15 -0
- yabi_torch-0.1.0/benchmarks/report.py +98 -0
- yabi_torch-0.1.0/benchmarks/telemetry.cpp +192 -0
- yabi_torch-0.1.0/benchmarks/telemetry.csv +9 -0
- yabi_torch-0.1.0/benchmarks/work_precision.png +0 -0
- yabi_torch-0.1.0/bindings/python/test_autograd.py +66 -0
- yabi_torch-0.1.0/bindings/python/test_batch_parallel.py +88 -0
- yabi_torch-0.1.0/bindings/python/test_cuda.py +89 -0
- yabi_torch-0.1.0/bindings/python/test_nn_module.py +125 -0
- yabi_torch-0.1.0/bindings/python/test_solve_ivp.py +121 -0
- yabi_torch-0.1.0/bindings/python/test_yabi.py +42 -0
- yabi_torch-0.1.0/bindings/python/torch_wrapper.py +79 -0
- yabi_torch-0.1.0/bindings/python/yabi.cpp +222 -0
- yabi_torch-0.1.0/examples/basic.cpp +48 -0
- yabi_torch-0.1.0/examples/dump_trajectories.cpp +160 -0
- yabi_torch-0.1.0/examples/heat1d_dense_analytic_jac.cpp +42 -0
- yabi_torch-0.1.0/examples/heat1d_dense_literature.cpp +40 -0
- yabi_torch-0.1.0/examples/heat1d_umfpack_literature.cpp +42 -0
- yabi_torch-0.1.0/examples/heat1d_umfpack_sens2.cpp +40 -0
- yabi_torch-0.1.0/examples/heat1d_umfpack_sens3.cpp +38 -0
- yabi_torch-0.1.0/examples/heat1d_umfpack_sens_dense_equiv.cpp +40 -0
- yabi_torch-0.1.0/examples/heat1d_umfpack_sens_simple.cpp +38 -0
- yabi_torch-0.1.0/examples/heat_1d_sparse_sens_umfpack.cpp +46 -0
- yabi_torch-0.1.0/examples/heat_1d_sparse_umfpack.cpp +43 -0
- yabi_torch-0.1.0/examples/heat_1d_umfpack_forward_sens.cpp +44 -0
- yabi_torch-0.1.0/examples/nonautonomous.cpp +52 -0
- yabi_torch-0.1.0/examples/plot_brusselator_traj.cpp +25 -0
- yabi_torch-0.1.0/examples/plot_heat_traj.cpp +43 -0
- yabi_torch-0.1.0/examples/plot_oscillator_traj.cpp +34 -0
- yabi_torch-0.1.0/examples/plot_robertson_traj.cpp +47 -0
- yabi_torch-0.1.0/examples/sensitivity_demo.cpp +300 -0
- yabi_torch-0.1.0/include/yabi/abstract_rhs.hpp +203 -0
- yabi_torch-0.1.0/include/yabi/amgcl_linear_solver.hpp +72 -0
- yabi_torch-0.1.0/include/yabi/bdf_integrator.hpp +433 -0
- yabi_torch-0.1.0/include/yabi/cppad_rhs.hpp +386 -0
- yabi_torch-0.1.0/include/yabi/csc_matrix.hpp +159 -0
- yabi_torch-0.1.0/include/yabi/cuda_krylov_solver.hpp +440 -0
- yabi_torch-0.1.0/include/yabi/cuda_linear_solver.hpp +176 -0
- yabi_torch-0.1.0/include/yabi/cudss_linear_solver.hpp +135 -0
- yabi_torch-0.1.0/include/yabi/detail/adjoint_engine.hpp +850 -0
- yabi_torch-0.1.0/include/yabi/detail/continuous_adjoint.hpp +436 -0
- yabi_torch-0.1.0/include/yabi/detail/core.ipp +1120 -0
- yabi_torch-0.1.0/include/yabi/detail/dense_linear_solver.ipp +76 -0
- yabi_torch-0.1.0/include/yabi/detail/integrator_config.hpp +79 -0
- yabi_torch-0.1.0/include/yabi/detail/klu_linear_solver.ipp +145 -0
- yabi_torch-0.1.0/include/yabi/detail/newton.ipp +50 -0
- yabi_torch-0.1.0/include/yabi/detail/newton_solver.hpp +1933 -0
- yabi_torch-0.1.0/include/yabi/detail/nordsieck_state.hpp +259 -0
- yabi_torch-0.1.0/include/yabi/detail/replay_tape.hpp +78 -0
- yabi_torch-0.1.0/include/yabi/detail/sensitivity_engine.hpp +1107 -0
- yabi_torch-0.1.0/include/yabi/detail/sensitivity_forward.ipp +40 -0
- yabi_torch-0.1.0/include/yabi/detail/sensitivity_reverse.ipp +37 -0
- yabi_torch-0.1.0/include/yabi/detail/sensitivity_setup.ipp +111 -0
- yabi_torch-0.1.0/include/yabi/detail/step_control.ipp +119 -0
- yabi_torch-0.1.0/include/yabi/detail/step_controller.hpp +598 -0
- yabi_torch-0.1.0/include/yabi/detail/umfpack_linear_solver.ipp +167 -0
- yabi_torch-0.1.0/include/yabi/external_sparse_solver.hpp +115 -0
- yabi_torch-0.1.0/include/yabi/ginkgo_linear_solver.hpp +94 -0
- yabi_torch-0.1.0/include/yabi/jacobian_backend.hpp +42 -0
- yabi_torch-0.1.0/include/yabi/linear_algebra.hpp +177 -0
- yabi_torch-0.1.0/include/yabi/linear_solver.hpp +158 -0
- yabi_torch-0.1.0/include/yabi/log.hpp +92 -0
- yabi_torch-0.1.0/include/yabi/matrix_free_operator.hpp +45 -0
- yabi_torch-0.1.0/include/yabi/matrix_free_solver.hpp +631 -0
- yabi_torch-0.1.0/include/yabi/ode_formulation.hpp +13 -0
- yabi_torch-0.1.0/include/yabi/preconditioner.hpp +101 -0
- yabi_torch-0.1.0/include/yabi/sensitivity_linearization.hpp +61 -0
- yabi_torch-0.1.0/include/yabi/sensitivity_manual.hpp +14 -0
- yabi_torch-0.1.0/include/yabi/sparsity_pattern.hpp +48 -0
- yabi_torch-0.1.0/include/yabi/user_ode.hpp +23 -0
- yabi_torch-0.1.0/include/yabi/user_ode_rhs.hpp +358 -0
- yabi_torch-0.1.0/pyplot/plot_all.py +217 -0
- yabi_torch-0.1.0/pyproject.toml +45 -0
- yabi_torch-0.1.0/python/yabi_torch/__init__.py +17 -0
- yabi_torch-0.1.0/python/yabi_torch/_solver.py +194 -0
- yabi_torch-0.1.0/reproduce_benchmarks.py +88 -0
- yabi_torch-0.1.0/run_examples.sh +52 -0
- yabi_torch-0.1.0/run_tests.sh +47 -0
- yabi_torch-0.1.0/src/bdf_integrator.cpp +24 -0
- yabi_torch-0.1.0/src/bdf_integrator_pure.cpp +17 -0
- yabi_torch-0.1.0/tests/CMakeLists.txt +204 -0
- yabi_torch-0.1.0/tests/README.md +111 -0
- yabi_torch-0.1.0/tests/component/amgcl_solver.cpp +93 -0
- yabi_torch-0.1.0/tests/component/cuda_krylov_solver.cpp +155 -0
- yabi_torch-0.1.0/tests/component/cuda_parallel.cpp +124 -0
- yabi_torch-0.1.0/tests/component/cuda_solver.cpp +95 -0
- yabi_torch-0.1.0/tests/component/cudss_solver.cpp +94 -0
- yabi_torch-0.1.0/tests/component/ginkgo_solver.cpp +93 -0
- yabi_torch-0.1.0/tests/component/matrix_free_jfree.cpp +251 -0
- yabi_torch-0.1.0/tests/component/matrix_free_newton.cpp +227 -0
- yabi_torch-0.1.0/tests/component/matrix_free_pure.cpp +220 -0
- yabi_torch-0.1.0/tests/component/native_sparse.cpp +200 -0
- yabi_torch-0.1.0/tests/component/parallel_sensitivity.cpp +126 -0
- yabi_torch-0.1.0/tests/component/rhs_backend_agreement.cpp +106 -0
- yabi_torch-0.1.0/tests/component/sensitivity_agreement.cpp +179 -0
- yabi_torch-0.1.0/tests/component/solver_matrix.cpp +165 -0
- yabi_torch-0.1.0/tests/component/suitesparse_sparse.cpp +207 -0
- yabi_torch-0.1.0/tests/system/01_linear_autonomous.cpp +43 -0
- yabi_torch-0.1.0/tests/system/02_linear_system.cpp +46 -0
- yabi_torch-0.1.0/tests/system/03_oscillator.cpp +42 -0
- yabi_torch-0.1.0/tests/system/04_nonautonomous.cpp +42 -0
- yabi_torch-0.1.0/tests/system/05_stiff_robertson_autonomous.cpp +76 -0
- yabi_torch-0.1.0/tests/system/06_van_der_pol_autonomous.cpp +64 -0
- yabi_torch-0.1.0/tests/system/07_sensitivity_forward_reverse_simultaneous.cpp +105 -0
- yabi_torch-0.1.0/tests/system/08_robertson_mdt_style.cpp +89 -0
- yabi_torch-0.1.0/tests/system/09_van_der_pol_mdt_style.cpp +78 -0
- yabi_torch-0.1.0/tests/system/10_zero_state_guard.cpp +111 -0
- yabi_torch-0.1.0/tests/system/11_tolerance_and_order.cpp +171 -0
- yabi_torch-0.1.0/tests/system/12_brusselator_autonomous.cpp +44 -0
- yabi_torch-0.1.0/tests/system/13_hires_autonomous.cpp +51 -0
- yabi_torch-0.1.0/tests/system/14_prothero_robinson_autonomous.cpp +39 -0
- yabi_torch-0.1.0/tests/system/15_oregonator_autonomous.cpp +59 -0
- yabi_torch-0.1.0/tests/system/16_damped_pendulum_stiff.cpp +34 -0
- yabi_torch-0.1.0/tests/system/17_polanyi_wigner_large_sparse.cpp +36 -0
- yabi_torch-0.1.0/tests/system/18_network_large_parametrized.cpp +36 -0
- yabi_torch-0.1.0/tests/system/20_brusselator_sensitivity.cpp +71 -0
- yabi_torch-0.1.0/tests/system/21_hi_res_sensitivity.cpp +80 -0
- yabi_torch-0.1.0/tests/system/22_prothero_robinson_sensitivity.cpp +71 -0
- yabi_torch-0.1.0/tests/system/23_oregonator_sensitivity.cpp +72 -0
- yabi_torch-0.1.0/tests/system/24_damped_pendulum_sensitivity.cpp +72 -0
- yabi_torch-0.1.0/tests/system/25_polanyi_wigner_sensitivity.cpp +74 -0
- yabi_torch-0.1.0/tests/system/26_network_sensitivity.cpp +86 -0
- yabi_torch-0.1.0/tests/system/27_prothero_robinson_central_diff.cpp +59 -0
- yabi_torch-0.1.0/tests/system/28_second_order_sensitivity.cpp +86 -0
- yabi_torch-0.1.0/tests/system/29_diagnostics.cpp +191 -0
- yabi_torch-0.1.0/tests/system/30_e5_stiff_autonomous.cpp +55 -0
- yabi_torch-0.1.0/tests/system/31_dense_output.cpp +141 -0
- yabi_torch-0.1.0/tests/system/32_fixed_order.cpp +120 -0
- yabi_torch-0.1.0/tests/system/33_higher_order_sensitivity.cpp +193 -0
- yabi_torch-0.1.0/tests/system/34_continuous_adjoint.cpp +196 -0
- yabi_torch-0.1.0/tests/system/35_second_order_continuous_adjoint.cpp +116 -0
- yabi_torch-0.1.0/tests/system/36_second_order_forward.cpp +87 -0
- yabi_torch-0.1.0/tests/system/37_jacobian_backends.cpp +141 -0
- yabi_torch-0.1.0/tests/system/38_higher_order_forward.cpp +123 -0
- yabi_torch-0.1.0/tests/system/39_generated_higher_order.cpp +77 -0
- yabi_torch-0.1.0/tests/system/40_heat_equation.cpp +89 -0
- yabi_torch-0.1.0/tests/system/41_two_body_orbit.cpp +74 -0
- yabi_torch-0.1.0/tests/system/42_medakzo.cpp +110 -0
- yabi_torch-0.1.0/tests/system/43_pollu.cpp +138 -0
- yabi_torch-0.1.0/tests/system/44_beam.cpp +171 -0
- yabi_torch-0.1.0/tests/system/45_emep.cpp +551 -0
- yabi_torch-0.1.0/tests/system/46_stiffness_sweep.cpp +76 -0
- yabi_torch-0.1.0/tests/system/47_line_search.cpp +80 -0
- yabi_torch-0.1.0/tests/system/48_parallel_sensitivity.cpp +123 -0
- yabi_torch-0.1.0/tests/system/README.md +42 -0
- yabi_torch-0.1.0/tests/system/cuda_krylov_heat.cpp +95 -0
- yabi_torch-0.1.0/tests/system/generated_cubic.hpp +60 -0
- yabi_torch-0.1.0/tests/system/manual_sensitivity_demo.cpp +15 -0
- yabi_torch-0.1.0/tests/system/manual_sensitivity_pure.cpp +30 -0
- yabi_torch-0.1.0/tests/system/pure_12_brusselator_autonomous.cpp +55 -0
- yabi_torch-0.1.0/tests/system/pure_13_hires_autonomous.cpp +122 -0
- yabi_torch-0.1.0/tests/system/pure_14_prothero_robinson_autonomous.cpp +4 -0
- yabi_torch-0.1.0/tests/system/pure_15_oregonator_autonomous.cpp +4 -0
- yabi_torch-0.1.0/tests/system/pure_16_damped_pendulum_stiff.cpp +4 -0
- yabi_torch-0.1.0/tests/system/pure_17_polanyi_wigner_large_sparse.cpp +4 -0
- yabi_torch-0.1.0/tests/system/pure_18_network_large_parametrized.cpp +4 -0
- yabi_torch-0.1.0/tests/system/pure_19_continuous_adjoint.cpp +57 -0
- yabi_torch-0.1.0/tests/system/pure_20_nonautonomous_adjoint.cpp +69 -0
- yabi_torch-0.1.0/tests/system/pure_21_second_order_adjoint.cpp +63 -0
- yabi_torch-0.1.0/tests/system/pure_22_second_order_forward.cpp +30 -0
- yabi_torch-0.1.0/tests/system/pure_23_jacobian_backends.cpp +144 -0
- yabi_torch-0.1.0/tests/system/pure_24_higher_order_forward.cpp +63 -0
- yabi_torch-0.1.0/tests/unit/engine_fixture.hpp +107 -0
- yabi_torch-0.1.0/tests/unit/test_abstract_rhs.cpp +110 -0
- yabi_torch-0.1.0/tests/unit/test_adjoint_engine.cpp +43 -0
- yabi_torch-0.1.0/tests/unit/test_config_statistics.cpp +60 -0
- yabi_torch-0.1.0/tests/unit/test_continuous_adjoint_rhs.cpp +99 -0
- yabi_torch-0.1.0/tests/unit/test_cppad_rhs_hooks.cpp +121 -0
- yabi_torch-0.1.0/tests/unit/test_cppad_rhs_sparsity.cpp +293 -0
- yabi_torch-0.1.0/tests/unit/test_csc_matrix.cpp +114 -0
- yabi_torch-0.1.0/tests/unit/test_external_sparse_solver.cpp +90 -0
- yabi_torch-0.1.0/tests/unit/test_krylov_solver.cpp +276 -0
- yabi_torch-0.1.0/tests/unit/test_linear_algebra.cpp +134 -0
- yabi_torch-0.1.0/tests/unit/test_linear_solver.cpp +121 -0
- yabi_torch-0.1.0/tests/unit/test_matrix_free_operator.cpp +54 -0
- yabi_torch-0.1.0/tests/unit/test_newton_solver.cpp +43 -0
- yabi_torch-0.1.0/tests/unit/test_nordsieck_state.cpp +100 -0
- yabi_torch-0.1.0/tests/unit/test_preconditioner.cpp +373 -0
- yabi_torch-0.1.0/tests/unit/test_property_sparse.cpp +103 -0
- yabi_torch-0.1.0/tests/unit/test_replay_tape.cpp +50 -0
- yabi_torch-0.1.0/tests/unit/test_sensitivity_engine.cpp +61 -0
- yabi_torch-0.1.0/tests/unit/test_sensitivity_linearization.cpp +268 -0
- yabi_torch-0.1.0/tests/unit/test_sparsity_pattern.cpp +50 -0
- yabi_torch-0.1.0/tests/unit/test_step_controller.cpp +39 -0
- yabi_torch-0.1.0/tests/unit/test_support.hpp +46 -0
- yabi_torch-0.1.0/tests/unit/test_trajectory.cpp +102 -0
- yabi_torch-0.1.0/tests/unit/test_user_ode_rhs.cpp +214 -0
- yabi_torch-0.1.0/tests/unit/test_user_ode_rhs_hooks.cpp +138 -0
- yabi_torch-0.1.0/tools/generate_derivatives.py +191 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
BasedOnStyle: LLVM
|
|
2
|
+
IndentWidth: 4
|
|
3
|
+
ColumnLimit: 100
|
|
4
|
+
BreakBeforeBraces: Attach
|
|
5
|
+
AllowShortFunctionsOnASingleLine: None
|
|
6
|
+
IndentCaseLabels: false
|
|
7
|
+
SpaceAfterCStyleCast: false
|
|
8
|
+
IndentPPDirectives: BeforeHash
|
|
9
|
+
KeepEmptyLinesAtTheStartOfBlocks: false
|
|
10
|
+
MaxEmptyLinesToKeep: 1
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build-test:
|
|
9
|
+
name: build-test (CppAD=${{ matrix.cppad }})
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
# Build and test both configurations: the full CppAD build and the
|
|
15
|
+
# pure (YABI_USE_CPPAD=OFF) build. The pure configuration also runs
|
|
16
|
+
# the CppAD-free unit and system tests.
|
|
17
|
+
cppad: ["ON", "OFF"]
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: |
|
|
23
|
+
sudo apt-get update
|
|
24
|
+
sudo apt-get install -y build-essential cmake pkg-config \
|
|
25
|
+
liblapack-dev libblas-dev libcppad-dev
|
|
26
|
+
# SuiteSparse enables the UMFPACK/KLU symbolic-reuse component test.
|
|
27
|
+
if [ "${{ matrix.cppad }}" = "ON" ]; then
|
|
28
|
+
sudo apt-get install -y libsuitesparse-dev
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
- name: Configure
|
|
32
|
+
run: cmake -S . -B build -DYABI_USE_CPPAD=${{ matrix.cppad }}
|
|
33
|
+
|
|
34
|
+
- name: Build
|
|
35
|
+
run: cmake --build build -j"$(nproc)"
|
|
36
|
+
|
|
37
|
+
- name: Test unit tier
|
|
38
|
+
run: ctest --test-dir build -L unit --output-on-failure --no-tests=error
|
|
39
|
+
|
|
40
|
+
- name: Test component tier
|
|
41
|
+
# The component tier also runs in the pure build: matrix_free_pure /
|
|
42
|
+
# matrix_free_jfree exercise the CppAD-free matrix-free Newton path.
|
|
43
|
+
run: ctest --test-dir build -L component --output-on-failure --no-tests=error
|
|
44
|
+
|
|
45
|
+
- name: Test system tier
|
|
46
|
+
run: ctest --test-dir build -L system --output-on-failure --no-tests=error
|
|
47
|
+
|
|
48
|
+
- name: Test benchmark (evidence) tier
|
|
49
|
+
if: matrix.cppad == 'ON'
|
|
50
|
+
run: ctest --test-dir build -L benchmark --output-on-failure --no-tests=error
|
|
51
|
+
|
|
52
|
+
- name: Test examples
|
|
53
|
+
run: ctest --test-dir build -L example --output-on-failure --no-tests=error
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
build/
|
|
2
|
+
build-pure/
|
|
3
|
+
build-py/
|
|
4
|
+
build-py-cuda/
|
|
5
|
+
dist/
|
|
6
|
+
bin/
|
|
7
|
+
build-cmake/
|
|
8
|
+
cmake-build-*/
|
|
9
|
+
*.o
|
|
10
|
+
*.a
|
|
11
|
+
*.so
|
|
12
|
+
*.so.*
|
|
13
|
+
*.dylib
|
|
14
|
+
*.exe
|
|
15
|
+
compile_commands.json
|
|
16
|
+
CMakeCache.txt
|
|
17
|
+
CMakeFiles/
|
|
18
|
+
cmake_install.cmake
|
|
19
|
+
plots/
|
|
20
|
+
trajectories/
|
|
21
|
+
work_precision_diagram.png
|
|
22
|
+
|
|
23
|
+
__pycache__/
|
|
24
|
+
*.pyc
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# YABI-BDF numerical baseline (T30 decomposition — behaviour-preservation anchor)
|
|
2
|
+
|
|
3
|
+
Captured 2026-11-04, before the T30 `BDFIntegrator` decomposition refactor. **These values must not differ after the
|
|
4
|
+
refactor.** The refactor is purely structural (separation of responsibilities); any numeric drift is a regression.
|
|
5
|
+
|
|
6
|
+
## Provenance — comparison against the initial commit (`cb357e3`)
|
|
7
|
+
|
|
8
|
+
The initial commit was built in isolation and its outputs compared against this baseline. **They differ**, and the
|
|
9
|
+
difference is fully explained (no unexplained regression):
|
|
10
|
+
|
|
11
|
+
| anchor | initial `cb357e3` | baseline (`main`) |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| `yabi_basic` error | `4.32592239807406e-10` | `6.53219478508049e-10` |
|
|
14
|
+
| `13_hires` y1 | `0.000737133157726` | `0.000737133286353` |
|
|
15
|
+
| `05_robertson` | `y1=0.107300429907 …` | identical |
|
|
16
|
+
| `12_brusselator` | `y1(5) = 0.938299 …` | identical |
|
|
17
|
+
| `15_oregonator` | `t=1 => (112559.4758, …)` | identical |
|
|
18
|
+
|
|
19
|
+
The change is introduced by the **first commit after the initial one**, `8f5492c`
|
|
20
|
+
`fix(step-control): reduce BDF order on rejected steps` — an intentional correctness fix. Every
|
|
21
|
+
commit in the history was built and run: there are exactly two value regimes, the switch occurs at `8f5492c`, and no
|
|
22
|
+
later commit (through `main`) changes either anchor.
|
|
23
|
+
|
|
24
|
+
**Do not read the smaller initial error as “better”.** The initial `yabi_basic` error (`4.33e-10`) is *smaller* than the
|
|
25
|
+
baseline (`6.53e-10`), but both are ~`1e-10` absolute against `y(2) ≈ 0.368` (≈0.6 ppm) — at the requested tolerance
|
|
26
|
+
floor. A different sequence of accepted steps/orders simply lands closer by coincidence; global error is not minimised
|
|
27
|
+
by the controller. The current algorithm is the correct one (order reduction on rejection), so the
|
|
28
|
+
baseline is `main` from `8f5492c` onward, **not** `cb357e3`. Testbenches `31`/`32` did not exist at the initial commit,
|
|
29
|
+
and `28` was a different (framework-only) program, so they have no initial-commit counterpart.
|
|
30
|
+
|
|
31
|
+
## Test suites
|
|
32
|
+
|
|
33
|
+
Tests are organised in three tiers below `tests/` and selected with ctest labels:
|
|
34
|
+
|
|
35
|
+
| tier | label | contents |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| unit | `unit` | isolated component contracts (config, replay tape, Nordsieck state, step controller, Newton, sensitivity, adjoint, dense/CSC solver, sparsity, CscMatrix, CppAD RHS sparsity/batch `J·V`, `UserODERHS` sparsity/`J·v`/batch `J·V`, Krylov solver, pluggable preconditioner/forcing, sensitivity linearization) |
|
|
38
|
+
| component | `component` | cross-component integration (`native_sparse`, `matrix_free_newton`, `matrix_free_pure`, `matrix_free_jfree`, plus `suitesparse_sparse` when SuiteSparse is available) |
|
|
39
|
+
| system | `system` | mathematical benchmarks and regressions (`tests/system/`) |
|
|
40
|
+
|
|
41
|
+
- `ctest --test-dir build` (CppAD): **64/64 pass** — 15 unit, 5 component (including
|
|
42
|
+
`suitesparse_sparse` when SuiteSparse is available), 41 system, 3 example.
|
|
43
|
+
- `ctest --test-dir build-pure` (no CppAD): **26/26 pass** — 14 unit, 2 component,
|
|
44
|
+
7 pure system, 3 example.
|
|
45
|
+
|
|
46
|
+
T32c (pluggable/J-free preconditioners and forcing) and T32d (batch/sparse/matrix-free sensitivity interfaces)
|
|
47
|
+
are additive: every pre-existing numeric anchor below is unchanged, `yabi_basic` remains
|
|
48
|
+
`6.53219478508049e-10`, and the sensitivity replay values are identical.
|
|
49
|
+
|
|
50
|
+
T29 (RAII memory management) and the arbitrary-order (3+) forward/reverse sensitivity work are likewise
|
|
51
|
+
behaviour-preserving for the *nominal* trajectory: every anchor below except `28_second_order_sensitivity`
|
|
52
|
+
is bit-identical after both. `28` itself was extended from second order to orders 1–4 and its `forward(2)`
|
|
53
|
+
now runs the full-Taylor replay, so its printed `forward(2)` relative error moved from `5.84e-09` to
|
|
54
|
+
`9.95e-09` (the value `8` is unchanged, still ≈1e-9).
|
|
55
|
+
|
|
56
|
+
## Key numeric anchors (representative outputs, verbatim)
|
|
57
|
+
|
|
58
|
+
`yabi_basic` (decay `y'=-y`, t=2):
|
|
59
|
+
```
|
|
60
|
+
y(2) = 0.367879440518223
|
|
61
|
+
exact = 0.367879441171442
|
|
62
|
+
error = 6.53219478508049e-10
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`05_stiff_robertson_autonomous` (t=1e4):
|
|
66
|
+
```
|
|
67
|
+
y1=0.107300429907 y2=4.800167041e-07 y3=0.892699090076 sum=1
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`12_brusselator_autonomous` (t=5):
|
|
71
|
+
```
|
|
72
|
+
y1(5) = 0.938299, y2(5) = 4.52728
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`13_hires_autonomous` (t=321.812):
|
|
76
|
+
```
|
|
77
|
+
y = (0.000737133286353,0.000144248972571,5.88876753153e-05,0.00117565512229,0.00238641718911,0.00623915986836,0.00285004111321,0.00284995888679)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`15_oregonator_autonomous`:
|
|
81
|
+
```
|
|
82
|
+
t=1 => y=(112559.4758,0.05952720969,6712.895565) OK
|
|
83
|
+
t=5 => y=(1.001753339,571.3421806,23962.69912) OK
|
|
84
|
+
t=10 => y=(1.000670524,1492.35853,10713.98688) OK
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`28_second_order_sensitivity` (orders 1–4):
|
|
88
|
+
```
|
|
89
|
+
forward(1) = 4 (exact 4) rel=1.55578e-09
|
|
90
|
+
forward(2) = 8 (exact 8) rel=9.95208e-09
|
|
91
|
+
forward(3) = 24 (exact 24) rel=4.2256e-08
|
|
92
|
+
forward(4) = 96 (exact 96) rel=1.32408e-07
|
|
93
|
+
reverse(4) = [order1 4 order2 8 order3 24 order4 96]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`31_dense_output` (interior points of the last step):
|
|
97
|
+
```
|
|
98
|
+
theta=-0.50 t=2.997174190003 y=0.049927955809049 exact=0.049927956130660 err=3.216e-10 OK
|
|
99
|
+
theta=-0.25 t=2.998587095001 y=0.049857462162821 exact=0.049857462484014 err=3.212e-10 OK
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`32_fixed_order` (per-order convergence slopes):
|
|
103
|
+
```
|
|
104
|
+
order 5: slope 5.229 order 6: slope 5.983
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Additive test/evidence + opt-in damped Newton (T41–T45) — anchors unchanged
|
|
108
|
+
|
|
109
|
+
The test-coverage and evidence effort (T41–T45: unit/component/system tiers, the CWI stiff-chemistry benchmarks
|
|
110
|
+
POLLU/BEAM/EMEP/MEDAKZO, the telemetry harness, the EMEP robustness investigation, and the damped-Newton feature) is
|
|
111
|
+
**behaviour-preserving for the defaults**. It added `tests/`, `benchmarks/`, and CI wiring, plus one opt-in feature:
|
|
112
|
+
a damped Newton (backtracking line search) behind `setNewtonLineSearch(bool)` — a new config flag that is **off by
|
|
113
|
+
default** and gated to the plain, non-taped state solve, so the historical full-step behaviour is reproduced
|
|
114
|
+
bit-for-bit when it is not enabled. Consequently every anchor above remains **byte-identical**, including `yabi_basic`
|
|
115
|
+
error `6.53219478508049e-10`. The EMEP benchmark (stiffness ratio ~1e14) originally needed a conservative
|
|
116
|
+
*configuration* (`setFixedOrder(4)` + fresh Jacobian per iteration + `setNewtonLineSearch(true)`); that was a
|
|
117
|
+
work-around for a one-character boundary bug in the minimal-step acceptance (`h < 4·eps·|t|` vs the floor's exact
|
|
118
|
+
`h = 4·eps·|t|`). Fixing the test to `h <= 4·eps·|t|` lets EMEP match its reference with defaults (max order 5, no
|
|
119
|
+
line search, no fixed order) — see README "Extreme-stiffness behaviour (EMEP)" — and is likewise a no-op on
|
|
120
|
+
well-behaved problems, so it leaves every anchor above untouched.
|
|
121
|
+
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.16)
|
|
2
|
+
project(YABI VERSION 0.2.0 LANGUAGES CXX)
|
|
3
|
+
|
|
4
|
+
option(YABI_BUILD_EXAMPLES "Build YABI example programs" ON)
|
|
5
|
+
option(YABI_BUILD_TESTS "Build the unit/component/system test suite" ON)
|
|
6
|
+
option(YABI_INSTALL_CORE "Install the C++ library + headers + CMake config (OFF for Python-only wheels)" ON)
|
|
7
|
+
# Backwards-compatible alias for the old flat testbench (now tests/system).
|
|
8
|
+
# Only consulted when supplied, so fresh configurations see a single toggle.
|
|
9
|
+
if(DEFINED YABI_BUILD_TESTBENCH)
|
|
10
|
+
message(DEPRECATION "YABI_BUILD_TESTBENCH is deprecated; use YABI_BUILD_TESTS")
|
|
11
|
+
set(YABI_BUILD_TESTS "${YABI_BUILD_TESTBENCH}")
|
|
12
|
+
endif()
|
|
13
|
+
|
|
14
|
+
# The examples self-check (they return non-zero on failure), so they are
|
|
15
|
+
# registered as ctest cases just like the test suite. enable_testing() is
|
|
16
|
+
# therefore unconditional: disabling tests must not silently disable the
|
|
17
|
+
# example checks.
|
|
18
|
+
enable_testing()
|
|
19
|
+
|
|
20
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
21
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
22
|
+
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
23
|
+
|
|
24
|
+
find_package(BLAS REQUIRED)
|
|
25
|
+
find_package(LAPACK REQUIRED)
|
|
26
|
+
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
# CppAD is an external dependency and is deliberately not bundled with YABI.
|
|
29
|
+
# It is located through pkg-config, through CPPAD_ROOT, or through the usual
|
|
30
|
+
# compiler search paths.
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
set(CPPAD_ROOT "" CACHE PATH
|
|
33
|
+
"Root of a CppAD installation (the directory containing include/ and lib/)")
|
|
34
|
+
|
|
35
|
+
find_package(PkgConfig QUIET)
|
|
36
|
+
if(PkgConfig_FOUND)
|
|
37
|
+
pkg_check_modules(CPPAD_PC QUIET cppad)
|
|
38
|
+
endif()
|
|
39
|
+
|
|
40
|
+
if(CPPAD_PC_FOUND)
|
|
41
|
+
set(CPPAD_INCLUDE_DIR ${CPPAD_PC_INCLUDE_DIRS})
|
|
42
|
+
set(CPPAD_LINK_LIBRARIES ${CPPAD_PC_LIBRARIES})
|
|
43
|
+
link_directories(${CPPAD_PC_LIBRARY_DIRS})
|
|
44
|
+
else()
|
|
45
|
+
find_path(CPPAD_INCLUDE_DIR cppad/cppad.hpp
|
|
46
|
+
HINTS ${CPPAD_ROOT} $ENV{CPPAD_ROOT}
|
|
47
|
+
PATH_SUFFIXES include)
|
|
48
|
+
find_library(CPPAD_LIBRARY NAMES cppad_lib
|
|
49
|
+
HINTS ${CPPAD_ROOT} $ENV{CPPAD_ROOT}
|
|
50
|
+
PATH_SUFFIXES lib lib64 lib/${CMAKE_LIBRARY_ARCHITECTURE})
|
|
51
|
+
if(CPPAD_LIBRARY)
|
|
52
|
+
set(CPPAD_LINK_LIBRARIES ${CPPAD_LIBRARY})
|
|
53
|
+
endif()
|
|
54
|
+
endif()
|
|
55
|
+
|
|
56
|
+
option(YABI_USE_CPPAD "Enable CppAD-based integrator (default ON)" ON)
|
|
57
|
+
if(YABI_USE_CPPAD)
|
|
58
|
+
if(NOT CPPAD_INCLUDE_DIR)
|
|
59
|
+
message(FATAL_ERROR
|
|
60
|
+
"CppAD was not found.\n"
|
|
61
|
+
"Install CppAD (https://github.com/coin-or/CppAD) and make sure "
|
|
62
|
+
"cppad/cppad.hpp is on the include path, or configure with "
|
|
63
|
+
"-DCPPAD_ROOT=/path/to/cppad.")
|
|
64
|
+
endif()
|
|
65
|
+
message(STATUS "CppAD headers: ${CPPAD_INCLUDE_DIR}")
|
|
66
|
+
if(CPPAD_LINK_LIBRARIES)
|
|
67
|
+
message(STATUS "CppAD library: ${CPPAD_LINK_LIBRARIES}")
|
|
68
|
+
endif()
|
|
69
|
+
else()
|
|
70
|
+
message(STATUS "CppAD disabled (-DYABI_USE_CPPAD=OFF). Pure/manual formulation available.")
|
|
71
|
+
endif()
|
|
72
|
+
|
|
73
|
+
# ---------------------------------------------------------------------------
|
|
74
|
+
# Experimental CUDA (cuSOLVER) linear solver (T36). OFF by default; the default
|
|
75
|
+
# build stays CUDA-free. Only the host-side cuSOLVER/cuBLAS C API is used (no
|
|
76
|
+
# nvcc, no CUDA language), so we just need the headers + libcusolver + libcudart.
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
option(YABI_USE_CUDA "Build the experimental cuSOLVER linear solver + GPU test (T36)" OFF)
|
|
79
|
+
if(YABI_USE_CUDA)
|
|
80
|
+
find_path(CUDA_INCLUDE_DIR cusolverDn.h
|
|
81
|
+
PATHS /usr/local/cuda-12.6/include /usr/local/cuda/include $ENV{CUDA_HOME}
|
|
82
|
+
PATH_SUFFIXES include)
|
|
83
|
+
find_library(CUSOLVER_LIBRARY cusolver
|
|
84
|
+
PATHS /usr/local/cuda-12.6/lib64 /usr/local/cuda/lib64 $ENV{CUDA_HOME}
|
|
85
|
+
PATH_SUFFIXES lib64 lib)
|
|
86
|
+
find_library(CUSPARSE_LIBRARY cusparse
|
|
87
|
+
PATHS /usr/local/cuda-12.6/lib64 /usr/local/cuda/lib64 $ENV{CUDA_HOME}
|
|
88
|
+
PATH_SUFFIXES lib64 lib)
|
|
89
|
+
find_library(CUBLAS_LIBRARY cublas
|
|
90
|
+
PATHS /usr/local/cuda-12.6/lib64 /usr/local/cuda/lib64 $ENV{CUDA_HOME}
|
|
91
|
+
PATH_SUFFIXES lib64 lib)
|
|
92
|
+
find_library(CUDART_LIBRARY cudart
|
|
93
|
+
PATHS /usr/local/cuda-12.6/lib64 /usr/local/cuda/lib64 $ENV{CUDA_HOME}
|
|
94
|
+
PATH_SUFFIXES lib64 lib)
|
|
95
|
+
if(CUDA_INCLUDE_DIR AND CUSOLVER_LIBRARY AND CUSPARSE_LIBRARY AND CUBLAS_LIBRARY AND CUDART_LIBRARY)
|
|
96
|
+
message(STATUS "CUDA (cuSOLVER/cuSPARSE/cuBLAS) found: ${CUDA_INCLUDE_DIR}")
|
|
97
|
+
else()
|
|
98
|
+
message(FATAL_ERROR
|
|
99
|
+
"YABI_USE_CUDA=ON but CUDA (cusolverDn.h, libcusolver, libcusparse, libcublas, libcudart) was not found.")
|
|
100
|
+
endif()
|
|
101
|
+
endif()
|
|
102
|
+
|
|
103
|
+
# ---------------------------------------------------------------------------
|
|
104
|
+
# External open-source sparse solver adapter (T36d): AMGCL (header-only, MIT) +
|
|
105
|
+
# Boost headers. OFF by default; the default build stays dependency-free. The
|
|
106
|
+
# adapter is an EXAMPLE of the open solver architecture (swap the solver by
|
|
107
|
+
# swapping the adapter object).
|
|
108
|
+
# ---------------------------------------------------------------------------
|
|
109
|
+
option(YABI_USE_AMGCL "Build the external AMGCL sparse-solver adapter + test (T36d)" OFF)
|
|
110
|
+
if(YABI_USE_AMGCL)
|
|
111
|
+
find_path(AMGCL_INCLUDE_DIR amgcl/backend/builtin.hpp
|
|
112
|
+
PATHS $ENV{HOME}/.local/opt/amgcl /usr/local/include /usr/include
|
|
113
|
+
PATH_SUFFIXES include)
|
|
114
|
+
find_path(BOOST_INCLUDE_DIR boost/version.hpp
|
|
115
|
+
PATHS $ENV{HOME}/.local/opt/boost /usr/local/include /usr/include
|
|
116
|
+
PATH_SUFFIXES include)
|
|
117
|
+
find_package(OpenMP QUIET)
|
|
118
|
+
if(AMGCL_INCLUDE_DIR AND BOOST_INCLUDE_DIR)
|
|
119
|
+
message(STATUS "AMGCL found: ${AMGCL_INCLUDE_DIR} (Boost: ${BOOST_INCLUDE_DIR})")
|
|
120
|
+
else()
|
|
121
|
+
message(FATAL_ERROR
|
|
122
|
+
"YABI_USE_AMGCL=ON but AMGCL or Boost headers were not found (see README for provisioning).")
|
|
123
|
+
endif()
|
|
124
|
+
endif()
|
|
125
|
+
|
|
126
|
+
option(YABI_USE_GINKGO "Build the external Ginkgo sparse-solver adapter + test (T36d)" OFF)
|
|
127
|
+
if(YABI_USE_GINKGO)
|
|
128
|
+
find_path(GINKGO_INCLUDE_DIR ginkgo/ginkgo.hpp
|
|
129
|
+
PATHS $ENV{HOME}/.local/opt/ginkgo /usr/local/include /usr/include
|
|
130
|
+
PATH_SUFFIXES include)
|
|
131
|
+
find_library(GINKGO_LIBRARY ginkgo PATHS $ENV{HOME}/.local/opt/ginkgo PATH_SUFFIXES lib)
|
|
132
|
+
find_library(GINKGO_DEVICE_LIBRARY ginkgo_device
|
|
133
|
+
PATHS $ENV{HOME}/.local/opt/ginkgo PATH_SUFFIXES lib)
|
|
134
|
+
find_library(GINKGO_OMP_LIBRARY ginkgo_omp
|
|
135
|
+
PATHS $ENV{HOME}/.local/opt/ginkgo PATH_SUFFIXES lib)
|
|
136
|
+
find_package(OpenMP REQUIRED)
|
|
137
|
+
if(GINKGO_INCLUDE_DIR AND GINKGO_LIBRARY AND GINKGO_OMP_LIBRARY)
|
|
138
|
+
get_filename_component(GINKGO_LIBRARY_DIR "${GINKGO_LIBRARY}" DIRECTORY)
|
|
139
|
+
message(STATUS "Ginkgo found: ${GINKGO_INCLUDE_DIR} (lib: ${GINKGO_LIBRARY_DIR})")
|
|
140
|
+
else()
|
|
141
|
+
message(FATAL_ERROR
|
|
142
|
+
"YABI_USE_GINKGO=ON but Ginkgo was not found (see README for provisioning).")
|
|
143
|
+
endif()
|
|
144
|
+
endif()
|
|
145
|
+
|
|
146
|
+
option(YABI_USE_CUDSS "Build the cuDSS sparse direct solver adapter + test (T36d)" OFF)
|
|
147
|
+
if(YABI_USE_CUDSS)
|
|
148
|
+
find_path(CUDSS_INCLUDE_DIR cudss.h
|
|
149
|
+
PATHS $ENV{HOME}/.local/opt/cudss /usr/local/include /usr/include PATH_SUFFIXES include)
|
|
150
|
+
find_library(CUDSS_LIBRARY cudss
|
|
151
|
+
PATHS $ENV{HOME}/.local/opt/cudss PATH_SUFFIXES lib lib64)
|
|
152
|
+
# cuDSS needs CUDA (cudart) for the host-side cudaMalloc/cudaMemcpy.
|
|
153
|
+
find_path(CUDA_INCLUDE_DIR cuda_runtime.h
|
|
154
|
+
PATHS /usr/local/cuda-12.6/include /usr/local/cuda/include $ENV{CUDA_HOME} PATH_SUFFIXES include)
|
|
155
|
+
find_library(CUDART_LIBRARY cudart
|
|
156
|
+
PATHS /usr/local/cuda-12.6/lib64 /usr/local/cuda/lib64 $ENV{CUDA_HOME} PATH_SUFFIXES lib64 lib)
|
|
157
|
+
if(CUDSS_INCLUDE_DIR AND CUDSS_LIBRARY AND CUDA_INCLUDE_DIR AND CUDART_LIBRARY)
|
|
158
|
+
get_filename_component(CUDSS_LIBRARY_DIR "${CUDSS_LIBRARY}" DIRECTORY)
|
|
159
|
+
message(STATUS "cuDSS found: ${CUDSS_INCLUDE_DIR} (lib: ${CUDSS_LIBRARY_DIR})")
|
|
160
|
+
else()
|
|
161
|
+
message(FATAL_ERROR
|
|
162
|
+
"YABI_USE_CUDSS=ON but cuDSS or CUDA was not found (see README for provisioning).")
|
|
163
|
+
endif()
|
|
164
|
+
endif()
|
|
165
|
+
|
|
166
|
+
# ---------------------------------------------------------------------------
|
|
167
|
+
# Library
|
|
168
|
+
# ---------------------------------------------------------------------------
|
|
169
|
+
add_library(yabi src/bdf_integrator.cpp)
|
|
170
|
+
add_library(YABI::yabi ALIAS yabi)
|
|
171
|
+
|
|
172
|
+
target_include_directories(yabi
|
|
173
|
+
PUBLIC
|
|
174
|
+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
|
175
|
+
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
|
176
|
+
)
|
|
177
|
+
target_include_directories(yabi SYSTEM PUBLIC ${CPPAD_INCLUDE_DIR})
|
|
178
|
+
target_link_libraries(yabi
|
|
179
|
+
PUBLIC
|
|
180
|
+
${LAPACK_LIBRARIES}
|
|
181
|
+
${BLAS_LIBRARIES}
|
|
182
|
+
${CPPAD_LINK_LIBRARIES}
|
|
183
|
+
)
|
|
184
|
+
set_target_properties(yabi PROPERTIES
|
|
185
|
+
POSITION_INDEPENDENT_CODE ON
|
|
186
|
+
VERSION ${PROJECT_VERSION}
|
|
187
|
+
SOVERSION 0
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
# Pure library: the same integrator core compiled without CppAD (YABI_NO_CPPAD
|
|
191
|
+
# drops the cppad_rhs.hpp include and the CppAD::ADFun convenience constructor),
|
|
192
|
+
# so a user_ode.hpp / UserODERHS consumer can build and run without <cppad/cppad.hpp>.
|
|
193
|
+
add_library(yabi_pure src/bdf_integrator_pure.cpp)
|
|
194
|
+
target_include_directories(yabi_pure
|
|
195
|
+
PUBLIC
|
|
196
|
+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
|
197
|
+
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
|
198
|
+
)
|
|
199
|
+
target_compile_definitions(yabi_pure PUBLIC YABI_NO_CPPAD)
|
|
200
|
+
target_link_libraries(yabi_pure
|
|
201
|
+
PUBLIC
|
|
202
|
+
${LAPACK_LIBRARIES}
|
|
203
|
+
${BLAS_LIBRARIES}
|
|
204
|
+
)
|
|
205
|
+
set_target_properties(yabi_pure PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
|
206
|
+
|
|
207
|
+
# ---------------------------------------------------------------------------
|
|
208
|
+
# Python bindings (T37) — pybind11 `yabi` module on the analytic (pure) path.
|
|
209
|
+
# OFF by default so the default build stays Python-free. Requires pybind11
|
|
210
|
+
# (>= 3.0 for Python 3.14): `pip install pybind11` and re-configure.
|
|
211
|
+
# ---------------------------------------------------------------------------
|
|
212
|
+
option(YABI_BUILD_PYTHON "Build the pybind11 `yabi` Python module (T37)" OFF)
|
|
213
|
+
if(YABI_BUILD_PYTHON)
|
|
214
|
+
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
|
|
215
|
+
# Locate a pip-installed pybind11 (>= 3.0) via its Python helper, so the
|
|
216
|
+
# configure step finds it wherever pip put it (no hand-set pybind11_DIR).
|
|
217
|
+
execute_process(COMMAND ${Python3_EXECUTABLE} -m pybind11 --cmakedir
|
|
218
|
+
OUTPUT_VARIABLE pybind11_DIR
|
|
219
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
220
|
+
RESULT_VARIABLE pybind11_HELPER_RC)
|
|
221
|
+
if(NOT pybind11_HELPER_RC EQUAL 0)
|
|
222
|
+
message(FATAL_ERROR
|
|
223
|
+
"pybind11 not found: `pip install pybind11` (>= 3.0) and re-configure.")
|
|
224
|
+
endif()
|
|
225
|
+
find_package(pybind11 CONFIG REQUIRED)
|
|
226
|
+
pybind11_add_module(yabi_python bindings/python/yabi.cpp)
|
|
227
|
+
# Importable as `import yabi` (target name stays yabi_python to avoid the
|
|
228
|
+
# C++ `yabi` library target).
|
|
229
|
+
set_target_properties(yabi_python PROPERTIES OUTPUT_NAME "yabi")
|
|
230
|
+
target_link_libraries(yabi_python PRIVATE yabi_pure)
|
|
231
|
+
# T54 — optional CUDA (cuSOLVER) Newton solve in the Python module. Reuses
|
|
232
|
+
# the CUDA detection above; the default build stays CUDA-free.
|
|
233
|
+
if(YABI_USE_CUDA AND CUDA_INCLUDE_DIR AND CUSOLVER_LIBRARY AND CUDART_LIBRARY)
|
|
234
|
+
target_include_directories(yabi_python PRIVATE ${CUDA_INCLUDE_DIR})
|
|
235
|
+
target_link_libraries(yabi_python PRIVATE ${CUSOLVER_LIBRARY} ${CUDART_LIBRARY})
|
|
236
|
+
target_compile_definitions(yabi_python PRIVATE YABI_HAS_CUDA)
|
|
237
|
+
message(STATUS "yabi_python: CUDA Newton solve enabled (cuSOLVER)")
|
|
238
|
+
endif()
|
|
239
|
+
# Install the compiled module into the wheel/site-packages root so `import yabi`
|
|
240
|
+
# resolves (used by scikit-build-core packaging; harmless in a plain build).
|
|
241
|
+
install(TARGETS yabi_python LIBRARY DESTINATION .)
|
|
242
|
+
# CI-verifiable smoke test (only when the module is built).
|
|
243
|
+
add_test(NAME python_bindings
|
|
244
|
+
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/bindings/python/test_yabi.py)
|
|
245
|
+
set_tests_properties(python_bindings PROPERTIES
|
|
246
|
+
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:yabi_python>"
|
|
247
|
+
LABELS "python")
|
|
248
|
+
# T38 — forward/reverse (adjoint) bindings vs central differences (no torch needed).
|
|
249
|
+
add_test(NAME python_autograd
|
|
250
|
+
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/bindings/python/test_autograd.py)
|
|
251
|
+
set_tests_properties(python_autograd PROPERTIES
|
|
252
|
+
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:yabi_python>"
|
|
253
|
+
LABELS "python")
|
|
254
|
+
# T51 — batched differentiable solve_ivp (needs torch; registered only when
|
|
255
|
+
# torch is importable so a torch-less build doesn't fail).
|
|
256
|
+
execute_process(COMMAND ${Python3_EXECUTABLE} -c "import torch"
|
|
257
|
+
RESULT_VARIABLE YABI_TORCH_AVAILABLE)
|
|
258
|
+
if(YABI_TORCH_AVAILABLE EQUAL 0)
|
|
259
|
+
add_test(NAME python_solve_ivp
|
|
260
|
+
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/bindings/python/test_solve_ivp.py)
|
|
261
|
+
set_tests_properties(python_solve_ivp PROPERTIES
|
|
262
|
+
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:yabi_python>:${CMAKE_CURRENT_SOURCE_DIR}/python"
|
|
263
|
+
LABELS "python")
|
|
264
|
+
# T52 — nn.Module f with automatic Jacobian (dense + matrix-free).
|
|
265
|
+
add_test(NAME python_nn_module
|
|
266
|
+
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/bindings/python/test_nn_module.py)
|
|
267
|
+
set_tests_properties(python_nn_module PROPERTIES
|
|
268
|
+
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:yabi_python>:${CMAKE_CURRENT_SOURCE_DIR}/python"
|
|
269
|
+
LABELS "python")
|
|
270
|
+
# T53 — parallel batch (threads=N bit-identical to serial).
|
|
271
|
+
add_test(NAME python_batch_parallel
|
|
272
|
+
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/bindings/python/test_batch_parallel.py)
|
|
273
|
+
set_tests_properties(python_batch_parallel PROPERTIES
|
|
274
|
+
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:yabi_python>:${CMAKE_CURRENT_SOURCE_DIR}/python"
|
|
275
|
+
LABELS "python")
|
|
276
|
+
# T54 — GPU (device="cuda"): skips cleanly when torch CUDA / a CUDA build
|
|
277
|
+
# of the module is absent.
|
|
278
|
+
add_test(NAME python_cuda
|
|
279
|
+
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/bindings/python/test_cuda.py)
|
|
280
|
+
set_tests_properties(python_cuda PROPERTIES
|
|
281
|
+
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:yabi_python>:${CMAKE_CURRENT_SOURCE_DIR}/python"
|
|
282
|
+
LABELS "python")
|
|
283
|
+
endif()
|
|
284
|
+
endif()
|
|
285
|
+
|
|
286
|
+
# T44 — telemetry harness: emits the machine-readable evidence table consumed by
|
|
287
|
+
# benchmarks/report.py, and self-checks tolerance adherence (error decreases as
|
|
288
|
+
# rtol is tightened; the tightest rtol reaches < 1e-6), so the evidence layer is
|
|
289
|
+
# CI-verified as a ctest under the "benchmark" label.
|
|
290
|
+
if(YABI_USE_CPPAD)
|
|
291
|
+
add_executable(yabi_telemetry benchmarks/telemetry.cpp)
|
|
292
|
+
target_link_libraries(yabi_telemetry PRIVATE yabi)
|
|
293
|
+
add_test(NAME telemetry COMMAND yabi_telemetry)
|
|
294
|
+
set_tests_properties(telemetry PROPERTIES LABELS "benchmark")
|
|
295
|
+
endif()
|
|
296
|
+
|
|
297
|
+
# ---------------------------------------------------------------------------
|
|
298
|
+
# Examples
|
|
299
|
+
# ---------------------------------------------------------------------------
|
|
300
|
+
if(YABI_BUILD_EXAMPLES)
|
|
301
|
+
foreach(example basic nonautonomous sensitivity_demo)
|
|
302
|
+
add_executable(yabi_${example} examples/${example}.cpp)
|
|
303
|
+
target_link_libraries(yabi_${example} PRIVATE yabi)
|
|
304
|
+
list(APPEND YABI_EXAMPLE_TARGETS yabi_${example})
|
|
305
|
+
# Each example checks its own result and returns non-zero on failure.
|
|
306
|
+
add_test(NAME example_${example} COMMAND yabi_${example})
|
|
307
|
+
set_tests_properties(example_${example} PROPERTIES LABELS "example")
|
|
308
|
+
endforeach()
|
|
309
|
+
# Trajectory dumper for the benchmark gallery (utility, not a ctest).
|
|
310
|
+
add_executable(dump_trajectories examples/dump_trajectories.cpp)
|
|
311
|
+
target_link_libraries(dump_trajectories PRIVATE yabi)
|
|
312
|
+
endif()
|
|
313
|
+
|
|
314
|
+
# ---------------------------------------------------------------------------
|
|
315
|
+
# Test suite
|
|
316
|
+
# ---------------------------------------------------------------------------
|
|
317
|
+
# tests/unit isolated component contracts (CppAD-free where possible)
|
|
318
|
+
# tests/component cross-component integration (native sparse, SuiteSparse)
|
|
319
|
+
# tests/system mathematical benchmarks (canonical ODE problems)
|
|
320
|
+
# add_subdirectory(tests) is at the end so the optional SuiteSparse probes have
|
|
321
|
+
# run before the component tests are configured.
|
|
322
|
+
|
|
323
|
+
# ---------------------------------------------------------------------------
|
|
324
|
+
# Install
|
|
325
|
+
# ---------------------------------------------------------------------------
|
|
326
|
+
include(GNUInstallDirs)
|
|
327
|
+
if(YABI_INSTALL_CORE)
|
|
328
|
+
if(YABI_USE_CPPAD)
|
|
329
|
+
install(TARGETS yabi
|
|
330
|
+
EXPORT YABITargets
|
|
331
|
+
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
332
|
+
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
333
|
+
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
|
334
|
+
else()
|
|
335
|
+
install(TARGETS yabi_pure
|
|
336
|
+
EXPORT YABITargets
|
|
337
|
+
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
338
|
+
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
339
|
+
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
|
340
|
+
endif()
|
|
341
|
+
install(DIRECTORY include/yabi DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
|
342
|
+
install(EXPORT YABITargets
|
|
343
|
+
NAMESPACE YABI::
|
|
344
|
+
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/YABI
|
|
345
|
+
)
|
|
346
|
+
endif()
|
|
347
|
+
|
|
348
|
+
# Optional: UMFPACK sparse solver example (if SuiteSparse available)
|
|
349
|
+
find_path(UMFPACK_INCLUDE_DIR umfpack.h PATHS /usr/include/suitesparse)
|
|
350
|
+
find_library(UMFPACK_LIBRARY umfpack)
|
|
351
|
+
if(UMFPACK_INCLUDE_DIR AND UMFPACK_LIBRARY)
|
|
352
|
+
add_executable(yabi_heat1d_umfpack examples/heat_1d_sparse_umfpack.cpp)
|
|
353
|
+
target_include_directories(yabi_heat1d_umfpack PRIVATE ${UMFPACK_INCLUDE_DIR})
|
|
354
|
+
target_link_libraries(yabi_heat1d_umfpack PRIVATE yabi ${UMFPACK_LIBRARY} amd cholmod colamd ccolamd camd suitesparseconfig lapack blas cppad_lib m)
|
|
355
|
+
endif()
|
|
356
|
+
if(UMFPACK_INCLUDE_DIR AND UMFPACK_LIBRARY)
|
|
357
|
+
add_executable(yabi_heat1d_umfpack_sens examples/heat_1d_umfpack_forward_sens.cpp)
|
|
358
|
+
target_include_directories(yabi_heat1d_umfpack_sens PRIVATE ${UMFPACK_INCLUDE_DIR})
|
|
359
|
+
target_link_libraries(yabi_heat1d_umfpack_sens PRIVATE yabi ${UMFPACK_LIBRARY} amd cholmod colamd ccolamd camd suitesparseconfig lapack blas cppad_lib m)
|
|
360
|
+
endif()
|
|
361
|
+
if(UMFPACK_INCLUDE_DIR AND UMFPACK_LIBRARY)
|
|
362
|
+
add_executable(yabi_heat1d_umfpack_sens2 examples/heat1d_umfpack_sens_simple.cpp)
|
|
363
|
+
target_include_directories(yabi_heat1d_umfpack_sens2 PRIVATE ${UMFPACK_INCLUDE_DIR})
|
|
364
|
+
target_link_libraries(yabi_heat1d_umfpack_sens2 PRIVATE yabi ${UMFPACK_LIBRARY} amd cholmod colamd ccolamd camd suitesparseconfig lapack blas cppad_lib m)
|
|
365
|
+
endif()
|
|
366
|
+
if(UMFPACK_INCLUDE_DIR AND UMFPACK_LIBRARY)
|
|
367
|
+
add_executable(yabi_heat1d_umfpack_sens3 examples/heat1d_umfpack_sens2.cpp)
|
|
368
|
+
target_include_directories(yabi_heat1d_umfpack_sens3 PRIVATE ${UMFPACK_INCLUDE_DIR})
|
|
369
|
+
target_link_libraries(yabi_heat1d_umfpack_sens3 PRIVATE yabi ${UMFPACK_LIBRARY} amd cholmod colamd ccolamd camd suitesparseconfig lapack blas cppad_lib m)
|
|
370
|
+
endif()
|
|
371
|
+
if(UMFPACK_INCLUDE_DIR AND UMFPACK_LIBRARY)
|
|
372
|
+
add_executable(yabi_heat1d_umfpack_sens4 examples/heat1d_umfpack_sens3.cpp)
|
|
373
|
+
target_include_directories(yabi_heat1d_umfpack_sens4 PRIVATE ${UMFPACK_INCLUDE_DIR})
|
|
374
|
+
target_link_libraries(yabi_heat1d_umfpack_sens4 PRIVATE yabi ${UMFPACK_LIBRARY} amd cholmod colamd ccolamd camd suitesparseconfig lapack blas cppad_lib m)
|
|
375
|
+
endif()
|
|
376
|
+
add_executable(yabi_heat1d_dense_lit examples/heat1d_dense_literature.cpp)
|
|
377
|
+
target_link_libraries(yabi_heat1d_dense_lit PRIVATE yabi cppad_lib m)
|
|
378
|
+
add_executable(yabi_heat1d_dense_analytic examples/heat1d_dense_analytic_jac.cpp)
|
|
379
|
+
target_link_libraries(yabi_heat1d_dense_analytic PRIVATE yabi cppad_lib m)
|
|
380
|
+
if(UMFPACK_INCLUDE_DIR AND UMFPACK_LIBRARY)
|
|
381
|
+
add_executable(yabi_heat1d_umfpack_lit examples/heat1d_umfpack_literature.cpp)
|
|
382
|
+
target_include_directories(yabi_heat1d_umfpack_lit PRIVATE ${UMFPACK_INCLUDE_DIR})
|
|
383
|
+
target_link_libraries(yabi_heat1d_umfpack_lit PRIVATE yabi ${UMFPACK_LIBRARY} amd cholmod colamd ccolamd camd suitesparseconfig lapack blas cppad_lib m)
|
|
384
|
+
endif()
|
|
385
|
+
if(UMFPACK_INCLUDE_DIR AND UMFPACK_LIBRARY)
|
|
386
|
+
add_executable(yabi_heat1d_umfpack_sens_dense_api examples/heat1d_umfpack_sens_dense_equiv.cpp)
|
|
387
|
+
target_include_directories(yabi_heat1d_umfpack_sens_dense_api PRIVATE ${UMFPACK_INCLUDE_DIR})
|
|
388
|
+
target_link_libraries(yabi_heat1d_umfpack_sens_dense_api PRIVATE yabi ${UMFPACK_LIBRARY} amd cholmod colamd ccolamd camd suitesparseconfig lapack blas cppad_lib m)
|
|
389
|
+
endif()
|
|
390
|
+
|
|
391
|
+
# Optional SuiteSparse solvers for the component test tier.
|
|
392
|
+
find_path(KLU_INCLUDE_DIR klu.h PATHS /usr/include/suitesparse)
|
|
393
|
+
find_library(KLU_LIBRARY klu)
|
|
394
|
+
|
|
395
|
+
# -----------------------------------------------------------------
|
|
396
|
+
# Plot runner (matplotlib / numpy / pandas) — external, not required
|
|
397
|
+
# -----------------------------------------------------------------
|
|
398
|
+
find_package(Python3 COMPONENTS Interpreter)
|
|
399
|
+
if(Python3_FOUND)
|
|
400
|
+
find_path(PYTHON_MATPLOTLIB matplotlib __init__.py PATHS /usr/lib/python3/dist-packages /usr/local/lib/python3.14/site-packages)
|
|
401
|
+
if(PYTHON_MATPLOTLIB)
|
|
402
|
+
add_custom_target(plot_all
|
|
403
|
+
COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/pyplot/plot_all.py
|
|
404
|
+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
405
|
+
COMMENT "Plotting yabi-bdf examples to plots/ (requires matplotlib/numpy/pandas)")
|
|
406
|
+
else()
|
|
407
|
+
message(STATUS "matplotlib not found in python3 — plot_all target skipped; install with pip install matplotlib numpy pandas")
|
|
408
|
+
endif()
|
|
409
|
+
endif()
|
|
410
|
+
|
|
411
|
+
# CSV dump for plotting
|
|
412
|
+
# CSV dump for plotting
|
|
413
|
+
|
|
414
|
+
# Plot examples (additional targets, not part of tests)
|
|
415
|
+
add_executable(plot_brusselator_traj examples/plot_brusselator_traj.cpp)
|
|
416
|
+
target_link_libraries(plot_brusselator_traj PRIVATE yabi cppad_lib m)
|
|
417
|
+
add_executable(plot_robertson_traj examples/plot_robertson_traj.cpp)
|
|
418
|
+
target_link_libraries(plot_robertson_traj PRIVATE yabi cppad_lib m)
|
|
419
|
+
|
|
420
|
+
# ---------------------------------------------------------------------------
|
|
421
|
+
# Tests (unit / component / system)
|
|
422
|
+
# ---------------------------------------------------------------------------
|
|
423
|
+
if(YABI_BUILD_TESTS)
|
|
424
|
+
add_subdirectory(tests)
|
|
425
|
+
endif()
|