sim_lib_numbers_optimize/
model.rs1use super::*;
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7pub enum DerivativeSource {
8 Analytic,
9 Automatic,
10 FiniteDifference,
11}
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum StepPolicy {
15 BrentGolden,
16 LevenbergMarquardt,
17 TrustRegionReflective,
18 ProjectedBfgs,
19}
20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
21pub enum Termination {
22 Converged,
23 BoundaryConverged,
24 Flat,
25 WorkLimit,
26 NonFinite,
27 NoProgress,
28 InvalidPlan,
29}
30
31#[derive(Clone, Copy, Debug, PartialEq)]
32pub struct Tolerances {
33 pub argument: f64,
34 pub objective: f64,
35 pub gradient: f64,
36}
37impl Default for Tolerances {
38 fn default() -> Self {
39 Self {
40 argument: 1e-9,
41 objective: 1e-12,
42 gradient: 1e-8,
43 }
44 }
45}
46
47#[derive(Clone, Copy, Debug, Eq, PartialEq)]
48pub struct Limits {
49 pub evaluations: usize,
50 pub iterations: usize,
51 pub memory_bytes: usize,
52}
53impl Default for Limits {
54 fn default() -> Self {
55 Self {
56 evaluations: 10_000,
57 iterations: 500,
58 memory_bytes: 64 * 1024 * 1024,
59 }
60 }
61}
62
63#[derive(Clone, Debug, PartialEq)]
64pub struct Bounds {
65 pub lower: Vec<f64>,
66 pub upper: Vec<f64>,
67}
68impl Bounds {
69 pub fn new(lower: Vec<f64>, upper: Vec<f64>) -> Result<Self, Error> {
70 if lower.len() != upper.len()
71 || lower
72 .iter()
73 .zip(&upper)
74 .any(|(l, u)| !l.is_finite() || !u.is_finite() || l > u)
75 {
76 return Err(Error::InvalidPlan(
77 "bounds must be finite, ordered, and equal length",
78 ));
79 }
80 Ok(Self { lower, upper })
81 }
82 pub(crate) fn project(&self, x: &mut [f64]) {
83 for ((x, l), u) in x.iter_mut().zip(&self.lower).zip(&self.upper) {
84 *x = x.clamp(*l, *u);
85 }
86 }
87}
88
89#[derive(Clone, Debug, PartialEq)]
90pub struct ObjectivePlan {
91 pub bounds: Bounds,
92 pub scale: Vec<f64>,
93 pub derivative: DerivativeSource,
94 pub policy: StepPolicy,
95 pub tolerances: Tolerances,
96 pub limits: Limits,
97 pub initial_radius: f64,
98}
99#[derive(Clone, Debug, PartialEq)]
100pub struct LeastSquaresPlan {
101 pub bounds: Option<Bounds>,
102 pub variable_scale: Vec<f64>,
103 pub residual_scale: Vec<f64>,
104 pub derivative: DerivativeSource,
105 pub policy: StepPolicy,
106 pub tolerances: Tolerances,
107 pub limits: Limits,
108 pub initial_damping: f64,
109}
110
111#[derive(Clone, Debug, PartialEq)]
112pub struct Work {
113 pub evaluations: usize,
114 pub iterations: usize,
115 pub memory_bytes: usize,
116}
117#[derive(Clone, Debug, PartialEq)]
118pub struct OptimizeResult {
119 pub point: Vec<f64>,
120 pub value: f64,
121 pub gradient_norm: f64,
122 pub active: Vec<usize>,
123 pub termination: Termination,
124 pub work: Work,
125}
126#[derive(Clone, Debug, PartialEq)]
127pub struct ScalarResult {
128 pub minimizer: f64,
129 pub value: f64,
130 pub final_bracket: (f64, f64),
131 pub termination: Termination,
132 pub work: Work,
133}
134#[derive(Clone, Debug, PartialEq)]
135pub enum Covariance {
136 Available(Vec<Vec<f64>>),
137 Unavailable(CovarianceUnavailable),
138}
139#[derive(Clone, Copy, Debug, Eq, PartialEq)]
140pub enum CovarianceUnavailable {
141 RankDeficient,
142 InsufficientDegreesOfFreedom,
143 StatisticalAssumptionsNotDeclared,
144}
145#[derive(Clone, Debug, PartialEq)]
146pub struct LeastSquaresResult {
147 pub point: Vec<f64>,
148 pub residuals: Vec<f64>,
149 pub residual_norm: f64,
150 pub rank: usize,
151 pub active: Vec<usize>,
152 pub covariance: Covariance,
153 pub termination: Termination,
154 pub work: Work,
155}
156
157#[derive(Clone, Debug, Eq, PartialEq)]
158pub enum Error {
159 InvalidPlan(&'static str),
160 Dimension(&'static str),
161}
162impl fmt::Display for Error {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 match self {
165 Self::InvalidPlan(s) | Self::Dimension(s) => f.write_str(s),
166 }
167 }
168}
169impl std::error::Error for Error {}
170
171pub(crate) fn finite(v: &[f64]) -> bool {
172 v.iter().all(|x| x.is_finite())
173}
174pub(crate) fn norm(v: &[f64]) -> f64 {
175 v.iter().map(|x| x * x).sum::<f64>().sqrt()
176}
177pub(crate) fn validate_scale(scale: &[f64], n: usize) -> Result<(), Error> {
178 if scale.len() != n || scale.iter().any(|x| !x.is_finite() || *x <= 0.0) {
179 Err(Error::InvalidPlan(
180 "scale must contain one finite positive value per variable",
181 ))
182 } else {
183 Ok(())
184 }
185}