pub use crate::numerical::Nonlinear_systems::engine::{
DiagnosticsOptions, EngineLogLevel, IterationRecord, IterationState, LinearSolverKind,
MemoryDiagnostics, MethodWorkspace, NewtonMethod, NonlinearMethod, RuntimeDiagnostics,
SolveAttemptStatistics, SolveOptions, SolveResult, SolveStatistics, SolverEngine,
StatisticsAvailability, StepOutcome, scaled_norm, scaling_vector, solve_linear_system,
};
pub use crate::numerical::Nonlinear_systems::error::{SolveError, TerminationReason};
pub use crate::numerical::Nonlinear_systems::problem::{
Bounds, JacobianProvider, NonlinearProblem,
};
pub use crate::numerical::Nonlinear_systems::LM_backtracking::{
BacktrackingLevenbergMarquardtMethod, BacktrackingLevenbergMarquardtState,
};
pub use crate::numerical::Nonlinear_systems::LM_vanilla::{
LMMinpackState, LevenbergMarquardtMethod, LevenbergMarquardtMinpack, LevenbergMarquardtState,
};
pub use crate::numerical::Nonlinear_systems::LM_Nielsen::{
NielsenLevenbergMarquardtMethod, NielsenLevenbergMarquardtMethodAdvanced,
NielsenLevenbergMarquardtState, NielsenLevenbergMarquardtStateAdvanced,
};
pub use crate::numerical::Nonlinear_systems::LM_utils::{
ConvergenceCriteria, ConvergenceCriteriaSolver, ConvergenceInfo, ReductionRatio,
ReductionRatioSolver, ScalingMethod, TrustRegionScaling, compute_scaled_gradient_norm2,
scaled_norm_common, test_convergence_gsl,
};
pub use crate::numerical::Nonlinear_systems::NR_damped::{
DampedNewtonMethod, DampedNewtonMethodAdvanced, bound_step,
};
pub use crate::numerical::Nonlinear_systems::trust_region::{
DoglegError, DoglegSolver, DoglegState, Powell_dogleg_method, PowellDoglegMethod,
PowellDoglegState, TrustRegionMethod, TrustRegionState,
};
pub use crate::numerical::Nonlinear_systems::trust_region_LM::{
TrustRegionLMMethod, TrustRegionLMState, TrustRegionResult, solve_trust_region_subproblem,
};
pub use crate::numerical::Nonlinear_systems::least_squares::{
ClosureLeastSquaresProblem, LeastSquaresError, LeastSquaresStage,
LevenbergMarquardt as LeastSquaresLevenbergMarquardt, LeastSquaresTelemetryMode,
MinimizationReport, PreparedSymbolicLeastSquaresProblem, SymbolicLeastSquaresSolver,
};
pub use crate::numerical::Nonlinear_systems::scalar_root::{
approx_equal, bisection, secant, ClosureFunction, FunctionWithDerivative, NonlinearFunction,
RootFindingConfig, RootFindingError, RootFindingMethod, RootFindingResult, ScalarRootFinder,
SymbolicFunction,
};
pub use crate::numerical::Nonlinear_systems::solver::NonlinearSolver;
pub use crate::numerical::Nonlinear_systems::symbolic::{
BoundSymbolicNonlinearProblem, LambdifyExecutionPolicy, NonlinearParameterSchema,
NonlinearParameterValues, PreparationExecutionMode, PreparationInputKind, PreparationStage,
PreparationStageTiming, PreparationTelemetry, PreparationTelemetryMode,
PreparedSymbolicNonlinearAotProblem, PreparedSymbolicNonlinearProblem, SymbolicArtifactAction,
SymbolicArtifactPolicy, SymbolicBackendConfig, SymbolicBackendKind, SymbolicDenseAotOptions,
SymbolicLambdifyFrontend, SymbolicNonlinearProblem, SymbolicPreparationFailure,
SymbolicPreparationReport, SymbolicProblemOptions,
};
pub use crate::numerical::Nonlinear_systems::symbolic_aot::{
generated_aot_crate_from_symbolic_nonlinear_problem, materialize_symbolic_nonlinear_aot_build,
prepared_problem_from_symbolic_nonlinear_problem,
};
pub use crate::numerical::Nonlinear_systems::symbolic_backend::{
SelectedSymbolicNonlinearBackend, SelectedSymbolicNonlinearBackendKind,
SymbolicBackendSelectionPolicy, select_symbolic_nonlinear_backend,
};
pub use crate::numerical::Nonlinear_systems::symbolic_generated::{
DenseGeneratedBackendMode, PreparedGeneratedSymbolicProblem, SymbolicAotBuildPolicy,
SymbolicAotBuildRetryPolicy, SymbolicGeneratedBackendConfig,
};
#[derive(Debug, Clone)]
pub enum NonlinearSolverMethod {
Newton(NewtonMethod),
DampedNewton(DampedNewtonMethod),
DampedNewtonAdvanced(DampedNewtonMethodAdvanced),
LevenbergMarquardt(LevenbergMarquardtMethod),
BacktrackingLevenbergMarquardt(BacktrackingLevenbergMarquardtMethod),
LevenbergMarquardtMinpack(LevenbergMarquardtMinpack),
NielsenLevenbergMarquardt(NielsenLevenbergMarquardtMethod),
NielsenLevenbergMarquardtAdvanced(NielsenLevenbergMarquardtMethodAdvanced),
TrustRegion(TrustRegionMethod),
PowellDogleg(PowellDoglegMethod),
TrustRegionLM(TrustRegionLMMethod),
}
impl Default for NonlinearSolverMethod {
fn default() -> Self {
Self::Newton(NewtonMethod)
}
}
#[derive(Debug, Clone)]
pub enum NonlinearSolverMethodState {
Newton(()),
DampedNewton(()),
DampedNewtonAdvanced(()),
LevenbergMarquardt(LevenbergMarquardtState),
BacktrackingLevenbergMarquardt(BacktrackingLevenbergMarquardtState),
LevenbergMarquardtMinpack(LMMinpackState),
NielsenLevenbergMarquardt(NielsenLevenbergMarquardtState),
NielsenLevenbergMarquardtAdvanced(NielsenLevenbergMarquardtStateAdvanced),
TrustRegion(TrustRegionState),
PowellDogleg(PowellDoglegState),
TrustRegionLM(TrustRegionLMState),
}
impl NonlinearSolverMethod {
pub fn name(&self) -> &'static str {
match self {
Self::Newton(_) => "newton",
Self::DampedNewton(_) => "damped_newton",
Self::DampedNewtonAdvanced(_) => "damped_newton_advanced",
Self::LevenbergMarquardt(_) => "levenberg_marquardt",
Self::BacktrackingLevenbergMarquardt(_) => "backtracking_levenberg_marquardt",
Self::LevenbergMarquardtMinpack(_) => "levenberg_marquardt_minpack",
Self::NielsenLevenbergMarquardt(_) => "nielsen_levenberg_marquardt",
Self::NielsenLevenbergMarquardtAdvanced(_) => "nielsen_levenberg_marquardt_advanced",
Self::TrustRegion(_) => "trust_region",
Self::PowellDogleg(_) => "powell_dogleg",
Self::TrustRegionLM(_) => "trust_region_lm",
}
}
pub fn engine(self, options: SolveOptions) -> SolverEngine<Self> {
SolverEngine::new(self, options)
}
pub fn solve<P>(
self,
problem: &P,
x0: nalgebra::DVector<f64>,
options: SolveOptions,
) -> Result<SolveResult, SolveError>
where
P: JacobianProvider,
{
self.engine(options).solve(problem, x0)
}
}
impl NonlinearMethod for NonlinearSolverMethod {
type MethodState = NonlinearSolverMethodState;
fn init<P: JacobianProvider>(
&self,
problem: &P,
x0: &nalgebra::DVector<f64>,
options: &SolveOptions,
residual: &nalgebra::DVector<f64>,
jacobian: &nalgebra::DMatrix<f64>,
) -> Result<Self::MethodState, SolveError> {
match self {
Self::Newton(method) => method
.init(problem, x0, options, residual, jacobian)
.map(NonlinearSolverMethodState::Newton),
Self::DampedNewton(method) => method
.init(problem, x0, options, residual, jacobian)
.map(NonlinearSolverMethodState::DampedNewton),
Self::DampedNewtonAdvanced(method) => method
.init(problem, x0, options, residual, jacobian)
.map(NonlinearSolverMethodState::DampedNewtonAdvanced),
Self::LevenbergMarquardt(method) => method
.init(problem, x0, options, residual, jacobian)
.map(NonlinearSolverMethodState::LevenbergMarquardt),
Self::BacktrackingLevenbergMarquardt(method) => method
.init(problem, x0, options, residual, jacobian)
.map(NonlinearSolverMethodState::BacktrackingLevenbergMarquardt),
Self::LevenbergMarquardtMinpack(method) => method
.init(problem, x0, options, residual, jacobian)
.map(NonlinearSolverMethodState::LevenbergMarquardtMinpack),
Self::NielsenLevenbergMarquardt(method) => method
.init(problem, x0, options, residual, jacobian)
.map(NonlinearSolverMethodState::NielsenLevenbergMarquardt),
Self::NielsenLevenbergMarquardtAdvanced(method) => method
.init(problem, x0, options, residual, jacobian)
.map(NonlinearSolverMethodState::NielsenLevenbergMarquardtAdvanced),
Self::TrustRegion(method) => method
.init(problem, x0, options, residual, jacobian)
.map(NonlinearSolverMethodState::TrustRegion),
Self::PowellDogleg(method) => method
.init(problem, x0, options, residual, jacobian)
.map(NonlinearSolverMethodState::PowellDogleg),
Self::TrustRegionLM(method) => method
.init(problem, x0, options, residual, jacobian)
.map(NonlinearSolverMethodState::TrustRegionLM),
}
}
fn step<P: JacobianProvider>(
&self,
problem: &P,
state: &IterationState,
method_state: &mut Self::MethodState,
options: &SolveOptions,
runtime: &mut RuntimeDiagnostics,
) -> Result<StepOutcome, SolveError> {
match (self, method_state) {
(Self::Newton(method), NonlinearSolverMethodState::Newton(inner)) => {
method.step(problem, state, inner, options, runtime)
}
(Self::DampedNewton(method), NonlinearSolverMethodState::DampedNewton(inner)) => {
method.step(problem, state, inner, options, runtime)
}
(
Self::DampedNewtonAdvanced(method),
NonlinearSolverMethodState::DampedNewtonAdvanced(inner),
) => method.step(problem, state, inner, options, runtime),
(
Self::LevenbergMarquardt(method),
NonlinearSolverMethodState::LevenbergMarquardt(inner),
) => method.step(problem, state, inner, options, runtime),
(
Self::BacktrackingLevenbergMarquardt(method),
NonlinearSolverMethodState::BacktrackingLevenbergMarquardt(inner),
) => method.step(problem, state, inner, options, runtime),
(
Self::LevenbergMarquardtMinpack(method),
NonlinearSolverMethodState::LevenbergMarquardtMinpack(inner),
) => method.step(problem, state, inner, options, runtime),
(
Self::NielsenLevenbergMarquardt(method),
NonlinearSolverMethodState::NielsenLevenbergMarquardt(inner),
) => method.step(problem, state, inner, options, runtime),
(
Self::NielsenLevenbergMarquardtAdvanced(method),
NonlinearSolverMethodState::NielsenLevenbergMarquardtAdvanced(inner),
) => method.step(problem, state, inner, options, runtime),
(Self::TrustRegion(method), NonlinearSolverMethodState::TrustRegion(inner)) => {
method.step(problem, state, inner, options, runtime)
}
(Self::PowellDogleg(method), NonlinearSolverMethodState::PowellDogleg(inner)) => {
method.step(problem, state, inner, options, runtime)
}
(Self::TrustRegionLM(method), NonlinearSolverMethodState::TrustRegionLM(inner)) => {
method.step(problem, state, inner, options, runtime)
}
_ => Err(SolveError::InvalidConfig(
"solver method state does not match the selected enum variant".to_string(),
)),
}
}
fn step_with_workspace<P: JacobianProvider>(
&self,
problem: &P,
state: &IterationState,
method_state: &mut Self::MethodState,
options: &SolveOptions,
runtime: &mut RuntimeDiagnostics,
workspace: Option<&mut MethodWorkspace>,
) -> Result<StepOutcome, SolveError> {
match (self, method_state) {
(Self::Newton(method), NonlinearSolverMethodState::Newton(inner)) => {
method.step_with_workspace(problem, state, inner, options, runtime, workspace)
}
(Self::DampedNewton(method), NonlinearSolverMethodState::DampedNewton(inner)) => {
method.step_with_workspace(problem, state, inner, options, runtime, workspace)
}
(
Self::DampedNewtonAdvanced(method),
NonlinearSolverMethodState::DampedNewtonAdvanced(inner),
) => method.step_with_workspace(problem, state, inner, options, runtime, workspace),
(
Self::LevenbergMarquardt(method),
NonlinearSolverMethodState::LevenbergMarquardt(inner),
) => method.step_with_workspace(problem, state, inner, options, runtime, workspace),
(
Self::BacktrackingLevenbergMarquardt(method),
NonlinearSolverMethodState::BacktrackingLevenbergMarquardt(inner),
) => method.step_with_workspace(problem, state, inner, options, runtime, workspace),
(
Self::LevenbergMarquardtMinpack(method),
NonlinearSolverMethodState::LevenbergMarquardtMinpack(inner),
) => method.step_with_workspace(problem, state, inner, options, runtime, workspace),
(
Self::NielsenLevenbergMarquardt(method),
NonlinearSolverMethodState::NielsenLevenbergMarquardt(inner),
) => method.step_with_workspace(problem, state, inner, options, runtime, workspace),
(
Self::NielsenLevenbergMarquardtAdvanced(method),
NonlinearSolverMethodState::NielsenLevenbergMarquardtAdvanced(inner),
) => method.step_with_workspace(problem, state, inner, options, runtime, workspace),
(Self::TrustRegion(method), NonlinearSolverMethodState::TrustRegion(inner)) => {
method.step_with_workspace(problem, state, inner, options, runtime, workspace)
}
(Self::PowellDogleg(method), NonlinearSolverMethodState::PowellDogleg(inner)) => {
method.step_with_workspace(problem, state, inner, options, runtime, workspace)
}
(Self::TrustRegionLM(method), NonlinearSolverMethodState::TrustRegionLM(inner)) => {
method.step_with_workspace(problem, state, inner, options, runtime, workspace)
}
_ => Err(SolveError::InvalidConfig(
"solver method state does not match the selected enum variant".to_string(),
)),
}
}
fn supports_step_workspace(&self) -> bool {
matches!(
self,
Self::DampedNewton(_)
| Self::DampedNewtonAdvanced(_)
| Self::LevenbergMarquardt(_)
| Self::BacktrackingLevenbergMarquardt(_)
| Self::NielsenLevenbergMarquardt(_)
| Self::NielsenLevenbergMarquardtAdvanced(_)
)
}
}
#[cfg(test)]
mod facade_tests {
use super::*;
use approx::assert_relative_eq;
use nalgebra::{DMatrix, DVector};
struct PlainProblem;
impl NonlinearProblem for PlainProblem {
fn dimension(&self) -> usize {
2
}
fn residual(&self, x: &DVector<f64>) -> Result<DVector<f64>, SolveError> {
Ok(DVector::from_vec(vec![
x[0] * x[0] + x[1] * x[1] - 10.0,
x[0] - x[1] - 4.0,
]))
}
}
impl JacobianProvider for PlainProblem {
fn jacobian(&self, x: &DVector<f64>) -> Result<DMatrix<f64>, SolveError> {
Ok(DMatrix::from_row_slice(
2,
2,
&[2.0 * x[0], 2.0 * x[1], 1.0, -1.0],
))
}
}
#[test]
fn enum_facade_reports_name() {
let method = NonlinearSolverMethod::TrustRegionLM(TrustRegionLMMethod::default());
assert_eq!(method.name(), "trust_region_lm");
let method = NonlinearSolverMethod::BacktrackingLevenbergMarquardt(
BacktrackingLevenbergMarquardtMethod::default(),
);
assert_eq!(method.name(), "backtracking_levenberg_marquardt");
}
#[test]
fn workspace_policy_keeps_unbenchmarked_trust_region_methods_owned() {
assert!(
NonlinearSolverMethod::LevenbergMarquardt(LevenbergMarquardtMethod::default())
.supports_step_workspace()
);
assert!(
NonlinearSolverMethod::NielsenLevenbergMarquardt(
NielsenLevenbergMarquardtMethod::default()
)
.supports_step_workspace()
);
assert!(
NonlinearSolverMethod::BacktrackingLevenbergMarquardt(
BacktrackingLevenbergMarquardtMethod::default()
)
.supports_step_workspace()
);
assert!(
!NonlinearSolverMethod::TrustRegion(TrustRegionMethod::default())
.supports_step_workspace()
);
assert!(
!NonlinearSolverMethod::PowellDogleg(PowellDoglegMethod::default())
.supports_step_workspace()
);
assert!(
!NonlinearSolverMethod::TrustRegionLM(TrustRegionLMMethod::default())
.supports_step_workspace()
);
}
#[test]
fn enum_facade_solves_plain_problem() {
let method =
NonlinearSolverMethod::LevenbergMarquardtMinpack(LevenbergMarquardtMinpack::default());
let result = method
.solve(
&PlainProblem,
DVector::from_vec(vec![1.0, 1.0]),
SolveOptions {
tolerance: 1e-8,
max_iterations: 100,
..SolveOptions::default()
},
)
.expect("solve");
assert_eq!(result.termination, TerminationReason::Converged);
assert_relative_eq!(result.x[0], 3.0, epsilon = 1e-6);
assert_relative_eq!(result.x[1], -1.0, epsilon = 1e-6);
}
#[test]
fn enum_facade_solves_plain_problem_with_backtracking_lm() {
let method = NonlinearSolverMethod::BacktrackingLevenbergMarquardt(
BacktrackingLevenbergMarquardtMethod::default(),
);
let result = method
.solve(
&PlainProblem,
DVector::from_vec(vec![1.0, 1.0]),
SolveOptions {
tolerance: 1e-8,
max_iterations: 100,
..SolveOptions::default()
},
)
.expect("backtracking LM solve");
assert_eq!(result.termination, TerminationReason::Converged);
assert_relative_eq!(result.x[0], 3.0, epsilon = 1e-6);
assert_relative_eq!(result.x[1], -1.0, epsilon = 1e-6);
}
}