scirs2_optimize/constrained/
mod.rs

1//! Constrained optimization algorithms
2//!
3//! This module provides methods for constrained optimization of scalar
4//! functions of one or more variables.
5//!
6//! ## Example
7//!
8//! ```
9//! use ndarray::{array, Array1};
10//! use scirs2_optimize::constrained::{minimize_constrained, Method, Constraint};
11//!
12//! // Define a simple function to minimize
13//! fn objective(x: &[f64]) -> f64 {
14//!     (x[0] - 1.0).powi(2) + (x[1] - 2.5).powi(2)
15//! }
16//!
17//! // Define a constraint: x[0] + x[1] <= 3
18//! fn constraint(x: &[f64]) -> f64 {
19//!     3.0 - x[0] - x[1]  // Should be >= 0
20//! }
21//!
22//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
23//! // Minimize the function starting at [0.0, 0.0]
24//! let initial_point = array![0.0, 0.0];
25//! let constraints = vec![Constraint::new(constraint, Constraint::INEQUALITY)];
26//!
27//! let result = minimize_constrained(
28//!     objective,
29//!     &initial_point,
30//!     &constraints,
31//!     Method::SLSQP,
32//!     None
33//! )?;
34//!
35//! // The constrained minimum should be at [0.5, 2.5]
36//! # Ok(())
37//! # }
38//! ```
39
40use crate::error::OptimizeResult;
41use crate::result::OptimizeResults;
42use ndarray::{ArrayBase, Data, Ix1};
43use std::fmt;
44
45// Re-export optimization methods
46pub mod cobyla;
47pub mod slsqp;
48pub mod trust_constr;
49
50// Re-export main functions
51pub use cobyla::minimize_cobyla;
52pub use slsqp::minimize_slsqp;
53pub use trust_constr::minimize_trust_constr;
54
55#[cfg(test)]
56mod tests;
57
58/// Type alias for constraint functions that take a slice of f64 and return f64
59pub type ConstraintFn = fn(&[f64]) -> f64;
60
61/// Optimization methods for constrained minimization.
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum Method {
64    /// Sequential Least SQuares Programming
65    SLSQP,
66
67    /// Trust-region constrained algorithm
68    TrustConstr,
69
70    /// Linear programming using the simplex algorithm
71    COBYLA,
72}
73
74impl fmt::Display for Method {
75    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76        match self {
77            Method::SLSQP => write!(f, "SLSQP"),
78            Method::TrustConstr => write!(f, "trust-constr"),
79            Method::COBYLA => write!(f, "COBYLA"),
80        }
81    }
82}
83
84/// Options for the constrained optimizer.
85#[derive(Debug, Clone)]
86pub struct Options {
87    /// Maximum number of iterations to perform
88    pub maxiter: Option<usize>,
89
90    /// Precision goal for the value in the stopping criterion
91    pub ftol: Option<f64>,
92
93    /// Precision goal for the gradient in the stopping criterion (relative)
94    pub gtol: Option<f64>,
95
96    /// Precision goal for constraint violation
97    pub ctol: Option<f64>,
98
99    /// Step size used for numerical approximation of the jacobian
100    pub eps: Option<f64>,
101
102    /// Whether to print convergence messages
103    pub disp: bool,
104
105    /// Return the optimization result after each iteration
106    pub return_all: bool,
107}
108
109impl Default for Options {
110    fn default() -> Self {
111        Options {
112            maxiter: None,
113            ftol: Some(1e-8),
114            gtol: Some(1e-8),
115            ctol: Some(1e-8),
116            eps: Some(1e-8),
117            disp: false,
118            return_all: false,
119        }
120    }
121}
122
123/// Constraint type for constrained optimization
124pub struct Constraint<F> {
125    /// The constraint function
126    pub fun: F,
127
128    /// The type of constraint (equality or inequality)
129    pub kind: ConstraintKind,
130
131    /// Lower bound for a box constraint
132    pub lb: Option<f64>,
133
134    /// Upper bound for a box constraint
135    pub ub: Option<f64>,
136}
137
138/// The kind of constraint
139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
140pub enum ConstraintKind {
141    /// Equality constraint: fun(x) = 0
142    Equality,
143
144    /// Inequality constraint: fun(x) >= 0
145    Inequality,
146}
147
148impl Constraint<fn(&[f64]) -> f64> {
149    /// Constant for equality constraint
150    pub const EQUALITY: ConstraintKind = ConstraintKind::Equality;
151
152    /// Constant for inequality constraint
153    pub const INEQUALITY: ConstraintKind = ConstraintKind::Inequality;
154
155    /// Create a new constraint
156    pub fn new(fun: fn(&[f64]) -> f64, kind: ConstraintKind) -> Self {
157        Constraint {
158            fun,
159            kind,
160            lb: None,
161            ub: None,
162        }
163    }
164
165    /// Create a new box constraint
166    pub fn new_bounds(lb: Option<f64>, ub: Option<f64>) -> Self {
167        Constraint {
168            fun: |_| 0.0, // Dummy function for box constraints
169            kind: ConstraintKind::Inequality,
170            lb,
171            ub,
172        }
173    }
174}
175
176impl<F> Constraint<F> {
177    /// Check if this is a box constraint
178    pub fn is_bounds(&self) -> bool {
179        self.lb.is_some() || self.ub.is_some()
180    }
181}
182
183/// Minimizes a scalar function of one or more variables with constraints.
184///
185/// # Arguments
186///
187/// * `func` - A function that takes a slice of values and returns a scalar
188/// * `x0` - The initial guess
189/// * `constraints` - Vector of constraints
190/// * `method` - The optimization method to use
191/// * `options` - Options for the optimizer
192///
193/// # Returns
194///
195/// * `OptimizeResults` containing the optimization results
196///
197/// # Example
198///
199/// ```
200/// use ndarray::array;
201/// use scirs2_optimize::constrained::{minimize_constrained, Method, Constraint};
202///
203/// // Function to minimize
204/// fn objective(x: &[f64]) -> f64 {
205///     (x[0] - 1.0).powi(2) + (x[1] - 2.5).powi(2)
206/// }
207///
208/// // Constraint: x[0] + x[1] <= 3
209/// fn constraint(x: &[f64]) -> f64 {
210///     3.0 - x[0] - x[1]  // Should be >= 0
211/// }
212///
213/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
214/// let initial_point = array![0.0, 0.0];
215/// let constraints = vec![Constraint::new(constraint, Constraint::INEQUALITY)];
216///
217/// let result = minimize_constrained(
218///     objective,
219///     &initial_point,
220///     &constraints,
221///     Method::SLSQP,
222///     None
223/// )?;
224/// # Ok(())
225/// # }
226/// ```
227pub fn minimize_constrained<F, S>(
228    func: F,
229    x0: &ArrayBase<S, Ix1>,
230    constraints: &[Constraint<ConstraintFn>],
231    method: Method,
232    options: Option<Options>,
233) -> OptimizeResult<OptimizeResults<f64>>
234where
235    F: Fn(&[f64]) -> f64,
236    S: Data<Elem = f64>,
237{
238    let options = options.unwrap_or_default();
239
240    // Implementation of various methods will go here
241    match method {
242        Method::SLSQP => minimize_slsqp(func, x0, constraints, &options),
243        Method::TrustConstr => minimize_trust_constr(func, x0, constraints, &options),
244        Method::COBYLA => minimize_cobyla(func, x0, constraints, &options),
245    }
246}