use numra_core::Scalar;
#[derive(Clone, Debug)]
pub struct OptimOptions<S: Scalar> {
pub max_iter: usize,
pub gtol: S,
pub ftol: S,
pub xtol: S,
pub verbose: bool,
}
impl<S: Scalar> Default for OptimOptions<S> {
fn default() -> Self {
Self {
max_iter: 1000,
gtol: S::from_f64(1e-8),
ftol: S::from_f64(1e-12),
xtol: S::from_f64(1e-12),
verbose: false,
}
}
}
impl<S: Scalar> OptimOptions<S> {
pub fn max_iter(mut self, n: usize) -> Self {
self.max_iter = n;
self
}
pub fn gtol(mut self, tol: S) -> Self {
self.gtol = tol;
self
}
pub fn ftol(mut self, tol: S) -> Self {
self.ftol = tol;
self
}
pub fn xtol(mut self, tol: S) -> Self {
self.xtol = tol;
self
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum OptimStatus {
GradientConverged,
FunctionConverged,
StepConverged,
MaxIterations,
LineSearchFailed,
Infeasible,
Unbounded,
}
#[derive(Clone, Debug)]
pub struct IterationRecord<S: Scalar> {
pub iteration: usize,
pub objective: S,
pub gradient_norm: S,
pub step_size: S,
pub constraint_violation: S,
}
#[derive(Clone, Debug)]
pub struct ParetoPoint<S: Scalar> {
pub x: Vec<S>,
pub objectives: Vec<S>,
}
#[derive(Clone, Debug)]
pub struct ParetoResult<S: Scalar> {
pub points: Vec<ParetoPoint<S>>,
}
#[derive(Clone, Debug)]
pub struct ParamSensitivity<S: Scalar> {
pub names: Vec<String>,
pub values: Vec<S>,
pub n_vars: usize,
pub n_params: usize,
}
impl<S: Scalar> ParamSensitivity<S> {
pub fn column(&self, j: usize) -> Vec<S> {
(0..self.n_vars)
.map(|i| self.values[i * self.n_params + j])
.collect()
}
pub fn row(&self, i: usize) -> Vec<S> {
self.values[i * self.n_params..(i + 1) * self.n_params].to_vec()
}
pub fn get(&self, i: usize, j: usize) -> S {
self.values[i * self.n_params + j]
}
}
#[derive(Clone, Debug)]
pub struct OptimResult<S: Scalar> {
pub x: Vec<S>,
pub f: S,
pub grad: Vec<S>,
pub iterations: usize,
pub n_feval: usize,
pub n_geval: usize,
pub converged: bool,
pub message: String,
pub status: OptimStatus,
pub history: Vec<IterationRecord<S>>,
pub lambda_eq: Vec<S>,
pub lambda_ineq: Vec<S>,
pub active_bounds: Vec<usize>,
pub constraint_violation: S,
pub wall_time_secs: f64,
pub pareto: Option<ParetoResult<S>>,
pub sensitivity: Option<ParamSensitivity<S>>,
}
impl<S: Scalar> OptimResult<S> {
pub(crate) fn with_wall_time(mut self, start: std::time::Instant) -> Self {
self.wall_time_secs = start.elapsed().as_secs_f64();
self
}
#[allow(clippy::too_many_arguments)]
pub fn unconstrained(
x: Vec<S>,
f: S,
grad: Vec<S>,
iterations: usize,
n_feval: usize,
n_geval: usize,
converged: bool,
message: String,
status: OptimStatus,
) -> Self {
Self {
x,
f,
grad,
iterations,
n_feval,
n_geval,
converged,
message,
status,
history: Vec::new(),
lambda_eq: Vec::new(),
lambda_ineq: Vec::new(),
active_bounds: Vec::new(),
constraint_violation: S::ZERO,
wall_time_secs: 0.0,
pareto: None,
sensitivity: None,
}
}
}