use crate::error::OptimizeResult;
use crate::result::OptimizeResults;
use scirs2_core::ndarray::{Array1, ArrayBase, Data, Ix1};
use std::fmt;
pub mod augmented_lagrangian;
pub mod cobyla;
pub mod interior_point;
pub mod slsqp;
pub mod sqp;
pub mod trust_constr;
pub use augmented_lagrangian::{
minimize_augmented_lagrangian, minimize_equality_constrained, minimize_inequality_constrained,
AugmentedLagrangianOptions, AugmentedLagrangianResult,
};
pub use cobyla::minimize_cobyla;
pub use interior_point::{
minimize_interior_point, minimize_interior_point_constrained, InteriorPointOptions,
InteriorPointResult,
};
pub use slsqp::minimize_slsqp;
pub use sqp::{minimize_sqp, SqpOptions, SqpResult};
pub use trust_constr::{
minimize_trust_constr, minimize_trust_constr_with_derivatives, GradientFn, HessianFn,
HessianUpdate,
};
#[cfg(test)]
mod tests;
pub type ConstraintFn = fn(&[f64]) -> f64;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Method {
SLSQP,
TrustConstr,
COBYLA,
InteriorPoint,
AugmentedLagrangian,
SQP,
}
impl fmt::Display for Method {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Method::SLSQP => write!(f, "SLSQP"),
Method::TrustConstr => write!(f, "trust-constr"),
Method::COBYLA => write!(f, "COBYLA"),
Method::InteriorPoint => write!(f, "interior-point"),
Method::AugmentedLagrangian => write!(f, "augmented-lagrangian"),
Method::SQP => write!(f, "SQP"),
}
}
}
#[derive(Debug, Clone)]
pub struct Options {
pub maxiter: Option<usize>,
pub ftol: Option<f64>,
pub gtol: Option<f64>,
pub ctol: Option<f64>,
pub eps: Option<f64>,
pub disp: bool,
pub return_all: bool,
}
impl Default for Options {
fn default() -> Self {
Options {
maxiter: None,
ftol: Some(1e-8),
gtol: Some(1e-8),
ctol: Some(1e-8),
eps: Some(1e-8),
disp: false,
return_all: false,
}
}
}
pub struct Constraint<F> {
pub fun: F,
pub kind: ConstraintKind,
pub lb: Option<f64>,
pub ub: Option<f64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConstraintKind {
Equality,
Inequality,
}
impl Constraint<fn(&[f64]) -> f64> {
pub const EQUALITY: ConstraintKind = ConstraintKind::Equality;
pub const INEQUALITY: ConstraintKind = ConstraintKind::Inequality;
pub fn new(fun: fn(&[f64]) -> f64, kind: ConstraintKind) -> Self {
Constraint {
fun,
kind,
lb: None,
ub: None,
}
}
pub fn new_bounds(lb: Option<f64>, ub: Option<f64>) -> Self {
Constraint {
fun: |_| 0.0, kind: ConstraintKind::Inequality,
lb,
ub,
}
}
}
impl<F> Constraint<F> {
pub fn is_bounds(&self) -> bool {
self.lb.is_some() || self.ub.is_some()
}
}
#[allow(dead_code)]
pub fn minimize_constrained<F, S>(
func: F,
x0: &ArrayBase<S, Ix1>,
constraints: &[Constraint<ConstraintFn>],
method: Method,
options: Option<Options>,
) -> OptimizeResult<OptimizeResults<f64>>
where
F: Fn(&[f64]) -> f64 + Clone,
S: Data<Elem = f64>,
{
let options = options.unwrap_or_default();
match method {
Method::SLSQP => minimize_slsqp(func, x0, constraints, &options),
Method::TrustConstr => minimize_trust_constr(func, x0, constraints, &options),
Method::COBYLA => minimize_cobyla(func, x0, constraints, &options),
Method::InteriorPoint => {
let x0_arr = Array1::from_vec(x0.to_vec());
let ip_options = InteriorPointOptions {
max_iter: options.maxiter.unwrap_or(100),
tol: options.gtol.unwrap_or(1e-8),
feas_tol: options.ctol.unwrap_or(1e-8),
..Default::default()
};
match minimize_interior_point_constrained(func, x0_arr, constraints, Some(ip_options)) {
Ok(result) => {
let opt_result = OptimizeResults::<f64> {
x: result.x,
fun: result.fun,
nit: result.nit,
nfev: result.nfev,
success: result.success,
message: result.message,
jac: None,
hess: None,
constr: None,
njev: 0, nhev: 0, maxcv: 0, status: if result.success { 0 } else { 1 },
};
Ok(opt_result)
}
Err(e) => Err(e),
}
}
Method::AugmentedLagrangian => {
Err(crate::error::OptimizeError::NotImplementedError(
"Augmented Lagrangian method integration with minimize_constrained not yet implemented"
.to_string(),
))
}
Method::SQP => sqp::minimize_sqp_compat(func, x0, constraints, &options),
}
}