optimization_engine/core/fbs/
fbs_optimizer.rs1use crate::{
4 constraints,
5 core::{
6 fbs::fbs_engine::FBSEngine, fbs::FBSCache, AlgorithmEngine, ExitStatus, Optimizer, Problem,
7 SolverStatus,
8 },
9 matrix_operations, FunctionCallResult, SolverError,
10};
11use num::Float;
12use std::time;
13
14const MAX_ITER: usize = 100_usize;
15
16pub struct FBSOptimizer<'a, GradientType, ConstraintType, CostType, T = f64>
27where
28 T: Float,
29 GradientType: Fn(&[T], &mut [T]) -> FunctionCallResult,
30 CostType: Fn(&[T], &mut T) -> FunctionCallResult,
31 ConstraintType: constraints::Constraint<T>,
32{
33 fbs_engine: FBSEngine<'a, GradientType, ConstraintType, CostType, T>,
34 max_iter: usize,
35 max_duration: Option<time::Duration>,
36}
37
38impl<'a, GradientType, ConstraintType, CostType, T>
39 FBSOptimizer<'a, GradientType, ConstraintType, CostType, T>
40where
41 T: Float,
42 GradientType: Fn(&[T], &mut [T]) -> FunctionCallResult + 'a,
43 CostType: Fn(&[T], &mut T) -> FunctionCallResult + 'a,
44 ConstraintType: constraints::Constraint<T> + 'a,
45{
46 #[must_use]
53 pub fn new(
54 problem: Problem<'a, GradientType, ConstraintType, CostType, T>,
55 cache: &'a mut FBSCache<T>,
56 ) -> Self {
57 FBSOptimizer {
58 fbs_engine: FBSEngine::new(problem, cache),
59 max_iter: MAX_ITER,
60 max_duration: None,
61 }
62 }
63
64 #[must_use]
70 pub fn with_tolerance(
71 self,
72 tolerance: T,
73 ) -> FBSOptimizer<'a, GradientType, ConstraintType, CostType, T> {
74 assert!(tolerance > T::zero());
75
76 self.fbs_engine.cache.tolerance = tolerance;
77 self
78 }
79
80 #[must_use]
82 pub fn with_max_iter(
83 mut self,
84 max_iter: usize,
85 ) -> FBSOptimizer<'a, GradientType, ConstraintType, CostType, T> {
86 self.max_iter = max_iter;
87 self
88 }
89
90 #[must_use]
92 pub fn with_max_duration(
93 mut self,
94 max_duration: time::Duration,
95 ) -> FBSOptimizer<'a, GradientType, ConstraintType, CostType, T> {
96 self.max_duration = Some(max_duration);
97 self
98 }
99
100 pub fn solve(&mut self, u: &mut [T]) -> Result<SolverStatus<T>, SolverError> {
102 let now = web_time::Instant::now();
103
104 self.fbs_engine.init(u)?;
105
106 let mut num_iter: usize = 0;
107 let mut step_flag = self.fbs_engine.step(u)?;
108
109 if let Some(dur) = self.max_duration {
110 while step_flag && num_iter < self.max_iter && now.elapsed() <= dur {
111 num_iter += 1;
112 step_flag = self.fbs_engine.step(u)?
113 }
114 } else {
115 while step_flag && num_iter < self.max_iter {
116 num_iter += 1;
117 step_flag = self.fbs_engine.step(u)?
118 }
119 }
120
121 let mut cost_value = T::zero();
122 (self.fbs_engine.problem.cost)(u, &mut cost_value)?;
123
124 if !matrix_operations::is_finite(u) || !cost_value.is_finite() {
125 return Err(SolverError::NotFiniteComputation(
126 "final FBS iterate or cost is non-finite",
127 ));
128 }
129
130 Ok(SolverStatus::new(
131 if num_iter < self.max_iter {
132 ExitStatus::Converged
133 } else {
134 ExitStatus::NotConvergedIterations
135 },
136 num_iter,
137 now.elapsed(),
138 self.fbs_engine.cache.norm_fpr,
139 cost_value,
140 ))
141 }
142}
143
144impl<'life, GradientType, ConstraintType, CostType, T> Optimizer<T>
145 for FBSOptimizer<'life, GradientType, ConstraintType, CostType, T>
146where
147 T: Float,
148 GradientType: Fn(&[T], &mut [T]) -> FunctionCallResult + 'life,
149 CostType: Fn(&[T], &mut T) -> FunctionCallResult + 'life,
150 ConstraintType: constraints::Constraint<T> + 'life,
151{
152 fn solve(&mut self, u: &mut [T]) -> Result<SolverStatus<T>, SolverError> {
153 FBSOptimizer::solve(self, u)
154 }
155}