Skip to main content

Module autodiff

Module autodiff 

Source
Expand description

In-crate forward-mode automatic differentiation (AD) substrate.

This module provides the numeric substrate for forward-mode automatic differentiation used by the differentiable FDA subset. It defines a Scalar trait bounding the arithmetic and transcendental operations a differentiable computation needs, a forward-mode Dual number carrying a value (primal) and a tangent (directional derivative), and a zero-cost Scalar implementation for f64.

§Design invariants

  • No external dependency. The Scalar trait is defined entirely in-crate. The num-traits crate is a transitive-only dependency of fdars-core and cannot be used without a Cargo.toml change, which would violate the no-new-dependency constraint. This module therefore never imports it.
  • Additive / non-breaking. Scalar is implemented for f64 as a zero-cost passthrough to the inherent f64 methods, so generic code instantiated at f64 is numerically identical to plain f64 code.
  • Value-only ordering for Dual. Dual’s PartialOrd compares the value (primal) field ONLY. This is the correct forward-mode branching semantics: control-flow decisions are made on the primal while tangents propagate through the selected branch. Deriving PartialOrd would use the tangent as a lexicographic tiebreaker and corrupt those semantics.

§Forward-mode in one line

Seed an input’s tangent to 1.0 (via Dual::seed), run a computation written against Scalar, and extract the (value, derivative) pair. The diff helper wraps this pattern.

use fdars_core::autodiff::{diff, Dual, Scalar};

// f(x) = x^2, f'(x) = 2x. At x = 3: f = 9, f' = 6.
let (value, deriv) = diff(|x| x * x, 3.0);
assert!((value - 9.0).abs() < 1e-12);
assert!((deriv - 6.0).abs() < 1e-12);

§Composing differentiable ops

grad flows a gradient through a composition of the crate’s Scalar-generic differentiable ops. Here one scalar objective composes a soft-DTW distance and an FPCA-score projection, then grad returns the objective value and its full gradient w.r.t. the input curve’s samples.

use fdars_core::prelude::*;
use fdars_core::regression::fdata_to_pc;

// Small trained FPCA model (mirrors regression::fdata_to_pc usage).
let m = 10usize;
let n = 12usize;
let argvals: Vec<f64> = (0..m).map(|j| 0.1 + 0.8 * j as f64 / (m - 1) as f64).collect();
let mut raw = vec![0.0f64; n * m];
for i in 0..n {
    for (j, &t) in argvals.iter().enumerate() {
        let phase = i as f64 * 0.3;
        raw[i + j * n] = (std::f64::consts::PI * t + phase).sin()
            + 0.5 * (2.0 * std::f64::consts::PI * t).cos();
    }
}
let data = FdMatrix::from_column_major(raw, n, m).unwrap();
let fpca = fdata_to_pc(&data, 2, &argvals).unwrap();

// Objective: soft-DTW(curve, reference) + sum of squared FPCA scores.
let reference: Vec<Dual> = argvals
    .iter()
    .map(|&t| Dual::constant((std::f64::consts::PI * t).sin()))
    .collect();
let curve: Vec<f64> = argvals
    .iter()
    .map(|&t| (std::f64::consts::PI * t).cos())
    .collect();

let objective = |c: &[Dual]| -> Dual {
    let sdtw = soft_dtw_distance_generic(c, &reference, 0.1);
    let scores = project_scores_generic(c, &fpca.mean, &fpca.rotation, &fpca.weights, 2);
    let mut acc = Dual::constant(0.0);
    for s in &scores {
        acc += *s * *s;
    }
    sdtw + acc
};

let (value, gradient) = grad(objective, &curve);
assert_eq!(gradient.len(), m);
assert!(value.is_finite());

Structs§

Dual
A forward-mode dual number: a value (primal) paired with a tangent (directional derivative).

Traits§

Scalar
Numeric substrate for forward-mode automatic differentiation.

Functions§

diff
Compute f(x) and f'(x) in one forward-mode pass.
directional_derivative
Compute a scalar objective’s value and its directional derivative along a supplied direction, in a SINGLE forward-mode pass.
grad
Compute a scalar objective’s value and its full gradient over an m-vector input, in m forward-mode passes (one per input).
jacobian
Compute a vector-valued map’s values and its full Jacobian over an m-vector input, in m forward-mode passes (one per input).