xad-rs 0.2.0

Automatic differentiation library for Rust — forward/reverse mode AD, a Rust port of the C++ XAD library (https://github.com/auto-differentiation/xad)
Documentation
//! # `xad-rs` — Automatic Differentiation for Rust
//!
//! A fast, ergonomic, type-safe automatic differentiation library for
//! Rust. `xad-rs` is an unofficial port of the C++
//! [XAD](https://github.com/auto-differentiation/xad) library by Xcelerit,
//! and ships all four of the canonical AD flavours in a single crate.
//!
//! Automatic differentiation computes derivatives of arbitrary programs
//! **exactly**, to machine precision, without finite-difference error or
//! symbolic manipulation. `xad-rs` is suitable for quantitative finance,
//! optimisation, machine learning, computational physics, and any setting
//! where gradients or Hessians of non-trivial numerical code are needed.
//!
//! ## Picking a mode
//!
//! | Type | Mode | Order | Seeds | Use when |
//! |---|---|---|---|---|
//! | [`FReal<T>`](freal::FReal) | Forward (tangent) | 1st | 1 direction | few inputs, many outputs |
//! | [`Dual`] | Forward, multi-variable | 1st | `n` in one pass | full gradient in one forward sweep |
//! | [`Dual2<T>`](dual2::Dual2) | Forward, second-order | 1st + 2nd | 1 direction | diagonal Hessian / own-gamma |
//! | [`AReal<T>`](areal::AReal) + [`Tape`] | Reverse (adjoint) | 1st | 1 adjoint seed | many inputs, few outputs (gradients of scalar losses) |
//!
//! Reverse mode is the right choice for `f: Rⁿ → R` with `n` larger than a
//! handful (the break-even vs. forward mode sits around `n ≈ 4` in this
//! crate after the April 2026 perf refactor — see `CHANGELOG.md`). Forward
//! mode is the right choice when there are few inputs (1–3) or many
//! outputs relative to inputs.
//!
//! ## Quick start — reverse mode gradient
//!
//! ```
//! use xad_rs::areal::AReal;
//! use xad_rs::tape::Tape;
//! use xad_rs::math;
//!
//! let mut tape = Tape::<f64>::new(true);
//! tape.activate();
//!
//! // f(x, y) = x² · y + sin(x), at (x, y) = (3, 4)
//! 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);
//!
//! 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();
//!
//! // ∂f/∂x = 2xy + cos(x) ≈ 23.010
//! // ∂f/∂y = x²          = 9.0
//! let dfdx = x.adjoint(&tape);
//! let dfdy = y.adjoint(&tape);
//! assert!((dfdx - (2.0 * 3.0 * 4.0 + 3.0_f64.cos())).abs() < 1e-12);
//! assert!((dfdy - 9.0).abs() < 1e-12);
//! # xad_rs::tape::Tape::<f64>::deactivate_all();
//! ```
//!
//! ## Quick start — forward mode (multi-variable)
//!
//! Seed all inputs in a single `n`-dimensional [`Dual`] and one forward
//! pass yields the whole gradient:
//!
//! ```
//! use xad_rs::dual::Dual;
//!
//! // f(x, y) = x² · y, at (x, y) = (3, 4)
//! let n = 2;
//! let x = Dual::variable(3.0, 0, n);
//! let y = Dual::variable(4.0, 1, n);
//! let f = &(&x * &x) * &y;
//! assert_eq!(f.real(), 36.0);
//! assert_eq!(f.partial(0), 24.0);  // ∂f/∂x = 2xy
//! assert_eq!(f.partial(1),  9.0);  // ∂f/∂y = x²
//! ```
//!
//! ## Second-order derivatives
//!
//! ```
//! use xad_rs::dual2::Dual2;
//!
//! // f(x) = x³, at x = 2
//! let x: Dual2<f64> = Dual2::variable(2.0);
//! let y = x * x * x;
//! assert_eq!(y.value(),             8.0);  // f(x)
//! assert_eq!(y.first_derivative(), 12.0);  // 3x²
//! assert_eq!(y.second_derivative(),12.0);  // 6x
//! ```
//!
//! ## Crate naming
//!
//! The crate name on crates.io is **`xad-rs`**; the library target is
//! **`xad_rs`** (Cargo rewrites the hyphen to an underscore), which is
//! what you write in `use` statements:
//!
//! ```ignore
//! use xad_rs::areal::AReal;
//! use xad_rs::tape::Tape;
//! ```
//!
//! ## Modules
//!
//! - [`areal`] — reverse-mode active scalar, records on a [`Tape`]
//! - [`dual`] — forward-mode multi-variable dual number
//! - [`dual2`] — forward-mode second-order dual number
//! - [`freal`] — forward-mode single-variable active scalar
//! - [`math`] — AD-aware transcendental functions (`sin`, `exp`, `erf`, …)
//!   for every active type, in `math::ad::*` and `math::fwd::*` submodules
//! - [`tape`] — tape data structure and active-tape thread-local slot
//! - [`jacobian`] — convenience `compute_jacobian_rev` / `compute_jacobian_fwd`
//! - [`hessian`] — convenience `compute_hessian` (scalar-valued functions)
//! - [`traits`] — the [`Scalar`] trait bound
//!
//! The [`adj`], [`adjf`], [`fwd`], and [`fwdf`] submodules re-export the
//! C++ XAD-style type aliases (`ActiveType`, `PassiveType`, `TapeType`)
//! to make porting C++ XAD code mechanical.
//!
//! ## Performance
//!
//! See [`CHANGELOG.md`](https://github.com/sercanatalik/xad-rs/blob/master/CHANGELOG.md)
//! for the full write-up of the 8-stage April 2026 perf refactor. Selected
//! numbers on an Apple M4 Pro (Rust 1.92 release):
//!
//! | Workload | Time | Speedup vs. pre-refactor |
//! |---|---:|---:|
//! | Swap pricer, 30-input reverse mode | 1.86 µs | 4.36× |
//! | FX option, 6-input reverse mode | 682 ns | 2.49× |
//! | FX option, 6-input forward `Dual` | 423 ns | 2.16× |
//! | FX option, `Dual2` spot gamma | 23 ns | 1.57× |
//!
//! Reproducible with `cargo bench`. See `benches/operators.rs` and
//! `benches/pricing.rs` for the Criterion harness.
//!
//! ## License
//!
//! `xad-rs` is licensed under the **GNU Affero General Public License
//! v3.0 or later**, matching upstream XAD. See `LICENSE.md` for the full
//! text.

pub mod tape;
pub mod traits;
pub mod areal;
pub mod freal;
pub mod dual;
pub mod dual2;
pub mod math;
pub mod jacobian;
pub mod hessian;

/// String-keyed labeled layer over the existing AD modes.
///
/// Available only with the `labeled` feature enabled.
#[cfg(feature = "labeled")]
pub mod labeled;

/// Dense multi-variable second-order forward-mode AD via `Dual2Vec`.
///
/// Available only with the `dual2-vec` feature enabled.
#[cfg(feature = "dual2-vec")]
pub mod dual2vec;

pub use areal::AReal;
pub use dual::Dual;
pub use dual2::Dual2;
pub use freal::FReal;
pub use tape::{Tape, TapeStorage};
pub use traits::Scalar;

#[cfg(feature = "labeled")]
pub use labeled::{LabeledAReal, LabeledDual, LabeledDual2, LabeledFReal, LabeledTape, VarRegistry};

#[cfg(feature = "labeled-ndarray")]
pub use labeled::jacobian::{LabeledJacobian, compute_labeled_jacobian};

#[cfg(feature = "labeled-hessian")]
pub use labeled::hessian::{LabeledHessian, compute_full_hessian};

#[cfg(feature = "dual2-vec")]
pub use dual2vec::Dual2Vec;

/// Convenience type aliases matching the C++ XAD interface
pub mod adj {
    use super::*;
    pub type TapeType = Tape<f64>;
    pub type ActiveType = AReal<f64>;
    pub type PassiveType = f64;
}

pub mod adjf {
    use super::*;
    pub type TapeType = Tape<f32>;
    pub type ActiveType = AReal<f32>;
    pub type PassiveType = f32;
}

pub mod fwd {
    use super::*;
    pub type ActiveType = FReal<f64>;
    pub type PassiveType = f64;
}

pub mod fwdf {
    use super::*;
    pub type ActiveType = FReal<f32>;
    pub type PassiveType = f32;
}