use super::Objective;
#[derive(Debug, Clone)]
pub struct Quadratic {
center: Vec<f64>,
}
impl Quadratic {
#[must_use]
pub const fn new(center: Vec<f64>) -> Self {
Self { center }
}
}
impl Objective for Quadratic {
fn value(&self, x: &[f64]) -> f64 {
x.iter()
.zip(&self.center)
.map(|(a, c)| (a - c) * (a - c))
.sum()
}
fn grad(&self, x: &[f64]) -> Vec<f64> {
x.iter()
.zip(&self.center)
.map(|(a, c)| 2.0 * (a - c))
.collect()
}
fn hessian(&self, x: &[f64]) -> Vec<Vec<f64>> {
let n = x.len();
(0..n)
.map(|i| (0..n).map(|j| if i == j { 2.0 } else { 0.0 }).collect())
.collect()
}
}
#[derive(Debug, Clone, Copy)]
pub struct Rosenbrock;
impl Objective for Rosenbrock {
fn value(&self, x: &[f64]) -> f64 {
let x0 = *x.first().unwrap_or(&0.0);
let x1 = *x.get(1).unwrap_or(&0.0);
let a = 1.0 - x0;
let b = x1 - x0 * x0;
b.mul_add(100.0 * b, a * a)
}
fn grad(&self, x: &[f64]) -> Vec<f64> {
let x0 = *x.first().unwrap_or(&0.0);
let x1 = *x.get(1).unwrap_or(&0.0);
let d0 = (-400.0 * x0).mul_add(x1 - x0 * x0, -2.0 * (1.0 - x0));
let d1 = 200.0 * (x1 - x0 * x0);
vec![d0, d1]
}
fn hessian(&self, x: &[f64]) -> Vec<Vec<f64>> {
let x0 = *x.first().unwrap_or(&0.0);
let x1 = *x.get(1).unwrap_or(&0.0);
let h00 = (-400.0_f64).mul_add(x1, (1200.0 * x0).mul_add(x0, 2.0));
let h01 = -400.0 * x0;
vec![vec![h00, h01], vec![h01, 200.0]]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn quadratic_minimum_is_zero_at_center() {
let q = Quadratic::new(vec![3.0, -2.0]);
assert!(
q.value(&[3.0, -2.0]).abs() < 1e-15,
"f(c) = {}",
q.value(&[3.0, -2.0])
);
let g = q.grad(&[3.0, -2.0]);
assert!(super::super::norm(&g) < 1e-15, "grad at center nonzero");
}
#[test]
fn rosenbrock_minimum_is_zero_at_one_one() {
let r = Rosenbrock;
assert!(
r.value(&[1.0, 1.0]).abs() < 1e-15,
"f(1,1) = {}",
r.value(&[1.0, 1.0])
);
let g = r.grad(&[1.0, 1.0]);
assert!(super::super::norm(&g) < 1e-12, "grad at (1,1) nonzero");
}
#[test]
fn rosenbrock_analytic_hessian_matches_finite_difference() {
let r = Rosenbrock;
let x = [0.5, 0.3];
let analytic = r.hessian(&x);
let fd = Objective::hessian(&FdRosen, &x);
for (ra, rf) in analytic.iter().zip(&fd) {
for (a, f) in ra.iter().zip(rf) {
assert!((a - f).abs() < 1e-4, "hessian mismatch: {a} vs {f}");
}
}
}
struct FdRosen;
impl Objective for FdRosen {
fn value(&self, x: &[f64]) -> f64 {
Rosenbrock.value(x)
}
fn grad(&self, x: &[f64]) -> Vec<f64> {
Rosenbrock.grad(x)
}
}
}