xad-rs 0.8.2

Exact automatic differentiation for Rust — forward-mode, reverse-mode, first- and second-order, with named variable support and a unified `Real` trait for mode-agnostic numerical code
Documentation
//! # `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](https://github.com/sercanatalik/xad-rs/blob/main/docs/README.md).
//!
//! # 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);
//! # xad_rs::Tape::<f64>::deactivate_all();
//! ```
//!
//! # 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 |

pub mod passive;
pub mod real;
pub mod real_stats;
pub mod tape;
pub mod math;
pub mod registry;
pub mod forward_tape;
pub mod forward;
pub mod reverse;
pub mod ops;
pub mod prelude;

// ---- re-exports: positional types ----
pub use forward::{Jet1, Jet1Vec, Jet2, Jet2Vec};
pub use reverse::AReal;
pub use tape::{Tape, TapeGuard, TapeStorage};
pub use passive::Passive;
pub use real::Real;
pub use real_stats::RealStats;

// ---- re-exports: named types ----
pub use forward::{NamedJet1, NamedJet1Vec, NamedJet2};
pub use reverse::{NamedAReal, NamedTape};
pub use registry::VarRegistry;
pub use forward_tape::{NamedForwardTape, NamedForwardScope, Jet1Handle, Jet1VecHandle, Jet2Handle};

// ---- re-exports: composite operations ----
pub use ops::{compute_jacobian_rev, compute_jacobian_fwd, compute_hessian};
pub use ops::{NamedJacobian, compute_named_jacobian};
pub use ops::{NamedHessian, compute_full_hessian};