1#[cfg(test)]
2mod test;
3
4mod constraint;
5mod gradient_descent;
6mod line_search;
7mod newton_raphson;
8
9pub use constraint::EqualityConstraint;
10pub use gradient_descent::GradientDescent;
11pub use line_search::{LineSearch, LineSearchError};
12pub use newton_raphson::NewtonRaphson;
13
14use crate::math::{
15 Jacobian, Scalar, Solution, Style, StyledError,
16 assert::AssertionError,
17 matrix::square::SquareMatrixError,
18 sparse::{SparseError, SparseSolver},
19 styled_error,
20};
21use std::{fmt::Debug, ops::Mul};
22
23pub trait ZerothOrderRootFinding<X> {
25 fn root(
26 &self,
27 function: impl FnMut(&X) -> Result<X, String>,
28 initial_guess: X,
29 equality_constraint: EqualityConstraint,
30 ) -> Result<X, OptimizationError>;
31}
32
33pub trait FirstOrderRootFinding<F, J, X> {
35 fn root(
36 &self,
37 function: impl FnMut(&X) -> Result<F, String>,
38 jacobian: impl FnMut(&X) -> Result<J, String>,
39 initial_guess: X,
40 equality_constraint: EqualityConstraint,
41 sparse: Option<SparseSolver>,
42 ) -> Result<X, OptimizationError>;
43}
44
45pub trait FirstOrderOptimization<F, X> {
47 fn minimize(
48 &self,
49 function: impl FnMut(&X) -> Result<F, String>,
50 jacobian: impl FnMut(&X) -> Result<X, String>,
51 initial_guess: X,
52 equality_constraint: EqualityConstraint,
53 ) -> Result<X, OptimizationError>;
54}
55
56pub trait SecondOrderOptimization<F, J, H, X> {
58 fn minimize(
59 &self,
60 function: impl FnMut(&X) -> Result<F, String>,
61 jacobian: impl FnMut(&X) -> Result<J, String>,
62 hessian: impl FnMut(&X) -> Result<H, String>,
63 initial_guess: X,
64 equality_constraint: EqualityConstraint,
65 sparse: Option<SparseSolver>,
66 ) -> Result<X, OptimizationError>;
67}
68
69trait BacktrackingLineSearch<J, X>
70where
71 Self: Debug,
72{
73 fn backtracking_line_search(
74 &self,
75 mut function: impl FnMut(&X) -> Result<Scalar, String>,
76 mut jacobian: impl FnMut(&X) -> Result<J, String>,
77 argument: &X,
78 jacobian0: &J,
79 decrement: &X,
80 step_size: Scalar,
81 ) -> Result<Scalar, OptimizationError>
82 where
83 J: Jacobian,
84 for<'a> &'a J: From<&'a X>,
85 X: Solution,
86 for<'a> &'a X: Mul<Scalar, Output = X>,
87 {
88 if matches!(self.get_line_search(), LineSearch::None) {
89 Ok(step_size)
90 } else {
91 match self.get_line_search().backtrack(
92 &mut function,
93 &mut jacobian,
94 argument,
95 jacobian0,
96 decrement,
97 step_size,
98 ) {
99 Ok(step_size) => Ok(step_size),
100 Err(error) => Err(OptimizationError::Upstream(
101 format!("{error}"),
102 format!("{self:?}"),
103 )),
104 }
105 }
106 }
107 fn get_line_search(&self) -> &LineSearch;
108}
109
110pub enum OptimizationError {
112 Intermediate(String),
113 MaximumStepsReached(usize, String),
114 NotMinimum(String, String),
115 Upstream(String, String),
116 SingularMatrix,
117}
118
119impl From<String> for OptimizationError {
120 fn from(error: String) -> Self {
121 Self::Intermediate(error)
122 }
123}
124
125impl StyledError for OptimizationError {
126 fn message(&self, style: &Style) -> String {
127 let (h, c) = (style.headline, style.frame);
128 match self {
129 Self::Intermediate(message) => message.to_string(),
130 Self::MaximumStepsReached(steps, solver) => format!(
131 "{h}Maximum number of steps ({steps}) reached.{c}\n\
132 In solver: {solver}."
133 ),
134 Self::NotMinimum(solution, solver) => format!(
135 "{h}The obtained solution is not a minimum.{c}\n\
136 For solution: {solution}.\n\
137 In solver: {solver}."
138 ),
139 Self::SingularMatrix => format!("{h}Matrix is singular."),
140 Self::Upstream(error, solver) => format!(
141 "{error}{c}\n\
142 In solver: {solver}."
143 ),
144 }
145 }
146}
147
148styled_error!(OptimizationError);
149
150impl From<OptimizationError> for String {
151 fn from(error: OptimizationError) -> Self {
152 error.to_string()
153 }
154}
155
156impl From<OptimizationError> for AssertionError {
157 fn from(error: OptimizationError) -> Self {
158 Self {
159 message: error.to_string(),
160 }
161 }
162}
163
164impl From<SquareMatrixError> for OptimizationError {
165 fn from(_error: SquareMatrixError) -> Self {
166 Self::SingularMatrix
167 }
168}
169
170impl From<SparseError> for OptimizationError {
171 fn from(_error: SparseError) -> Self {
172 Self::SingularMatrix
173 }
174}