#![allow(unused)]
use std::error::Error as StdError;
use nalgebra::{
dvector,
storage::{Storage, StorageMut},
DVector, Dim, DimName, Dyn, IsContiguous, OVector, Vector, U1,
};
use thiserror::Error;
use crate::core::{Domain, Function, Optimizer, Problem, Solver, System};
pub trait TestProblem: Problem {
fn initials(&self) -> Vec<OVector<Self::Field, Dyn>>;
}
pub trait TestSystem: System + TestProblem
where
Self::Field: approx::RelativeEq,
{
fn roots(&self) -> Vec<OVector<Self::Field, Dyn>> {
Vec::new()
}
fn is_root<Sx>(&self, x: &Vector<Self::Field, Dyn, Sx>, eps: Self::Field) -> bool
where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
{
let mut rx = x.clone_owned();
self.eval(x, &mut rx);
rx.norm() <= eps
}
}
pub trait TestFunction: Function + TestProblem
where
Self::Field: approx::RelativeEq,
{
fn optima(&self) -> Vec<OVector<Self::Field, Dyn>> {
Vec::new()
}
fn is_optimum<Sx>(&self, x: &Vector<Self::Field, Dyn, Sx>, eps: Self::Field) -> bool
where
Sx: Storage<Self::Field, Dyn> + IsContiguous;
}
#[derive(Debug, Clone, Copy)]
pub struct ExtendedRosenbrock {
n: usize,
alpha: f64,
}
impl ExtendedRosenbrock {
pub fn new(n: usize) -> Self {
Self::with_scaling(n, 1.0)
}
pub fn with_scaling(n: usize, alpha: f64) -> Self {
assert!(n > 0, "n must be greater than zero");
assert!(n % 2 == 0, "n must be a multiple of 2");
assert!(alpha > 0.0, "alpha must be greater than zero");
Self { n, alpha }
}
fn residuals<'a, Sx>(&self, x: &'a Vector<f64, Dyn, Sx>) -> impl Iterator<Item = f64> + 'a
where
Sx: Storage<f64, Dyn> + IsContiguous,
{
let alpha = self.alpha;
(0..(self.n / 2)).flat_map(move |i| {
let i1 = 2 * i;
let i2 = 2 * i + 1;
let x1 = x[i1] * alpha;
let x2 = x[i2] / alpha;
let rx1 = 10.0 * (x2 - x1 * x1);
let rx2 = 1.0 - x1;
[rx1, rx2].into_iter()
})
}
}
impl Default for ExtendedRosenbrock {
fn default() -> Self {
Self::new(2)
}
}
impl Problem for ExtendedRosenbrock {
type Field = f64;
fn domain(&self) -> Domain<Self::Field> {
(0..self.n)
.map(|i| {
if i % 2 == 0 {
self.alpha
} else {
1.0 / self.alpha
}
})
.collect()
}
}
impl System for ExtendedRosenbrock {
fn eval<Sx, Srx>(
&self,
x: &Vector<Self::Field, Dyn, Sx>,
rx: &mut Vector<Self::Field, Dyn, Srx>,
) where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
Srx: StorageMut<Self::Field, Dyn>,
{
eval(self.residuals(x), rx)
}
fn norm<Sx>(&self, x: &Vector<Self::Field, Dyn, Sx>) -> Self::Field
where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
{
norm(self.residuals(x))
}
}
impl TestProblem for ExtendedRosenbrock {
fn initials(&self) -> Vec<OVector<Self::Field, Dyn>> {
let init1 = DVector::from_iterator(
self.n,
(0..self.n).map(|i| if i % 2 == 0 { -1.2 } else { 1.0 }),
);
let init2 = DVector::from_iterator(
self.n,
(0..self.n).map(|i| if i % 2 == 0 { 6.39 } else { -0.221 }),
);
vec![init1, init2]
}
}
impl TestSystem for ExtendedRosenbrock {
fn roots(&self) -> Vec<OVector<Self::Field, Dyn>> {
let root = (0..self.n).map(|i| {
if i % 2 == 0 {
1.0 / self.alpha
} else {
self.alpha
}
});
vec![DVector::from_iterator(self.n, root)]
}
}
#[derive(Debug, Clone, Copy)]
pub struct ExtendedPowell {
n: usize,
}
impl ExtendedPowell {
pub fn new(n: usize) -> Self {
assert!(n > 0, "n must be greater than zero");
assert!(n % 4 == 0, "n must be a multiple of 4");
Self { n }
}
fn residuals<'a, Sx>(&self, x: &'a Vector<f64, Dyn, Sx>) -> impl Iterator<Item = f64> + 'a
where
Sx: Storage<f64, Dyn> + IsContiguous,
{
(0..(self.n / 4)).flat_map(move |i| {
let i1 = 4 * i;
let i2 = 4 * i + 1;
let i3 = 4 * i + 2;
let i4 = 4 * i + 3;
let rx1 = x[i1] + 10.0 * x[i2];
let rx2 = 5f64.sqrt() * (x[i3] - x[i4]);
let rx3 = (x[i2] - 2.0 * x[i3]).powi(2);
let rx4 = 10f64.sqrt() * (x[i1] - x[i4]).powi(2);
[rx1, rx2, rx3, rx4].into_iter()
})
}
}
impl Default for ExtendedPowell {
fn default() -> Self {
Self::new(4)
}
}
impl Problem for ExtendedPowell {
type Field = f64;
fn domain(&self) -> Domain<Self::Field> {
Domain::unconstrained(self.n)
}
}
impl System for ExtendedPowell {
fn eval<Sx, Srx>(
&self,
x: &Vector<Self::Field, Dyn, Sx>,
rx: &mut Vector<Self::Field, Dyn, Srx>,
) where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
Srx: StorageMut<Self::Field, Dyn>,
{
eval(self.residuals(x), rx)
}
fn norm<Sx>(&self, x: &Vector<Self::Field, Dyn, Sx>) -> Self::Field
where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
{
norm(self.residuals(x))
}
}
impl TestProblem for ExtendedPowell {
fn initials(&self) -> Vec<OVector<Self::Field, Dyn>> {
let init = DVector::from_iterator(
self.n,
(0..self.n).map(|i| match i % 4 {
0 => 3.0,
1 => -1.0,
2 => 0.0,
3 => 1.0,
_ => unreachable!(),
}),
);
vec![init]
}
}
impl TestSystem for ExtendedPowell {
fn roots(&self) -> Vec<OVector<Self::Field, Dyn>> {
vec![DVector::from_element(self.n, 0.0)]
}
}
#[derive(Debug, Clone, Copy)]
pub struct BullardBiegler(());
impl BullardBiegler {
pub fn new() -> Self {
Self(())
}
fn residuals<'a, Sx>(&self, x: &'a Vector<f64, Dyn, Sx>) -> impl Iterator<Item = f64> + 'a
where
Sx: Storage<f64, Dyn> + IsContiguous,
{
[
1e4 * x[0] * x[1] - 1.0,
(-x[0]).exp() + (-x[1]).exp() - 1.001,
]
.into_iter()
}
}
impl Default for BullardBiegler {
fn default() -> Self {
Self::new()
}
}
impl Problem for BullardBiegler {
type Field = f64;
fn domain(&self) -> Domain<Self::Field> {
[(5.45e-6, 4.553), (2.196e-3, 18.21)].into_iter().collect()
}
}
impl System for BullardBiegler {
fn eval<Sx, Srx>(
&self,
x: &Vector<Self::Field, Dyn, Sx>,
rx: &mut Vector<Self::Field, Dyn, Srx>,
) where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
Srx: StorageMut<Self::Field, Dyn>,
{
eval(self.residuals(x), rx)
}
fn norm<Sx>(&self, x: &Vector<Self::Field, Dyn, Sx>) -> Self::Field
where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
{
norm(self.residuals(x))
}
}
impl TestProblem for BullardBiegler {
fn initials(&self) -> Vec<OVector<Self::Field, Dyn>> {
let init1 = dvector![0.1, 1.0];
let init2 = dvector![1.0, 1.0];
vec![init1, init2]
}
}
impl TestSystem for BullardBiegler {
fn roots(&self) -> Vec<OVector<Self::Field, Dyn>> {
vec![dvector![1.45e-5, 6.8933353]]
}
}
#[derive(Debug, Clone, Copy)]
pub struct Sphere {
n: usize,
}
impl Sphere {
pub fn new(n: usize) -> Self {
assert!(n > 0, "n must be greater than zero");
Self { n }
}
fn residuals<'a, Sx>(&self, x: &'a Vector<f64, Dyn, Sx>) -> impl Iterator<Item = f64> + 'a
where
Sx: Storage<f64, Dyn> + IsContiguous,
{
x.iter().map(|xi| xi.powi(2))
}
}
impl Default for Sphere {
fn default() -> Self {
Self::new(2)
}
}
impl Problem for Sphere {
type Field = f64;
fn domain(&self) -> Domain<Self::Field> {
Domain::unconstrained(self.n)
}
}
impl System for Sphere {
fn eval<Sx, Srx>(
&self,
x: &Vector<Self::Field, Dyn, Sx>,
rx: &mut Vector<Self::Field, Dyn, Srx>,
) where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
Srx: StorageMut<Self::Field, Dyn>,
{
eval(self.residuals(x), rx)
}
fn norm<Sx>(&self, x: &Vector<Self::Field, Dyn, Sx>) -> Self::Field
where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
{
norm(self.residuals(x))
}
}
impl TestProblem for Sphere {
fn initials(&self) -> Vec<OVector<Self::Field, Dyn>> {
let init = DVector::from_iterator(
self.n,
(0..self.n).map(|i| if i % 2 == 0 { 10.0 } else { -10.0 }),
);
vec![init]
}
}
impl TestSystem for Sphere {
fn roots(&self) -> Vec<OVector<Self::Field, Dyn>> {
vec![DVector::from_element(self.n, 0.0)]
}
}
impl TestFunction for Sphere {
fn optima(&self) -> Vec<OVector<Self::Field, Dyn>> {
<Sphere as TestSystem>::roots(self)
}
fn is_optimum<Sx>(&self, x: &Vector<Self::Field, Dyn, Sx>, eps: Self::Field) -> bool
where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
{
self.apply(x).abs() <= eps
}
}
#[derive(Debug, Clone, Copy)]
pub struct Brown {
n: usize,
}
impl Brown {
pub fn new(n: usize) -> Self {
assert!(n > 1, "n must be greater than one");
Self { n }
}
fn residuals<'a, Sx>(&self, x: &'a Vector<f64, Dyn, Sx>) -> impl Iterator<Item = f64> + 'a
where
Sx: Storage<f64, Dyn> + IsContiguous,
{
let n = self.n as f64;
let x_sum = x.sum();
let r0x = x.iter().product::<f64>() - 1.0;
let rix = x
.iter()
.skip(1)
.copied()
.map(move |xi| xi + x_sum - n + 1.0);
std::iter::once(r0x).chain(rix)
}
}
impl Default for Brown {
fn default() -> Self {
Self::new(5)
}
}
impl Problem for Brown {
type Field = f64;
fn domain(&self) -> Domain<Self::Field> {
Domain::unconstrained(self.n)
}
}
impl System for Brown {
fn eval<Sx, Srx>(
&self,
x: &Vector<Self::Field, Dyn, Sx>,
rx: &mut Vector<Self::Field, Dyn, Srx>,
) where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
Srx: StorageMut<Self::Field, Dyn>,
{
eval(self.residuals(x), rx)
}
fn norm<Sx>(&self, x: &Vector<Self::Field, Dyn, Sx>) -> Self::Field
where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
{
norm(self.residuals(x))
}
}
impl TestProblem for Brown {
fn initials(&self) -> Vec<OVector<Self::Field, Dyn>> {
let init = DVector::zeros_generic(Dyn::from_usize(self.n), U1::name());
vec![init]
}
}
impl TestSystem for Brown {}
#[derive(Debug, Clone, Copy)]
pub struct Exponential {
n: usize,
}
impl Exponential {
pub fn new(n: usize) -> Self {
assert!(n > 0, "n must be greater than zero");
Self { n }
}
fn residuals<'a, Sx>(&self, x: &'a Vector<f64, Dyn, Sx>) -> impl Iterator<Item = f64> + 'a
where
Sx: Storage<f64, Dyn> + IsContiguous,
{
let x_sum = x.sum();
x.iter()
.copied()
.enumerate()
.map(move |(i, xi)| xi - (((i + 1) as f64) * x_sum).cos().exp())
}
}
impl Default for Exponential {
fn default() -> Self {
Self::new(2)
}
}
impl Problem for Exponential {
type Field = f64;
fn domain(&self) -> Domain<Self::Field> {
Domain::unconstrained(2)
}
}
impl System for Exponential {
fn eval<Sx, Srx>(
&self,
x: &Vector<Self::Field, Dyn, Sx>,
rx: &mut Vector<Self::Field, Dyn, Srx>,
) where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
Srx: StorageMut<Self::Field, Dyn>,
{
eval(self.residuals(x), rx)
}
fn norm<Sx>(&self, x: &Vector<Self::Field, Dyn, Sx>) -> Self::Field
where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
{
norm(self.residuals(x))
}
}
impl TestProblem for Exponential {
fn initials(&self) -> Vec<OVector<Self::Field, Dyn>> {
let init = DVector::zeros_generic(Dyn::from_usize(self.n), U1::name());
vec![init]
}
}
impl TestSystem for Exponential {}
#[derive(Debug, Clone, Copy)]
pub struct InfiniteSolutions {
n: usize,
}
impl InfiniteSolutions {
pub fn new(n: usize) -> Self {
assert!(n > 0, "n must be greater than zero");
Self { n }
}
}
impl Default for InfiniteSolutions {
fn default() -> Self {
Self { n: 1 }
}
}
impl Problem for InfiniteSolutions {
type Field = f64;
fn domain(&self) -> Domain<Self::Field> {
Domain::unconstrained(self.n)
}
}
impl System for InfiniteSolutions {
fn eval<Sx, Srx>(
&self,
_x: &Vector<Self::Field, Dyn, Sx>,
rx: &mut Vector<Self::Field, Dyn, Srx>,
) where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
Srx: StorageMut<Self::Field, Dyn>,
{
rx.fill(0.0);
}
fn norm<Sx>(&self, _: &Vector<Self::Field, Dyn, Sx>) -> Self::Field
where
Sx: Storage<Self::Field, Dyn> + IsContiguous,
{
0.0
}
}
impl TestProblem for InfiniteSolutions {
fn initials(&self) -> Vec<OVector<Self::Field, Dyn>> {
let init = DVector::zeros_generic(Dyn::from_usize(self.n), U1::name());
vec![init]
}
}
impl TestSystem for InfiniteSolutions {}
#[derive(Debug, Error)]
pub enum TestingError<E: StdError + 'static> {
#[error("{0}")]
Inner(#[from] E),
#[error("algorithm did not terminate")]
Termination,
}
pub fn solve<R: TestSystem, A: Solver<R>>(
r: &R,
dom: &Domain<R::Field>,
mut solver: A,
mut x: OVector<R::Field, Dyn>,
max_iters: usize,
tolerance: R::Field,
) -> Result<OVector<R::Field, Dyn>, TestingError<A::Error>>
where
A::Error: StdError,
{
let mut rx = x.clone_owned();
let mut iter = 0;
loop {
solver.solve_next(r, dom, &mut x, &mut rx)?;
if rx.norm() <= tolerance {
return Ok(x);
}
if iter == max_iters {
return Err(TestingError::Termination);
} else {
iter += 1;
}
}
}
pub fn optimize<F: Function, O: Optimizer<F>>(
f: &F,
dom: &Domain<F::Field>,
mut optimizer: O,
mut x: OVector<F::Field, Dyn>,
min: F::Field,
max_iters: usize,
tolerance: F::Field,
) -> Result<OVector<F::Field, Dyn>, TestingError<O::Error>>
where
O::Error: StdError,
{
let mut iter = 0;
loop {
let fx = optimizer.opt_next(f, dom, &mut x)?;
if fx <= min + tolerance {
return Ok(x);
}
if iter == max_iters {
return Err(TestingError::Termination);
} else {
iter += 1;
}
}
}
pub fn iter<R: TestSystem, A: Solver<R>, G>(
r: &R,
dom: &Domain<R::Field>,
mut solver: A,
mut x: OVector<R::Field, Dyn>,
iters: usize,
mut inspect: G,
) -> Result<(), A::Error>
where
A::Error: StdError,
G: FnMut(&A, &OVector<R::Field, Dyn>, R::Field, usize),
{
let mut rx = x.clone_owned();
for iter in 0..iters {
solver.solve_next(r, dom, &mut x, &mut rx)?;
inspect(&solver, &x, rx.norm(), iter);
}
Ok(())
}
fn eval<Srx>(residuals: impl Iterator<Item = f64>, rx: &mut Vector<f64, Dyn, Srx>)
where
Srx: StorageMut<f64, Dyn>,
{
rx.iter_mut()
.zip(residuals)
.for_each(|(rix, res)| *rix = res);
}
fn norm(residuals: impl Iterator<Item = f64>) -> f64 {
residuals.map(|res| res.powi(2)).sum::<f64>().sqrt()
}