use crate::prelude::*;
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum BrentError {
#[error("Brent error: f(min) and f(max) must have different signs.")]
WrongSign,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct Brent<F> {
tol: F,
a: F,
b: F,
c: F,
d: F,
e: F,
fa: F,
fb: F,
fc: F,
}
impl<F: ArgminFloat> Brent<F> {
pub fn new(min: F, max: F, tol: F) -> Brent<F> {
Brent {
tol: tol,
a: min,
b: max,
c: max,
d: F::nan(),
e: F::nan(),
fa: F::nan(),
fb: F::nan(),
fc: F::nan(),
}
}
}
impl<O, F> Solver<O> for Brent<F>
where
O: ArgminOp<Param = F, Output = F, Float = F>,
F: ArgminFloat,
{
const NAME: &'static str = "Brent";
fn init(
&mut self,
op: &mut OpWrapper<O>,
_state: &IterState<O>,
) -> Result<Option<ArgminIterData<O>>, Error> {
self.fa = op.apply(&self.a)?;
self.fb = op.apply(&self.b)?;
if self.fa * self.fb > F::from_f64(0.0).unwrap() {
return Err(BrentError::WrongSign.into());
}
self.fc = self.fb;
Ok(Some(
ArgminIterData::new().param(self.b).cost(self.fb.abs()),
))
}
fn next_iter(
&mut self,
op: &mut OpWrapper<O>,
_state: &IterState<O>,
) -> Result<ArgminIterData<O>, Error> {
if (self.fb > F::from_f64(0.0).unwrap() && self.fc > F::from_f64(0.0).unwrap())
|| self.fb < F::from_f64(0.0).unwrap() && self.fc < F::from_f64(0.0).unwrap()
{
self.c = self.a;
self.fc = self.fa;
self.d = self.b - self.a;
self.e = self.d;
}
if self.fc.abs() < self.fb.abs() {
self.a = self.b;
self.b = self.c;
self.c = self.a;
self.fa = self.fb;
self.fb = self.fc;
self.fc = self.fa;
}
let eff_tol = F::from_f64(2.0).unwrap() * F::epsilon() * self.b.abs()
+ F::from_f64(0.5).unwrap() * self.tol;
let mid = F::from_f64(0.5).unwrap() * (self.c - self.b);
if mid.abs() <= eff_tol || self.fb == F::from_f64(0.0).unwrap() {
return Ok(ArgminIterData::new()
.termination_reason(TerminationReason::TargetPrecisionReached)
.param(self.b)
.cost(self.fb.abs()));
}
if self.e.abs() >= eff_tol && self.fa.abs() > self.fb.abs() {
let s = self.fb / self.fa;
let (mut p, mut q) = if self.a == self.c {
(
F::from_f64(2.0).unwrap() * mid * s,
F::from_f64(1.0).unwrap() - s,
)
} else {
let q = self.fa / self.fc;
let r = self.fb / self.fc;
(
s * (F::from_f64(2.0).unwrap() * mid * q * (q - r)
- (self.b - self.a) * (r - F::from_f64(1.0).unwrap())),
(q - F::from_f64(1.0).unwrap())
* (r - F::from_f64(1.0).unwrap())
* (s - F::from_f64(1.0).unwrap()),
)
};
if p > F::from_f64(0.0).unwrap() {
q = -q;
}
p = p.abs();
let min1 = F::from_f64(3.0).unwrap() * mid * q - (eff_tol * q).abs();
let min2 = (self.e * q).abs();
if F::from_f64(2.0).unwrap() * p < if min1 < min2 { min1 } else { min2 } {
self.e = self.d;
self.d = p / q;
} else {
self.d = mid;
self.e = self.d;
};
} else {
self.d = mid;
self.e = self.d;
};
self.a = self.b;
self.fa = self.fb;
if self.d.abs() > eff_tol {
self.b = self.b + self.d;
} else {
self.b = self.b
+ if mid >= F::from_f64(0.0).unwrap() {
eff_tol.abs()
} else {
-eff_tol.abs()
};
}
self.fb = op.apply(&self.b)?;
Ok(ArgminIterData::new().param(self.b).cost(self.fb.abs()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_trait_impl;
test_trait_impl!(brent, Brent<f64>);
}