Expand description
Finite-difference derivative synthesis.
FiniteDiff wraps a problem that only exposes function values
(CostFunction and/or Residual) and adds the derivative traits
the solvers want: Gradient (for first-order solvers), Jacobian
(for least-squares solvers), and Hessian (for second-order solvers).
Each derivative is approximated by finite differences of the wrapped
problem, so a values-only problem flows straight into the existing
solvers via basin’s type-system dispatch.
The wrapper forwards BoxConstraints when the inner problem carries
box bounds — adding derivatives must not silently un-constrain a problem
(tenet 4 in CONTRIBUTING.md: an adapter that adds a capability preserves
the rest).
§Step sizes (paper-anchored)
- Gradient and Hessian use Numerical-Recipes-style adaptive
steps
hⱼ = scale · max(|xⱼ|, 1), withscale = eps_f^{1/3}(central gradient),eps_f^{1/2}(forward gradient), oreps_f^{1/4}(Hessian), whereeps_f = function_precision.max(f64::EPSILON)is the assumed relative accuracy of the function. The step is re-rounded soxⱼ + hⱼis exactly representable. - Forward Jacobian reproduces MINPACK
fdjac2exactly:eps = sqrt(eps_f),hⱼ = eps · |xⱼ|, andhⱼ = epswhenxⱼ = 0. This is the function-values-only counterpart of MINPACKlmder, i.e. thelmdifJacobian — chosen as the default so least-squares fits viaFiniteDiffmatchlmdif’s convergence lineage.
§Caveats
- Evaluation counting. One counted
Gradient::gradient,Jacobian::jacobian, orHessian::hessiancall performs many internal cost/residual evaluations (2ncentral /n+1forward gradient,n+1/2nJacobian columns,~2n²Hessian). Eval counting lives on the solverStateand is incremented by the solver per derivative call, so a problem wrapper has no way to record the inner calls —result.cost_evals()will not reflect them. Users who need true cost-evaluation budgets should account for theO(n)/O(n²)multiplier themselves. - Domain & hard-abort. Each probe
?-propagates the inner function’sError. If the innercost/residualreturnsErr, the derivative call returns the sameErr— no swallowing, no partial result. A point where the inner function soft-rejects (Ok(f64::INFINITY)) poisons the derivative with±∞rather than aborting; keeping probes inside the strict domain (e.g. viaFiniteDiff::function_precisionorFiniteDiff::with_step) is the caller’s responsibility.
§Parallelism (parallel feature)
The probes of one derivative are independent, so with the parallel
feature they fan across a rayon thread pool: the n gradient/Jacobian
columns, and the n(n+1)/2 upper-triangular Hessian entries, are evaluated
concurrently (the shared f(x) / r(x) base evaluation runs once up
front). The result is bit-identical to the sequential path — each
output slot is written independently and collected in order, with no
floating-point reduction — so enabling the feature never changes the
numbers, only the wall-clock.
This is a win when the wrapped cost / residual is expensive; for a
cheap function at small n the thread-pool overhead can exceed the saving,
so it is opt-in. Under the feature, FiniteDiff’s Gradient /
Jacobian / Hessian impls additionally require the wrapped problem
(and its param vector / error type) to be Sync / Send; without the
feature there is no such bound and the evaluation is sequential.
Structs§
- Finite
Diff - Wraps a problem to synthesize its derivatives by finite differences.
Enums§
- Method
- Which finite-difference stencil to use for a given derivative.
Functions§
- central_
difference_ gradient - Central-difference gradient
∇f(x)ⱼ ≈ (f(x+hⱼeⱼ) − f(x−hⱼeⱼ)) / 2hⱼ. - central_
difference_ hessian - Central-difference Hessian (Numerical Recipes second differences).
- central_
difference_ jacobian - Central-difference Jacobian, column
j ≈ (r(x+hⱼeⱼ) − r(x−hⱼeⱼ)) / 2hⱼ. - forward_
difference_ gradient - Forward-difference gradient
∇f(x)ⱼ ≈ (f(x+hⱼeⱼ) − f(x)) / hⱼ. - forward_
difference_ hessian - Forward-difference Hessian (one-sided second differences).
- forward_
difference_ jacobian - Forward-difference Jacobian, column
j ≈ (r(x+hⱼeⱼ) − r(x)) / hⱼ.