linalg design
This page explains how the linalg drivers obtain gradients and Jacobians
from scalar dual numbers, what each one costs, and why the package is shaped
as a thin bridge to linear-algebra.
Design goal
Differentiate functions and written with
the immutable vectors of linear-algebra, reusing Dual[T] unchanged, with
a small API whose results follow the usual matrix conventions.
Mathematical background
Directional derivatives from one pass
Let be differentiable at and let . Seed every input as . By the dual design each output becomes
so one evaluation on dual numbers yields the Jacobian-vector product . This is the multivariate chain rule: for , .
Columns by unit seeds
Seeding the unit vector returns the -th column of the Jacobian, . The drivers make such passes:
For the single row is , and gradient
returns it as a vector.
Cost compared with reverse mode
Let be the cost of evaluating on . One dual pass costs at most a small constant times (see the dual design), so
Reverse mode computes vector-Jacobian products , one row per pass, and obtains a gradient for a constant multiple of independent of , at the price of storing the computation.11 This is the “cheap gradient principle”; see A. Griewank and A. Walther, Evaluating Derivatives, 2nd ed., SIAM, 2008, section 4.6. Forward mode is therefore the right tool when is small or , and the slower one for gradients of functions of many variables.
Design decisions
Scalar tangents and passes
Problem. A gradient needs directional derivatives.
Options. A dual type with a vector of tangents (one pass,
work per operation); passes with the scalar Dual[T].
Choice. passes. The total arithmetic is the same order, , and the scalar type needs no allocation per operation and no new number type. A vector-tangent type remains future work.
Output-by-input convention
The matrix is with entry ,
the convention of the chain rule , so results
compose with ordinary matrix multiplication. Column is pass ; the
implementation collects the output vectors and then builds the matrix
with Matrix::make(m, n, (i, j) => columns[j][i].tangent()).
A separate pass for values and for
value_and_gradient and value_and_jacobian evaluate f once more with
all tangents zero instead of reading the value from one of the seeded
passes. By the projection homomorphism
every pass has the same value, so this costs one evaluation but keeps the
drivers independent of each other and correct for . jacobian uses
the same zero-tangent call to learn before allocating the matrix.
No shape validation yet
Problem. A function may read past its input or change its output length.
Options. Return Result with a shape error; abort; document a
precondition.
Choice. A documented precondition. linear-algebra does not yet share a
shape-error type with this repository, and a private error type would be
replaced later. The source carries a TODO for checked variants.
Only ring operations on Dual[T]
The drivers need nothing but One + Zero for seeding. Vector and matrix
operations inside f use the Add, Mul and Neg instances of
Dual[T], so any linear-algebra operation that only needs ring structure
works on dual vectors. Algorithms that need Field, Inverse or an order
on the scalar (pivoting, for example) cannot be instantiated at Dual[T];
this is intentional, see the dual design.
Correctness and invariants
gradient(f, x)[j]andjacobian(f, x)[i][j]for programs satisfying the preconditions, with the rounding bounds of the dual design for each entry.value_and_gradient(f, x)andvalue_and_jacobian(f, x); the values are exactly whatfcomputes onT.- Evaluations:
gradient,value_and_gradient,jacobian,value_and_jacobian. - Memory:
jacobianholds the output vectors of length before building the matrix.
Alternatives rejected
- Reverse mode for gradients. Asymptotically cheaper for large , but it needs a recorded computation and is not implemented.
- A public Jacobian-vector product. One pass with
Dual::new(x[j], v[j])already gives (see the linalg tutorial); a dedicated function would add little. - Mutable matrices. The drivers return
immutvalues, which match the value semantics of the rest of the repository.
Boundaries
- Forward mode only: no reverse mode, and no Hessian or higher-order driver.
- No shape checking and no checked variants.
- Dense
immutvectors and matrices only; no sparse or mutable containers. - No algorithms that need a field or an order on
Dual[T].
Footnotes
-
This is the “cheap gradient principle”; see A. Griewank and A. Walther, Evaluating Derivatives, 2nd ed., SIAM, 2008, section 4.6. ↩