pub struct FiniteDiff<P> { /* private fields */ }Expand description
Wraps a problem to synthesize its derivatives by finite differences.
Construct with FiniteDiff::new (central gradient and Hessian, forward
Jacobian; see the module docs) and adjust with the builder
methods. The wrapper delegates CostFunction/Residual /
all constraint traits to the inner problem and implements Gradient,
Jacobian, crate::ConstraintJacobian, and Hessian via finite differences.
§Backends
Gradient and HessianProduct are backend-generic (any V: Clone + VectorLen + VectorIndex; no matrix type is involved). Jacobian and
Hessian additionally require V: DenseMatrixFromFn, so they are
available for Vec<f64>/DenseMatrix, nalgebra DVector<f64>/DMatrix,
ndarray Array1<f64>/Array2, and faer Col<f64>/Mat.
crate::ConstraintJacobian uses the parameter backend’s dense matrix,
independently of the linear block matrices. Use BoundedFiniteDiff for f32 first derivatives.
§Solver stopping tolerances
The synthesized gradient has truncation and rounding error, so it can be
nonzero at an exact minimum. Choose the solver’s gradient stopping tolerance
to account for that error in the problem’s units. A threshold below the
derivative error can cause extra evaluations or a line-search failure even
after reaching an accurate solution. Central differences often reduce
truncation error, but do not guarantee a particular stopping accuracy.
function_precision controls probe step sizes;
it is not a bound on gradient error. Objective scaling, curvature, parameter
magnitudes, and evaluation noise also affect that error. See
Lbfgs for a forward-difference stopping example.
§Examples
Run a gradient solver against a problem that only implements
CostFunction: wrapping it in FiniteDiff synthesizes the
Gradient by central differences.
use basin::{
FirstOrderState, CostFunction, Executor, FiniteDiff, GradientDescent};
struct Sphere;
impl basin::ProblemConstraints for Sphere {
type Constraints = basin::NoConstraints;
fn constraint_set(&self) -> basin::NoConstraints { basin::NoConstraints::EMPTY }
}
impl CostFunction for Sphere {
type Param = Vec<f64>;
type Output = f64;
type Error = std::convert::Infallible;
fn cost(&self, x: &Vec<f64>) -> Result<f64, std::convert::Infallible> {
Ok(x.iter().map(|xi| xi * xi).sum())
}
}
let result = Executor::new(
FiniteDiff::new(Sphere),
GradientDescent::new(0.1).with_absolute_gradient_tolerance(1e-8),
FirstOrderState::new(vec![1.0, 1.0]),
)
.max_iter(1_000)
.run()
.unwrap();
assert!(result.cost() < 1e-10);Implementations§
Source§impl<P> FiniteDiff<P>
impl<P> FiniteDiff<P>
Sourcepub fn with_bounds<V: VectorLen + VectorIndex>(
self,
lower: V,
upper: V,
) -> BoundedFiniteDiff<P>
pub fn with_bounds<V: VectorLen + VectorIndex>( self, lower: V, upper: V, ) -> BoundedFiniteDiff<P>
Transfer first-derivative settings into a BoundedFiniteDiff.
The resulting adapter supports gradients and Jacobians only. Hessian
settings have no effect on its first derivatives.
Source§impl<P> FiniteDiff<P>
impl<P> FiniteDiff<P>
Sourcepub fn new(problem: P) -> Self
pub fn new(problem: P) -> Self
Wrap problem with default settings: central-difference gradient
and Hessian, forward-difference (MINPACK fdjac2) Jacobian,
function_precision = f64::EPSILON, adaptive step sizes.
Sourcepub fn gradient_method(self, method: Method) -> Self
pub fn gradient_method(self, method: Method) -> Self
Set the stencil used for the gradient (default Method::Central).
Sourcepub fn jacobian_method(self, method: Method) -> Self
pub fn jacobian_method(self, method: Method) -> Self
Set the stencil used for the Jacobian (default Method::Forward,
the MINPACK fdjac2 parity choice).
Sourcepub fn hessian_method(self, method: Method) -> Self
pub fn hessian_method(self, method: Method) -> Self
Set the stencil used for the Hessian (default Method::Central).
Sourcepub fn function_precision(self, epsfcn: f64) -> Self
pub fn function_precision(self, epsfcn: f64) -> Self
Set the assumed relative accuracy of the wrapped function (MINPACK’s
epsfcn). Larger values widen the step, which helps when the
function is noisy. Floored at f64::EPSILON. Default f64::EPSILON.
Sourcepub fn with_step(self, h: f64) -> Self
pub fn with_step(self, h: f64) -> Self
Override the adaptive step rule with a fixed absolute step h used
for every coordinate. Escape hatch; most callers should leave the
adaptive |xⱼ|-scaled rule in place.
Sourcepub fn into_inner(self) -> P
pub fn into_inner(self) -> P
Unwrap and return the inner problem.
Trait Implementations§
Source§impl<P: Clone> Clone for FiniteDiff<P>
impl<P: Clone> Clone for FiniteDiff<P>
Source§impl<P, V> ConstraintJacobian for FiniteDiff<P>where
P: CostFunction<Param = V, Output = f64> + MaybeSync,
P::Constraints: NonlinearSet<P, V, f64>,
V: Clone + VectorLen + VectorIndex<f64> + DenseMatrixFromFn<f64> + MaybeSync,
P::Error: MaybeSend,
impl<P, V> ConstraintJacobian for FiniteDiff<P>where
P: CostFunction<Param = V, Output = f64> + MaybeSync,
P::Constraints: NonlinearSet<P, V, f64>,
V: Clone + VectorLen + VectorIndex<f64> + DenseMatrixFromFn<f64> + MaybeSync,
P::Error: MaybeSend,
Source§type Jacobian = <V as DenseMatrixFromFn>::Matrix
type Jacobian = <V as DenseMatrixFromFn>::Matrix
Source§type Error = <P as CostFunction>::Error
type Error = <P as CostFunction>::Error
Source§fn constraint_jacobian(
&self,
x: &V,
) -> Result<Self::Jacobian, ConstraintSolveError<P::Error>>
fn constraint_jacobian( &self, x: &V, ) -> Result<Self::Jacobian, ConstraintSolveError<P::Error>>
impl<P: Copy> Copy for FiniteDiff<P>
Source§impl<P: CostFunction> CostFunction for FiniteDiff<P>
impl<P: CostFunction> CostFunction for FiniteDiff<P>
Source§type Param = <P as CostFunction>::Param
type Param = <P as CostFunction>::Param
Source§type Output = <P as CostFunction>::Output
type Output = <P as CostFunction>::Output
f64 (see CONTRIBUTING.md’s
provisional choices).Source§type Error = <P as CostFunction>::Error
type Error = <P as CostFunction>::Error
std::convert::Infallible when the cost cannot fail: its
niche optimization keeps Result<f64, Infallible> the same
layout as bare f64 on the happy path.Source§impl<P: Debug> Debug for FiniteDiff<P>
impl<P: Debug> Debug for FiniteDiff<P>
Source§impl<P, V> Gradient for FiniteDiff<P>where
P: CostFunction<Param = V, Output = f64> + MaybeSync,
V: Clone + VectorLen + VectorIndex + MaybeSync,
<P as CostFunction>::Error: MaybeSend,
impl<P, V> Gradient for FiniteDiff<P>where
P: CostFunction<Param = V, Output = f64> + MaybeSync,
V: Clone + VectorLen + VectorIndex + MaybeSync,
<P as CostFunction>::Error: MaybeSend,
Source§type Gradient = V
type Gradient = V
CostFunction::Param.Source§fn cost_and_gradient(
&self,
param: &Self::Param,
) -> Result<(Self::Output, Self::Gradient), Self::Error>
fn cost_and_gradient( &self, param: &Self::Param, ) -> Result<(Self::Output, Self::Gradient), Self::Error>
param in one call. The default
body delegates to CostFunction::cost and
Gradient::gradient; override when shared intermediate work
can be amortized across the two. Read moreSource§impl<P, V> Hessian for FiniteDiff<P>where
P: CostFunction<Param = V, Output = f64> + MaybeSync,
V: Clone + VectorLen + VectorIndex + DenseMatrixFromFn + MaybeSync,
<P as CostFunction>::Error: MaybeSend,
impl<P, V> Hessian for FiniteDiff<P>where
P: CostFunction<Param = V, Output = f64> + MaybeSync,
V: Clone + VectorLen + VectorIndex + DenseMatrixFromFn + MaybeSync,
<P as CostFunction>::Error: MaybeSend,
Source§type Hessian = <V as DenseMatrixFromFn>::Matrix
type Hessian = <V as DenseMatrixFromFn>::Matrix
n × n and symmetric.Source§fn hessian(&self, param: &V) -> Result<Self::Hessian, P::Error>
fn hessian(&self, param: &V) -> Result<Self::Hessian, P::Error>
param.Source§fn cost_and_gradient_and_hessian(
&self,
param: &Self::Param,
) -> Result<(<Self as CostFunction>::Output, <Self as Gradient>::Gradient, Self::Hessian), <Self as CostFunction>::Error>
fn cost_and_gradient_and_hessian( &self, param: &Self::Param, ) -> Result<(<Self as CostFunction>::Output, <Self as Gradient>::Gradient, Self::Hessian), <Self as CostFunction>::Error>
param in one call.
The default body delegates to Gradient::cost_and_gradient
followed by Hessian::hessian; override when all three share
intermediate work. Read moreSource§impl<P, V> HessianProduct for FiniteDiff<P>where
P: CostFunction<Param = V, Output = f64> + MaybeSync,
V: Clone + VectorLen + VectorIndex + MaybeSync,
<P as CostFunction>::Error: MaybeSend,
impl<P, V> HessianProduct for FiniteDiff<P>where
P: CostFunction<Param = V, Output = f64> + MaybeSync,
V: Clone + VectorLen + VectorIndex + MaybeSync,
<P as CostFunction>::Error: MaybeSend,
Source§fn hessian_product(&self, param: &V, v: &V) -> Result<V, P::Error>
fn hessian_product(&self, param: &V, v: &V) -> Result<V, P::Error>
Hessian-vector product by differencing this wrapper’s own
finite-difference gradients along v (stencil set by
hessian_method). Note this stacks two
truncation errors (FD of FD): it is the no-analytic-anything
fallback. With an analytic Gradient, skip the wrapper and call
forward_difference_hessian_product /
central_difference_hessian_product directly (2 gradient
evaluations per product). Cost here is 2 synthesized gradients:
~4n cost evaluations with the central/central default.
Source§impl<P, V> Jacobian for FiniteDiff<P>where
P: Residual<Param = V, Output = V> + MaybeSync,
V: Clone + VectorLen + VectorIndex + DenseMatrixFromFn + MaybeSync + MaybeSend,
<P as Residual>::Error: MaybeSend,
impl<P, V> Jacobian for FiniteDiff<P>where
P: Residual<Param = V, Output = V> + MaybeSync,
V: Clone + VectorLen + VectorIndex + DenseMatrixFromFn + MaybeSync + MaybeSend,
<P as Residual>::Error: MaybeSend,
Source§type Jacobian = <V as DenseMatrixFromFn>::Matrix
type Jacobian = <V as DenseMatrixFromFn>::Matrix
m × n.Source§fn jacobian(&self, param: &V) -> Result<Self::Jacobian, P::Error>
fn jacobian(&self, param: &V) -> Result<Self::Jacobian, P::Error>
param.Source§fn residual_and_jacobian(
&self,
param: &Self::Param,
) -> Result<(<Self as Residual>::Output, Self::Jacobian), <Self as Residual>::Error>
fn residual_and_jacobian( &self, param: &Self::Param, ) -> Result<(<Self as Residual>::Output, Self::Jacobian), <Self as Residual>::Error>
param in one call. The
default body delegates to Residual::residual and
Jacobian::jacobian; override when shared intermediate work
can be amortized across the two, common in NLLS where r(x)
reuses forward-mode AD state that J(x) continues from. Read moreSource§impl<P> NonlinearEqualityValues for FiniteDiff<P>where
P: NonlinearEqualityValues,
impl<P> NonlinearEqualityValues for FiniteDiff<P>where
P: NonlinearEqualityValues,
Source§type Param = <P as NonlinearEqualityValues>::Param
type Param = <P as NonlinearEqualityValues>::Param
Source§type Values = <P as NonlinearEqualityValues>::Values
type Values = <P as NonlinearEqualityValues>::Values
Source§type Error = <P as NonlinearEqualityValues>::Error
type Error = <P as NonlinearEqualityValues>::Error
Source§fn nonlinear_equalities(
&self,
x: &Self::Param,
) -> Result<Self::Values, ConstraintSolveError<Self::Error>>
fn nonlinear_equalities( &self, x: &Self::Param, ) -> Result<Self::Values, ConstraintSolveError<Self::Error>>
h(x) = 0.Source§impl<P> NonlinearInequalityValues for FiniteDiff<P>where
P: NonlinearInequalityValues,
impl<P> NonlinearInequalityValues for FiniteDiff<P>where
P: NonlinearInequalityValues,
Source§type Param = <P as NonlinearInequalityValues>::Param
type Param = <P as NonlinearInequalityValues>::Param
Source§type Values = <P as NonlinearInequalityValues>::Values
type Values = <P as NonlinearInequalityValues>::Values
Source§type Error = <P as NonlinearInequalityValues>::Error
type Error = <P as NonlinearInequalityValues>::Error
Source§fn nonlinear_inequalities(
&self,
x: &Self::Param,
) -> Result<Self::Values, ConstraintSolveError<Self::Error>>
fn nonlinear_inequalities( &self, x: &Self::Param, ) -> Result<Self::Values, ConstraintSolveError<Self::Error>>
c(x) <= 0 convention.Source§impl<P> ProblemConstraints for FiniteDiff<P>where
P: ProblemConstraints,
impl<P> ProblemConstraints for FiniteDiff<P>where
P: ProblemConstraints,
Source§type Constraints = <P as ProblemConstraints>::Constraints
type Constraints = <P as ProblemConstraints>::Constraints
Source§fn constraint_set(
&self,
) -> <Self::Constraints as ConstraintDescription>::Borrowed<'_>
fn constraint_set( &self, ) -> <Self::Constraints as ConstraintDescription>::Borrowed<'_>
Source§impl<P: Residual> Residual for FiniteDiff<P>
impl<P: Residual> Residual for FiniteDiff<P>
Source§type Param = <P as Residual>::Param
type Param = <P as Residual>::Param
CostFunction::Param).Source§type Output = <P as Residual>::Output
type Output = <P as Residual>::Output
m,
independent of param.len() = n.Auto Trait Implementations§
impl<P> Freeze for FiniteDiff<P>where
P: Freeze,
impl<P> RefUnwindSafe for FiniteDiff<P>where
P: RefUnwindSafe,
impl<P> Send for FiniteDiff<P>where
P: Send,
impl<P> Sync for FiniteDiff<P>where
P: Sync,
impl<P> Unpin for FiniteDiff<P>where
P: Unpin,
impl<P> UnsafeUnpin for FiniteDiff<P>where
P: UnsafeUnpin,
impl<P> UnwindSafe for FiniteDiff<P>where
P: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T, U> Imply<T> for U
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.