1use numra_core::Scalar;
8
9#[derive(Clone, Debug)]
11pub struct OptimOptions<S: Scalar> {
12 pub max_iter: usize,
13 pub gtol: S,
14 pub ftol: S,
15 pub xtol: S,
16 pub verbose: bool,
17}
18
19impl<S: Scalar> Default for OptimOptions<S> {
20 fn default() -> Self {
21 Self {
22 max_iter: 1000,
23 gtol: S::from_f64(1e-8),
24 ftol: S::from_f64(1e-12),
25 xtol: S::from_f64(1e-12),
26 verbose: false,
27 }
28 }
29}
30
31impl<S: Scalar> OptimOptions<S> {
32 pub fn max_iter(mut self, n: usize) -> Self {
33 self.max_iter = n;
34 self
35 }
36 pub fn gtol(mut self, tol: S) -> Self {
37 self.gtol = tol;
38 self
39 }
40 pub fn ftol(mut self, tol: S) -> Self {
41 self.ftol = tol;
42 self
43 }
44 pub fn xtol(mut self, tol: S) -> Self {
45 self.xtol = tol;
46 self
47 }
48}
49
50#[derive(Clone, Debug, PartialEq, Eq)]
52pub enum OptimStatus {
53 GradientConverged,
54 FunctionConverged,
55 StepConverged,
56 MaxIterations,
57 LineSearchFailed,
58 Infeasible,
59 Unbounded,
60}
61
62#[derive(Clone, Debug)]
64pub struct IterationRecord<S: Scalar> {
65 pub iteration: usize,
66 pub objective: S,
67 pub gradient_norm: S,
68 pub step_size: S,
69 pub constraint_violation: S,
70}
71
72#[derive(Clone, Debug)]
74pub struct ParetoPoint<S: Scalar> {
75 pub x: Vec<S>,
77 pub objectives: Vec<S>,
79}
80
81#[derive(Clone, Debug)]
83pub struct ParetoResult<S: Scalar> {
84 pub points: Vec<ParetoPoint<S>>,
86}
87
88#[derive(Clone, Debug)]
90pub struct ParamSensitivity<S: Scalar> {
91 pub names: Vec<String>,
93 pub values: Vec<S>,
96 pub n_vars: usize,
98 pub n_params: usize,
100}
101
102impl<S: Scalar> ParamSensitivity<S> {
103 pub fn column(&self, j: usize) -> Vec<S> {
105 (0..self.n_vars)
106 .map(|i| self.values[i * self.n_params + j])
107 .collect()
108 }
109
110 pub fn row(&self, i: usize) -> Vec<S> {
112 self.values[i * self.n_params..(i + 1) * self.n_params].to_vec()
113 }
114
115 pub fn get(&self, i: usize, j: usize) -> S {
117 self.values[i * self.n_params + j]
118 }
119}
120
121#[derive(Clone, Debug)]
123pub struct OptimResult<S: Scalar> {
124 pub x: Vec<S>,
125 pub f: S,
126 pub grad: Vec<S>,
127 pub iterations: usize,
128 pub n_feval: usize,
129 pub n_geval: usize,
130 pub converged: bool,
131 pub message: String,
132 pub status: OptimStatus,
133 pub history: Vec<IterationRecord<S>>,
134 pub lambda_eq: Vec<S>,
135 pub lambda_ineq: Vec<S>,
136 pub active_bounds: Vec<usize>,
137 pub constraint_violation: S,
138 pub wall_time_secs: f64,
139 pub pareto: Option<ParetoResult<S>>,
140 pub sensitivity: Option<ParamSensitivity<S>>,
141}
142
143impl<S: Scalar> OptimResult<S> {
144 pub(crate) fn with_wall_time(mut self, start: std::time::Instant) -> Self {
146 self.wall_time_secs = start.elapsed().as_secs_f64();
147 self
148 }
149
150 #[allow(clippy::too_many_arguments)]
153 pub fn unconstrained(
154 x: Vec<S>,
155 f: S,
156 grad: Vec<S>,
157 iterations: usize,
158 n_feval: usize,
159 n_geval: usize,
160 converged: bool,
161 message: String,
162 status: OptimStatus,
163 ) -> Self {
164 Self {
165 x,
166 f,
167 grad,
168 iterations,
169 n_feval,
170 n_geval,
171 converged,
172 message,
173 status,
174 history: Vec::new(),
175 lambda_eq: Vec::new(),
176 lambda_ineq: Vec::new(),
177 active_bounds: Vec::new(),
178 constraint_violation: S::ZERO,
179 wall_time_secs: 0.0,
180 pareto: None,
181 sensitivity: None,
182 }
183 }
184}