use nalgebra::{ComplexField, Const, DimMin, RealField, SMatrix, SVector};
use num_traits::{FromPrimitive, Zero};
mod polynomial;
pub use polynomial::*;
pub fn bisection<N, F>(
(mut left, mut right): (N, N),
mut f: F,
tol: N,
n_max: usize,
) -> Result<N, String>
where
N: RealField + FromPrimitive + Copy,
F: FnMut(N) -> N,
{
if left >= right {
return Err("Bisection: requirement: right > left".to_owned());
}
let mut n = 1;
let mut f_a = f(left);
if (f_a * f(right)).is_sign_positive() {
return Err("Bisection: requirement: Signs must be different".to_owned());
}
let half = N::from_f64(0.5).unwrap();
let mut half_interval = (left - right) * half;
let mut middle = left + half_interval;
if middle.abs() <= tol {
return Ok(middle);
}
while n <= n_max {
let f_p = f(middle);
if (f_p * f_a).is_sign_positive() {
left = middle;
f_a = f_p;
} else {
right = middle;
}
half_interval = (right - left) * half;
let middle_new = left + half_interval;
if (middle - middle_new).abs() / middle.abs() < tol || middle_new.abs() < tol {
return Ok(middle_new);
}
middle = middle_new;
n += 1;
}
Err("Bisection: Maximum iterations exceeded".to_owned())
}
pub fn steffensen<N>(mut initial: N, f: fn(N) -> N, tol: N, n_max: usize) -> Result<N, String>
where
N: RealField + FromPrimitive + Copy,
{
let mut n = 0;
while n < n_max {
let guess = f(initial);
let new_guess = f(guess);
let diff = initial
- (guess - initial).powi(2) / (new_guess - N::from_f64(2.0).unwrap() * guess + initial);
if (diff - initial).abs() <= tol {
return Ok(diff);
}
initial = diff;
n += 1;
}
Err("Steffensen: Maximum number of iterations exceeded".to_owned())
}
pub fn newton<N, F, G, const S: usize>(
initial: &[N],
mut f: F,
mut jac: G,
tol: <N as ComplexField>::RealField,
n_max: usize,
) -> Result<SVector<N, S>, String>
where
N: ComplexField + FromPrimitive + Copy,
<N as ComplexField>::RealField: FromPrimitive + Copy,
F: FnMut(&[N]) -> SVector<N, S>,
G: FnMut(&[N]) -> SMatrix<N, S, S>,
Const<S>: DimMin<Const<S>, Output = Const<S>>,
{
let mut guess = SVector::<N, S>::from_column_slice(initial);
let mut norm = guess.dot(&guess).sqrt().abs();
let mut n = 0;
if norm <= tol {
return Ok(guess);
}
while n < n_max {
let f_val = -f(guess.as_slice());
let f_deriv_val = jac(guess.as_slice());
let lu = f_deriv_val.lu();
match lu.solve(&f_val) {
None => return Err("newton: failed to solve linear equation".to_owned()),
Some(adjustment) => {
let new_guess = guess + adjustment;
let new_norm = new_guess.dot(&new_guess).sqrt().abs();
if ((norm - new_norm) / norm).abs() <= tol || new_norm <= tol {
return Ok(new_guess);
}
norm = new_norm;
guess = new_guess;
n += 1;
}
}
}
Err("Newton: Maximum iterations exceeded".to_owned())
}
fn jac_finite_diff<N, F, const S: usize>(
mut f: F,
x: &mut SVector<N, S>,
h: <N as ComplexField>::RealField,
) -> SMatrix<N, S, S>
where
N: ComplexField + FromPrimitive + Copy,
<N as ComplexField>::RealField: FromPrimitive + Copy,
F: FnMut(&[N]) -> SVector<N, S>,
{
let mut mat = SMatrix::<N, S, S>::zero();
let h = N::from_real(h);
let denom = N::one() / (N::from_i32(2).unwrap() * h);
for col in 0..mat.row(0).len() {
x[col] += h;
let above = f(x.as_slice());
x[col] -= h;
x[col] -= h;
let below = f(x.as_slice());
x[col] += h;
let jac_col = (above + below) * denom;
for row in 0..mat.column(0).len() {
mat[(row, col)] = jac_col[row];
}
}
mat
}
pub fn secant<N, F, const S: usize>(
initial: &[N],
mut func: F,
h: <N as ComplexField>::RealField,
tol: <N as ComplexField>::RealField,
n_max: usize,
) -> Result<SVector<N, S>, String>
where
N: ComplexField + FromPrimitive + Copy,
<N as ComplexField>::RealField: FromPrimitive + Copy,
F: FnMut(&[N]) -> SVector<N, S>,
Const<S>: DimMin<Const<S>, Output = Const<S>>,
{
let mut n = 2;
let mut guess = SVector::<N, S>::from_column_slice(initial);
let mut func_eval = func(guess.as_slice());
let jac = jac_finite_diff(&mut func, &mut guess, h);
let lu = jac.lu();
let try_inv = lu.try_inverse();
let mut jac_inv = if let Some(inv) = try_inv {
inv
} else {
return Err("Secant: Can not inverse finite element difference jacobian".to_owned());
};
let mut shift = -jac_inv * func_eval;
guess += &shift;
while n < n_max {
let func_eval_last = func_eval;
func_eval = func(guess.as_slice());
let diff = func_eval - func_eval_last;
let adjustment = -jac_inv * diff;
let s_transpose = shift.transpose();
let p = (-s_transpose * adjustment)[(0, 0)];
let u = s_transpose * jac_inv;
jac_inv += (shift + adjustment) * u / p;
shift = -&jac_inv * func_eval;
guess += &shift;
if shift.norm().abs() <= tol {
return Ok(guess);
}
n += 1;
}
Err("Secant: Maximum iterations exceeded".to_owned())
}
pub fn brent<N, F>(initial: (N, N), mut f: F, tol: N) -> Result<N, String>
where
N: RealField + FromPrimitive + Copy,
F: FnMut(N) -> N,
{
if !tol.is_sign_positive() {
return Err("brent: tolerance must be positive".to_owned());
}
let mut left = initial.0;
let mut right = initial.1;
let mut f_left = f(left);
let mut f_right = f(right);
if f_left.abs() < f_right.abs() {
std::mem::swap(&mut left, &mut right);
std::mem::swap(&mut f_left, &mut f_right);
}
if !(f_left * f_right).is_sign_negative() {
return Err("brent: initial guesses do not bracket root".to_owned());
}
let two = N::from_i32(2).unwrap();
let three = N::from_i32(3).unwrap();
let four = N::from_i32(4).unwrap();
let mut c = left;
let mut f_c = f_left;
let mut s = right - f_right * (right - left) / (f_right - f_left);
let mut f_s = f(s);
let mut mflag = true;
let mut d = c;
while !(f_right.abs() < tol || f_s.abs() < tol || (left - right).abs() < tol) {
if (f_left - f_c).abs() < tol && (f_right - f_c).abs() < tol {
s = (left * f_right * f_c) / ((f_left - f_right) * (f_left - f_c))
+ (right * f_left * f_c) / ((f_right - f_left) * (f_right - f_c))
+ (c * f_left * f_right) / ((f_c - f_left) * (f_c - f_right));
} else {
s = right - f_right * (right - left) / (f_right - f_left);
}
if !(s >= (three * left + right) / four && s <= right)
|| (mflag && (s - right).abs() >= (right - c) / two)
|| (!mflag && (s - right).abs() >= (c - d).abs() / two)
|| (mflag && (right - c).abs() < tol)
|| (!mflag && (c - d).abs() < tol)
{
s = (left + right) / two;
mflag = true;
} else {
mflag = false;
}
f_s = f(s);
d = c;
c = right;
f_c = f_right;
if (f_left * f_s).is_sign_negative() {
right = s;
f_right = f_s;
} else {
left = s;
f_left = f_s;
}
if f_left.abs() < f_right.abs() {
std::mem::swap(&mut left, &mut right);
std::mem::swap(&mut f_left, &mut f_right);
}
}
if f_s.abs() < tol {
Ok(s)
} else {
Ok(right)
}
}
pub fn itp<N, F>(initial: (N, N), mut f: F, k_1: N, k_2: N, n_0: N, tol: N) -> Result<N, String>
where
N: RealField + FromPrimitive + Copy,
F: FnMut(N) -> N,
{
if !tol.is_sign_positive() {
return Err("itp: tolerance must be positive".to_owned());
}
if !k_1.is_sign_positive() {
return Err("itp: k_1 must be positive".to_owned());
}
if k_2 <= N::one() || k_2 >= (N::one() + N::from_f64(0.5 * (1.0 + 5.0_f64.sqrt())).unwrap()) {
return Err("itp: k_2 must be in (1, 1 + golden_ratio)".to_owned());
}
let mut left = initial.0;
let mut right = initial.1;
let mut f_left = f(left);
let mut f_right = f(right);
if !(f_left * f_right).is_sign_negative() {
return Err("itp: initial guesses must bracket root".to_owned());
}
if f_left.is_sign_positive() {
std::mem::swap(&mut left, &mut right);
std::mem::swap(&mut f_left, &mut f_right);
}
let two = N::from_i32(2).unwrap();
let n_half = ((right - left).abs() / (two * tol)).log2().ceil();
let n_max = n_half + n_0;
let mut j = 0;
while (right - left).abs() > two * tol {
let x_half = (left + right) / two;
let r = tol * two.powf(n_max + n_0 - N::from_i32(j).unwrap()) - (right - left) / two;
let x_f = (f_right * left - f_left * right) / (f_right - f_left);
let sigma = (x_half - x_f) / (x_half - x_f).abs();
let delta = k_1 * (right - left).powf(k_2);
let x_t = if delta <= (x_half - x_f).abs() {
x_f + sigma * delta
} else {
x_half
};
let x_itp = if (x_t - x_half).abs() <= r {
x_t
} else {
x_half - sigma * r
};
let f_itp = f(x_itp);
if f_itp.is_sign_positive() {
right = x_itp;
f_right = f_itp;
} else if f_itp.is_sign_negative() {
left = x_itp;
f_left = f_itp;
} else {
left = x_itp;
right = x_itp;
}
j += 1;
}
Ok((left + right) / two)
}