use rayon::prelude::*;
use ndarray::{Array, Dimension};
pub trait Function<D:Dimension> : Sync {
fn value(&self, position: &ndarray::Array<f64,D>) -> f64;
}
pub trait FunctionC1<D:Dimension> : Function<D> {
fn gradient(&self, position: &Array<f64,D>) -> Array<f64,D>;
}
pub trait Summation<D:Dimension>: Function<D> {
fn terms(&self) -> usize;
fn term_value(&self, position: &Array<f64,D>, term: usize) -> f64;
fn partial_value(&self, position:&Array<f64,D> , terms: &[usize]) -> f64 {
let f = |t : usize| -> f64 { self.term_value(position, t)};
let value = terms.into_par_iter().map(|t| f(*t)).sum();
value
}
}
impl<D : Dimension, S: Summation<D> > Function<D> for S {
fn value(&self, position: &Array<f64,D>) -> f64 {
let value = self.partial_value(position, &(0..self.terms()).into_iter().collect::<Vec<usize>>());
value/(self.terms() as f64)
}
}
pub trait SummationC1<D:Dimension> : Summation<D> + FunctionC1<D> {
fn term_gradient(&self, position: &Array<f64, D>, term: &usize, gradient : &mut Array<f64, D>);
fn partial_gradient(&self, position: &Array<f64, D>, terms: &[usize], gradient : &mut Array<f64, D>) {
assert!(terms.len() > 0);
gradient.fill(0.);
let mut term_gradient : Array<f64, D> = gradient.clone();
if terms.len() < 1000 {
for term in terms.into_iter() {
self.term_gradient(position, &term, &mut term_gradient);
*gradient += &term_gradient;
}
}
else {
let block_size = 500;
let nb_block = if terms.len() % block_size == 0 {
terms.len() / block_size
}
else {
1 + (terms.len() / block_size)
};
let first = | i : usize | -> usize {
let start = i * block_size;
start
};
let last = | i : usize | -> usize {
let end = ((i+1) * block_size).min(terms.len());
end
};
let compute = | i: usize | -> Array<f64, D> {
let mut block_gradient : Array<f64, D> = gradient.clone();
block_gradient.fill(0.);
let mut term_gradient : Array<f64, D> = gradient.clone();
for k in first(i)..last(i) {
self.term_gradient(position, &terms[k], &mut term_gradient);
block_gradient += &term_gradient;
}
block_gradient
}; *gradient = (0..nb_block).into_par_iter().map(|b| compute(b)).reduce(|| gradient.clone(), | acc , g| acc + g );
}
}
fn mean_partial_gradient(&self, position: &Array<f64, D>, terms: &[usize], gradient : &mut Array<f64, D>) {
self.partial_gradient(position, terms, gradient);
gradient.iter_mut().for_each(|x| *x /= terms.len() as f64);
}
}
impl<D:Dimension, S: SummationC1<D> > FunctionC1<D> for S {
fn gradient(&self, position: &Array<f64, D>) -> Array<f64, D> {
let mut gradient : Array<f64, D> = position.clone();
gradient.fill(0.);
let mut gradient_term : Array<f64, D> = position.clone();
gradient_term.fill(0.);
if self.terms() < 2000 {
for term in 0..self.terms() {
self.term_gradient(position, &term, &mut gradient_term);
gradient += &gradient_term;
}
}
else {
let block_size = 1000;
let nb_block = if self.terms() % block_size == 0 {
self.terms() / block_size
}
else {
1 + (self.terms() / block_size)
};
let first = | i : usize | -> usize {
let start = i * block_size;
start
};
let last = | i : usize | -> usize {
let end = ((i+1) * block_size).min(self.terms());
end
};
let compute = | i: usize | -> Array<f64, D> {
let mut block_gradient : Array<f64, D> = gradient.clone();
block_gradient.fill(0.);
let mut term_gradient : Array<f64, D> = gradient.clone();
for k in first(i)..last(i) {
self.term_gradient(position, &k, &mut term_gradient);
block_gradient += &term_gradient;
}
block_gradient
}; gradient = (0..nb_block).into_par_iter().map(|b| compute(b)).reduce(|| gradient.clone(), | acc , g| acc + g );
}
gradient.iter_mut().for_each(|x| *x /= self.terms() as f64);
gradient
}
}
pub trait Minimizer<D: Dimension, F: ?Sized, MinimizerArg> {
type Solution: Evaluation<D>;
fn minimize(&self, function: &F, initial_position: &Array<f64,D>, args : Option<MinimizerArg>) -> Self::Solution;
}
pub trait Evaluation<D:Dimension> {
fn position(&self) -> &Array<f64,D>;
fn value(&self) -> f64;
}
#[derive(Debug, Clone)]
pub struct Solution<D:Dimension> {
pub position: Array<f64,D>,
pub value: f64
}
impl <D:Dimension> Solution<D> {
pub fn new(position: Array<f64,D>, value: f64) -> Solution<D> {
Solution {
position: position,
value: value
}
}
}
impl <D:Dimension> Evaluation<D> for Solution<D> {
fn position(&self) -> &Array<f64,D> {
&self.position
}
fn value(&self) -> f64 {
self.value
}
}
#[allow(dead_code)]
pub fn norm_l2<D:Dimension>(gradient : &Array<f64,D>) -> f64 {
let norm = gradient.fold(0., |norm, x | norm+ (x * x));
norm.sqrt()
}