simplers_optimization 0.2.0

A Rust implementation of the Simple(x) black-box optimization algorithm.
Documentation
use crate::point::*;
use crate::search_space::*;
use std::hash::{Hash, Hasher};
use std::rc::Rc;
use ordered_float::OrderedFloat;
use num_traits::Float;

/// represents a simplex which is a d+1 corners solid (in dimenssion d)
/// (not to be confused with the simplex algorithm)
#[derive(Clone)]
pub struct Simplex<CoordFloat: Float, ValueFloat: Float>
{
   /// the coordinate+evaluations of the corners of the simplex
   pub corners: Vec<Rc<Point<CoordFloat, ValueFloat>>>,
   /// the coordinates of the center of the simplex (which is where it is evaluated)
   pub center: Coordinates<CoordFloat>
}

impl<CoordFloat: Float, ValueFloat: Float> Simplex<CoordFloat, ValueFloat>
{
   /// creates a new simplex
   fn new(corners: Vec<Rc<Point<CoordFloat, ValueFloat>>>) -> Self
   {
      let center = Point::average_coordinate(&corners);
      Simplex { corners, center }
   }

   /// builds the initial unit simplex with one point per axis plus an origin at zero
   pub fn initial_simplex(search_space: &SearchSpace<CoordFloat, ValueFloat>)
                          -> Simplex<CoordFloat, ValueFloat>
   {
      // origin, a vector of zero
      let origin = vec![CoordFloat::zero(); search_space.dimension].into_boxed_slice();

      // builds one corner per dimension
      let mut corners: Vec<Rc<Point<CoordFloat, ValueFloat>>> = (0..search_space.dimension).map(|i| {
                                                                   let mut coordinates = origin.clone();
                                                                   coordinates[i] = CoordFloat::one();
                                                                   let value =
                                                                      search_space.evaluate(&coordinates);
                                                                   Rc::new(Point { coordinates, value })
                                                                })
                                                                .collect();

      // adds the corner corresponding to the origin
      let min_corner = Point { value: search_space.evaluate(&origin), coordinates: origin };
      corners.push(Rc::new(min_corner));

      // assemble the simplex
      Simplex::new(corners)
   }

   /// takes a simplex and splits it around a point
   pub fn split(self, new_point: Rc<Point<CoordFloat, ValueFloat>>) -> Vec<Self>
   {
      // computes each sub simplex
      let mut result = vec![];
      for i in 0..self.corners.len()
      {
         // we refuse simplex reduced to a point
         let mut corners = self.corners.clone();
         corners[i] = new_point.clone();
         let simplex = Simplex::new(corners);
         result.push(simplex);
      }

      result
   }

   /// takes a simplex and a lipvitz constant
   /// returns a score for the simplex
   pub fn evaluate(&self, k: ValueFloat) -> ValueFloat
   {
      self.corners
          .iter()
          .map(|corner| corner.evaluate_upper_bound(&self.center, k))
          .min_by_key(|&value| OrderedFloat(value))
          .expect("You need at least one corner in order to evaluate a simplex!")
   }
}

//-----------------------------------------------------------------------------
// TRAITS FOR PRIORITY QUEUE

/// workaround since floats cannot be hashed
impl<CoordFloat: Float, ValueFloat: Float> Hash for Simplex<CoordFloat, ValueFloat>
{
   /// relies on a hash of the bit representation of the coordinates of the center of the simplex
   fn hash<H: Hasher>(&self, state: &mut H)
   {
      // TODO I will drop `.to_f64().unwrap()`
      // once the relevant [issue](https://github.com/rust-num/num-traits/issues/123) is resolved
      self.center.iter().map(|&x| x.to_f64().unwrap().to_bits()).collect::<Box<[u64]>>().hash(state);
   }
}

impl<CoordFloat: Float, ValueFloat: Float> PartialEq for Simplex<CoordFloat, ValueFloat>
{
   /// two Simplex are equal if they have the exact same center
   fn eq(&self, other: &Self) -> bool
   {
      self.center == other.center
   }
}

impl<CoordFloat: Float, ValueFloat: Float> Eq for Simplex<CoordFloat, ValueFloat> {}