use std::fmt;
use rand::{
random,
thread_rng,
seq::SliceRandom,
};
use maria_linalg::Vector;
use super::optimizer::Optimizer;
pub enum Paradigm {
SteepestDescent,
Newton,
Genetic,
SimulatedAnnealing,
}
impl fmt::Display for Paradigm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let p = match self {
Self::SteepestDescent => "Steepest Descent",
Self::Newton => "Newton's Method",
Self::Genetic => "Genetic Algorithm",
Self::SimulatedAnnealing => "Simulated Annealing",
};
write!(f, "{}", p)
}
}
impl Paradigm {
pub fn update<const C: usize, const D: usize, const K: usize>(
&self,
optimizer: &Optimizer<C, D, K>,
iter: usize,
continuous: [Vector<C>; K],
discrete: [Vector<D>; K],
permitted: &[f64],
) -> ([Vector<C>; K], [Vector<D>; K]) {
match self {
Self::SteepestDescent => Self::update_steepest_descent(optimizer, continuous, discrete),
Self::Newton => Self::update_newton(optimizer, continuous, discrete),
Self::Genetic => Self::update_genetic(optimizer, continuous, discrete, permitted),
Self::SimulatedAnnealing => Self::update_simulated_annealing(optimizer, iter, continuous, discrete, permitted),
}
}
fn update_steepest_descent<const C: usize, const D: usize, const K: usize>(
optimizer: &Optimizer<C, D, K>,
continuous: [Vector<C>; K],
discrete: [Vector<D>; K],
) -> ([Vector<C>; K], [Vector<D>; K]) {
let mut output = [Vector::zero(); K];
for i in 0..K {
let input = continuous[i];
let d = discrete[i];
let hessian = optimizer.hessian(input, d);
let step = optimizer.step(input, d);
if step.dot(hessian.mult(step)) < 0.0 {
println!();
println!("Non-convex region");
let mut stepsize = 0.1;
let mut i = 0;
while optimizer.objective(input + step.scale(0.01), d) > optimizer.objective(input, d) {
i += 1;
println!("Line search iteration: {}", i);
stepsize *= 0.5;
}
println!("Step size: {:.8}", stepsize);
output[i] = input + step.scale(stepsize)
} else {
output[i] = input + step.scale(step.dot(step) / hessian.mult(step).dot(step))
}
}
(output, discrete)
}
fn update_newton<const C: usize, const D: usize, const K: usize>(
optimizer: &Optimizer<C, D, K>,
continuous: [Vector<C>; K],
discrete: [Vector<D>; K],
) -> ([Vector<C>; K], [Vector<D>; K]) {
let mut output = [Vector::zero(); K];
for i in 0..K {
let input = continuous[i];
let d = discrete[i];
let hessian = optimizer.hessian(input, d);
let step = optimizer.step(input, d);
if step.dot(hessian.mult(step)) < 0.0 {
println!();
println!("Non-convex region");
let mut stepsize = 0.1;
let mut i = 0;
while optimizer.objective(input + step.scale(0.01), d) > optimizer.objective(input, d) {
i += 1;
println!("Line search iteration: {}", i);
stepsize *= 0.5;
}
println!("Step size: {:.8}", stepsize);
output[i] = input + step.scale(stepsize)
} else {
output[i] = input + hessian.inverse().mult(optimizer.step(input, d))
}
}
(output, discrete)
}
fn update_genetic<const C: usize, const D: usize, const K: usize>(
optimizer: &Optimizer<C, D, K>,
continuous: [Vector<C>; K],
discrete: [Vector<D>; K],
permitted: &[f64],
) -> ([Vector<C>; K], [Vector<D>; K]) {
let mut nextgen_continuous = [Vector::zero(); K];
let mut nextgen_discrete = [Vector::zero(); K];
let (sorted_continuous, sorted_discrete) = optimizer.sort(continuous, discrete);
for i in 0..K {
if i < K / 2 {
nextgen_continuous[i] = sorted_continuous[i];
nextgen_discrete[i] = sorted_discrete[i];
} else {
let mut rng = thread_rng();
if C > 0 {
let mother = continuous.choose(&mut rng).unwrap();
let father = continuous.choose(&mut rng).unwrap();
let child = Vector::<C>::child(mother, father, optimizer.stdev);
nextgen_continuous[i] = child;
}
if D > 0 {
let mother = discrete.choose(&mut rng).unwrap();
let father = discrete.choose(&mut rng).unwrap();
let child = Vector::<D>::child_discrete(mother, father, permitted);
nextgen_discrete[i] = child;
}
}
}
(nextgen_continuous, nextgen_discrete)
}
fn update_simulated_annealing<const C: usize, const D: usize, const K: usize>(
optimizer: &Optimizer<C, D, K>,
iter: usize,
continuous: [Vector<C>; K],
discrete: [Vector<D>; K],
permitted: &[f64],
) -> ([Vector<C>; K], [Vector<D>; K]) {
let mut nextgen_continuous = [Vector::zero(); K];
let mut nextgen_discrete = [Vector::zero(); K];
let temp = optimizer.maxtemp / (iter as f64);
for i in 0..K {
let mut rng = thread_rng();
if C > 0 {
let mother = continuous.choose(&mut rng).unwrap();
let father = continuous.choose(&mut rng).unwrap();
let child = Vector::<C>::child(mother, father, optimizer.stdev);
let obj = optimizer.objective(continuous[i], discrete[i]);
let child_obj = optimizer.objective(child, discrete[i]);
let p = if child_obj < obj {
1.0
} else {
let t = obj - child_obj;
(t / temp).exp()
};
if random::<f64>() < p {
nextgen_continuous[i] = child;
}
}
if D > 0 {
let mother = discrete.choose(&mut rng).unwrap();
let father = discrete.choose(&mut rng).unwrap();
let child = Vector::<D>::child_discrete(mother, father, permitted);
let obj = optimizer.objective(continuous[i], discrete[i]);
let child_obj = optimizer.objective(continuous[i], child);
let p = if child_obj < obj {
1.0
} else {
let t = obj - child_obj;
(t / temp).exp()
};
if random::<f64>() < p {
nextgen_discrete[i] = child;
}
}
}
(nextgen_continuous, nextgen_discrete)
}
}