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::{
15 defeat_message,
16 math::{
17 Jacobian, Scalar, Solution, TestError,
18 matrix::square::{Banded, SquareMatrixError},
19 },
20};
21use std::{
22 fmt::{self, Debug, Display, Formatter},
23 ops::Mul,
24};
25
26pub trait ZerothOrderRootFinding<X> {
28 fn root(
29 &self,
30 function: impl FnMut(&X) -> Result<X, String>,
31 initial_guess: X,
32 equality_constraint: EqualityConstraint,
33 ) -> Result<X, OptimizationError>;
34}
35
36pub trait FirstOrderRootFinding<F, J, X> {
38 fn root(
39 &self,
40 function: impl FnMut(&X) -> Result<F, String>,
41 jacobian: impl FnMut(&X) -> Result<J, String>,
42 initial_guess: X,
43 equality_constraint: EqualityConstraint,
44 ) -> Result<X, OptimizationError>;
45}
46
47pub trait FirstOrderOptimization<F, X> {
49 fn minimize(
50 &self,
51 function: impl FnMut(&X) -> Result<F, String>,
52 jacobian: impl FnMut(&X) -> Result<X, String>,
53 initial_guess: X,
54 equality_constraint: EqualityConstraint,
55 ) -> Result<X, OptimizationError>;
56}
57
58pub trait SecondOrderOptimization<F, J, H, X> {
60 fn minimize(
61 &self,
62 function: impl FnMut(&X) -> Result<F, String>,
63 jacobian: impl FnMut(&X) -> Result<J, String>,
64 hessian: impl FnMut(&X) -> Result<H, String>,
65 initial_guess: X,
66 equality_constraint: EqualityConstraint,
67 banded: Option<Banded>,
68 ) -> Result<X, OptimizationError>;
69}
70
71trait BacktrackingLineSearch<J, X>
72where
73 Self: Debug,
74{
75 fn backtracking_line_search(
76 &self,
77 mut function: impl FnMut(&X) -> Result<Scalar, String>,
78 mut jacobian: impl FnMut(&X) -> Result<J, String>,
79 argument: &X,
80 jacobian0: &J,
81 decrement: &X,
82 step_size: Scalar,
83 ) -> Result<Scalar, OptimizationError>
84 where
85 J: Jacobian,
86 for<'a> &'a J: From<&'a X>,
87 X: Solution,
88 for<'a> &'a X: Mul<Scalar, Output = X>,
89 {
90 if matches!(self.get_line_search(), LineSearch::None) {
91 Ok(step_size)
92 } else {
93 match self.get_line_search().backtrack(
94 &mut function,
95 &mut jacobian,
96 argument,
97 jacobian0,
98 decrement,
99 step_size,
100 ) {
101 Ok(step_size) => Ok(step_size),
102 Err(error) => Err(OptimizationError::Upstream(
103 format!("{error}"),
104 format!("{self:?}"),
105 )),
106 }
107 }
108 }
109 fn get_line_search(&self) -> &LineSearch;
110}
111
112pub enum OptimizationError {
114 Intermediate(String),
115 MaximumStepsReached(usize, String),
116 NotMinimum(String, String),
117 Upstream(String, String),
118 SingularMatrix,
119}
120
121impl From<String> for OptimizationError {
122 fn from(error: String) -> Self {
123 Self::Intermediate(error)
124 }
125}
126
127impl Debug for OptimizationError {
128 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
129 let error = match self {
130 Self::Intermediate(message) => message.to_string(),
131 Self::MaximumStepsReached(steps, solver) => {
132 format!(
133 "\x1b[1;91mMaximum number of steps ({steps}) reached.\x1b[0;91m\n\
134 In solver: {solver}."
135 )
136 }
137 Self::NotMinimum(solution, solver) => {
138 format!(
139 "\x1b[1;91mThe obtained solution is not a minimum.\x1b[0;91m\n\
140 For solution: {solution}.\n\
141 In solver: {solver}."
142 )
143 }
144 Self::SingularMatrix => "\x1b[1;91mMatrix is singular.".to_string(),
145 Self::Upstream(error, solver) => {
146 format!(
147 "{error}\x1b[0;91m\n\
148 In solver: {solver}."
149 )
150 }
151 };
152 write!(f, "\n{error}\n\x1b[0;2;31m{}\x1b[0m\n", defeat_message())
153 }
154}
155
156impl Display for OptimizationError {
157 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
158 let error = match self {
159 Self::Intermediate(message) => message.to_string(),
160 Self::MaximumStepsReached(steps, solver) => {
161 format!(
162 "\x1b[1;91mMaximum number of steps ({steps}) reached.\x1b[0;91m\n\
163 In solver: {solver}."
164 )
165 }
166 Self::NotMinimum(solution, solver) => {
167 format!(
168 "\x1b[1;91mThe obtained solution is not a minimum.\x1b[0;91m\n\
169 For solution: {solution}.\n\
170 In solver: {solver}."
171 )
172 }
173 Self::SingularMatrix => "\x1b[1;91mMatrix is singular.".to_string(),
174 Self::Upstream(error, solver) => {
175 format!(
176 "{error}\x1b[0;91m\n\
177 In solver: {solver}."
178 )
179 }
180 };
181 write!(f, "{error}\x1b[0m")
182 }
183}
184
185impl From<OptimizationError> for String {
186 fn from(error: OptimizationError) -> Self {
187 error.to_string()
188 }
189}
190
191impl From<OptimizationError> for TestError {
192 fn from(error: OptimizationError) -> Self {
193 Self {
194 message: error.to_string(),
195 }
196 }
197}
198
199impl From<SquareMatrixError> for OptimizationError {
200 fn from(_error: SquareMatrixError) -> Self {
201 Self::SingularMatrix
202 }
203}