Expand description
§xad-rs — Automatic Differentiation for Rust
Exact, machine-precision derivatives of arbitrary numerical programs — no finite-difference error, no symbolic manipulation.
xad-rs ships four AD modes in a single crate, each suited to a
different problem shape. Every mode also has a named variant that
lets you read back gradients by variable name ("spot", "vol", …)
instead of positional index.
Conceptually, the crate is built around the Real trait — a unified
active-scalar abstraction that lets the same numerical body run
against f64, forward-mode, or reverse-mode types.
See also (long-form theory): docs/README.md on GitHub.
§Choosing what to program against
Program your numerical logic once against the trait Real; pick
the concrete mode at the call site that matches your problem shape:
use xad_rs::prelude::*;
// Same body, four call sites — see below.
fn quadratic<R: Real>(x: &R) -> R {
x.clone() * x.clone() + R::from(2.0_f64) * x.clone() + R::from(1.0_f64)
}§Choosing a mode
| Type | Mode | Order | Use when |
|---|---|---|---|
f64 | none (passive) | 0 | no derivatives needed |
Jet1<T> | Forward | 1st | 1 input direction, many outputs |
Jet1Vec | Forward, multi-var | 1st | full gradient in one pass |
Jet2<T> | Forward, 2nd-order | 1st + 2nd | diagonal Hessian / gamma |
AReal<T> + Tape | Reverse (adjoint) | 1st | many inputs, scalar output |
Reverse mode breaks even with forward around n ~ 4 inputs. For
n >> 4 (e.g. 30-input swap pricer), reverse is dramatically faster.
§Quick start — reverse mode
use xad_rs::{AReal, Tape, math};
let mut tape = Tape::<f64>::new(true);
tape.activate();
let mut x = AReal::new(3.0);
let mut y = AReal::new(4.0);
AReal::register_input(std::slice::from_mut(&mut x), &mut tape);
AReal::register_input(std::slice::from_mut(&mut y), &mut tape);
// f(x, y) = x^2 * y + sin(x)
let mut f = &(&x * &x) * &y + math::ad::sin(&x);
AReal::register_output(std::slice::from_mut(&mut f), &mut tape);
f.set_adjoint(&mut tape, 1.0);
tape.compute_adjoints();
let dfdx = x.adjoint(&tape); // 2xy + cos(x)
let dfdy = y.adjoint(&tape); // x^2
assert!((dfdx - (2.0 * 3.0 * 4.0 + 3.0_f64.cos())).abs() < 1e-12);
assert!((dfdy - 9.0).abs() < 1e-12);§Quick start — forward mode
Seed all inputs in one pass and read the full gradient:
use xad_rs::Jet1Vec;
let (x, y) = (Jet1Vec::variable(3.0, 0, 2), Jet1Vec::variable(4.0, 1, 2));
let f = &(&x * &x) * &y; // x^2 * y
assert_eq!(f.partial(0), 24.0); // df/dx = 2xy
assert_eq!(f.partial(1), 9.0); // df/dy = x^2§Named variables
Access derivatives by name instead of index — useful in financial models with many risk factors:
use xad_rs::{NamedForwardTape, NamedForwardScope};
let mut ft = NamedForwardTape::new();
let spot_h = ft.declare_jet1vec("spot", 100.0);
let strike_h = ft.declare_jet1vec("strike", 105.0);
let scope: NamedForwardScope = ft.into_scope();
let spot = scope.jet1vec(spot_h);
let strike = scope.jet1vec(strike_h);
let ratio = spot / strike;
assert!((ratio.partial("spot") - 1.0 / 105.0).abs() < 1e-14);§Second-order derivatives
use xad_rs::Jet2;
let x: Jet2<f64> = Jet2::variable(2.0);
let y = x * x * x; // x^3
assert_eq!(y.first_derivative(), 12.0); // 3x^2
assert_eq!(y.second_derivative(), 12.0); // 6x§Module overview
| Module | Contents |
|---|---|
real | The unified active-scalar trait Real |
real_stats | The RealStats extension trait (erf, norm_cdf, …) |
passive | The passive-scalar bound Passive (f32, f64) — was Scalar in 0.4.x |
prelude | Real, RealStats, Passive, AReal, Jet1, Jet2, Tape, TapeStorage |
forward | Jet1, Jet1Vec, Jet2, Jet2Vec + named wrappers |
reverse | AReal, NamedAReal, NamedTape |
math | AD-aware transcendentals (sin, exp, erf, norm_cdf, …) |
tape | Reverse-mode tape and thread-local active-tape slot |
ops | compute_jacobian_*, compute_hessian, compute_full_hessian |
registry | VarRegistry — ordered name-to-index map |
forward_tape | NamedForwardTape / NamedForwardScope setup |
Re-exports§
pub use forward::Jet1;pub use forward::Jet1Vec;pub use forward::Jet2;pub use forward::Jet2Vec;pub use reverse::AReal;pub use tape::Tape;pub use tape::TapeGuard;pub use tape::TapeStorage;pub use passive::Passive;pub use real::Real;pub use real_stats::RealStats;pub use forward::NamedJet1;pub use forward::NamedJet1Vec;pub use forward::NamedJet2;pub use reverse::NamedAReal;pub use reverse::NamedTape;pub use registry::VarRegistry;pub use forward_tape::NamedForwardTape;pub use forward_tape::NamedForwardScope;pub use forward_tape::Jet1Handle;pub use forward_tape::Jet1VecHandle;pub use forward_tape::Jet2Handle;pub use ops::compute_jacobian_rev;pub use ops::compute_jacobian_fwd;pub use ops::compute_hessian;pub use ops::NamedJacobian;pub use ops::compute_named_jacobian;pub use ops::NamedHessian;pub use ops::compute_full_hessian;
Modules§
- forward
- Forward-mode AD types.
- forward_
tape NamedForwardTape<T>— declare-and-scope lifecycle for named forward-mode values.- math
- AD-aware transcendental functions.
- ops
- Composite AD operations: Jacobian and Hessian computations.
- passive
- The
Passivetrait — the bound for the underlying passive (non-AD) scalar storage type that the active scalars (AReal<T>,Jet1<T>,Jet2<T>) wrap. - prelude
- Convenience re-exports —
use xad_rs::prelude::*;brings the mode-agnostic traitReal, the passive boundPassive, and the most-used concrete types into scope. - real
- The
Realtrait — the unified active-scalar trait that abstracts over every AD mode this crate ships (reverse viacrate::AReal, forward first-order viacrate::Jet1, forward second-order viacrate::Jet2) plus the no-AD case (plainf64). - real_
stats - The
RealStatsextension trait — statistical functions (erf,erfc,norm_cdf,inv_norm_cdf) for the active-scalar surface. - registry
VarRegistry— frozen-after-construction ordered set of variable names.- reverse
- Reverse-mode (adjoint) AD types.
- tape
- Tape for recording operations in reverse-mode automatic differentiation.