xad_rs/lib.rs
1//! # `xad-rs` — Automatic Differentiation for Rust
2//!
3//! Exact, machine-precision derivatives of arbitrary numerical programs —
4//! no finite-difference error, no symbolic manipulation.
5//!
6//! `xad-rs` ships four AD modes in a single crate, each suited to a
7//! different problem shape. Every mode also has a **named** variant that
8//! lets you read back gradients by variable name (`"spot"`, `"vol"`, ...)
9//! instead of positional index.
10//!
11//! # Choosing a mode
12//!
13//! | Type | Mode | Order | Use when |
14//! |---|---|---|---|
15//! | [`FReal<T>`] | Forward | 1st | 1 input direction, many outputs |
16//! | [`Dual`] | Forward, multi-var | 1st | full gradient in one pass |
17//! | [`Dual2<T>`] | Forward, 2nd-order | 1st + 2nd | diagonal Hessian / gamma |
18//! | [`AReal<T>`] + [`Tape`] | Reverse (adjoint) | 1st | many inputs, scalar output |
19//!
20//! Reverse mode breaks even with forward around `n ~ 4` inputs. For
21//! `n >> 4` (e.g. 30-input swap pricer), reverse is dramatically faster.
22//!
23//! # Quick start — reverse mode
24//!
25//! ```
26//! use xad_rs::{AReal, Tape, math};
27//!
28//! let mut tape = Tape::<f64>::new(true);
29//! tape.activate();
30//!
31//! let mut x = AReal::new(3.0);
32//! let mut y = AReal::new(4.0);
33//! AReal::register_input(std::slice::from_mut(&mut x), &mut tape);
34//! AReal::register_input(std::slice::from_mut(&mut y), &mut tape);
35//!
36//! // f(x, y) = x^2 * y + sin(x)
37//! let mut f = &(&x * &x) * &y + math::ad::sin(&x);
38//! AReal::register_output(std::slice::from_mut(&mut f), &mut tape);
39//! f.set_adjoint(&mut tape, 1.0);
40//! tape.compute_adjoints();
41//!
42//! let dfdx = x.adjoint(&tape); // 2xy + cos(x)
43//! let dfdy = y.adjoint(&tape); // x^2
44//! assert!((dfdx - (2.0 * 3.0 * 4.0 + 3.0_f64.cos())).abs() < 1e-12);
45//! assert!((dfdy - 9.0).abs() < 1e-12);
46//! # xad_rs::Tape::<f64>::deactivate_all();
47//! ```
48//!
49//! # Quick start — forward mode
50//!
51//! Seed all inputs in one pass and read the full gradient:
52//!
53//! ```
54//! use xad_rs::Dual;
55//!
56//! let (x, y) = (Dual::variable(3.0, 0, 2), Dual::variable(4.0, 1, 2));
57//! let f = &(&x * &x) * &y; // x^2 * y
58//! assert_eq!(f.partial(0), 24.0); // df/dx = 2xy
59//! assert_eq!(f.partial(1), 9.0); // df/dy = x^2
60//! ```
61//!
62//! # Named variables
63//!
64//! Access derivatives by name instead of index — useful in financial
65//! models with many risk factors:
66//!
67//! ```
68//! use xad_rs::{NamedForwardTape, NamedForwardScope};
69//!
70//! let mut ft = NamedForwardTape::new();
71//! let spot_h = ft.declare_dual("spot", 100.0);
72//! let strike_h = ft.declare_dual("strike", 105.0);
73//! let scope: NamedForwardScope = ft.freeze_dual();
74//!
75//! let spot = scope.dual(spot_h);
76//! let strike = scope.dual(strike_h);
77//! let ratio = spot / strike;
78//!
79//! assert!((ratio.partial("spot") - 1.0 / 105.0).abs() < 1e-14);
80//! ```
81//!
82//! # Second-order derivatives
83//!
84//! ```
85//! use xad_rs::Dual2;
86//!
87//! let x: Dual2<f64> = Dual2::variable(2.0);
88//! let y = x * x * x; // x^3
89//! assert_eq!(y.first_derivative(), 12.0); // 3x^2
90//! assert_eq!(y.second_derivative(), 12.0); // 6x
91//! ```
92//!
93//! # Module overview
94//!
95//! | Module | Contents |
96//! |---|---|
97//! | [`forward`] | `FReal`, `Dual`, `Dual2`, `Dual2Vec` + named wrappers |
98//! | [`reverse`] | `AReal`, `NamedAReal`, `NamedTape` |
99//! | [`math`] | AD-aware transcendentals (`sin`, `exp`, `erf`, `norm_cdf`, ...) |
100//! | [`tape`] | Reverse-mode tape and thread-local active-tape slot |
101//! | [`ops`] | `compute_jacobian_*`, `compute_hessian`, `compute_full_hessian` |
102//! | [`scalar`] | The [`Scalar`] trait bound (`f32`, `f64`) |
103//! | [`registry`] | [`VarRegistry`] — ordered name-to-index map |
104//! | [`forward_tape`] | [`NamedForwardTape`] / [`NamedForwardScope`] setup |
105
106pub mod scalar;
107pub mod tape;
108pub mod math;
109pub mod registry;
110pub mod forward_tape;
111pub mod forward;
112pub mod reverse;
113pub mod ops;
114
115// ---- re-exports: positional types ----
116pub use forward::{FReal, Dual, Dual2, Dual2Vec};
117pub use reverse::AReal;
118pub use tape::{Tape, TapeStorage};
119pub use scalar::Scalar;
120
121// ---- re-exports: named types ----
122pub use forward::{NamedFReal, NamedDual, NamedDual2};
123pub use reverse::{NamedAReal, NamedTape};
124pub use registry::VarRegistry;
125pub use forward_tape::{NamedForwardTape, NamedForwardScope, DualHandle, Dual2Handle};
126
127// ---- re-exports: composite operations ----
128pub use ops::{compute_jacobian_rev, compute_jacobian_fwd, compute_hessian};
129pub use ops::{NamedJacobian, compute_named_jacobian};
130pub use ops::{NamedHessian, compute_full_hessian};
131
132/// C++ XAD-compatible type aliases for reverse mode (`f64`).
133pub mod adj {
134 use super::*;
135 pub type TapeType = Tape<f64>;
136 pub type ActiveType = AReal<f64>;
137 pub type PassiveType = f64;
138}
139
140/// C++ XAD-compatible type aliases for reverse mode (`f32`).
141pub mod adjf {
142 use super::*;
143 pub type TapeType = Tape<f32>;
144 pub type ActiveType = AReal<f32>;
145 pub type PassiveType = f32;
146}
147
148/// C++ XAD-compatible type aliases for forward mode (`f64`).
149pub mod fwd {
150 use super::*;
151 pub type ActiveType = FReal<f64>;
152 pub type PassiveType = f64;
153}
154
155/// C++ XAD-compatible type aliases for forward mode (`f32`).
156pub mod fwdf {
157 use super::*;
158 pub type ActiveType = FReal<f32>;
159 pub type PassiveType = f32;
160}