extern crate rand;
use rand::distributions::{IndependentSample, Range};
pub struct Settings<F, R>
where F: Fn(&[f32]) -> f32,
R: rand::Rng
{
pub min_max_pos: Vec<(f32, f32)>,
pub cr_min_max: (f32, f32),
pub cr_change_probability: f32,
pub f_min_max: (f32, f32),
pub f_change_probability: f32,
pub pop_size: usize,
pub rng: R,
pub cost_function: F,
}
impl<F> Settings<F, rand::XorShiftRng>
where F: Fn(&[f32]) -> f32
{
pub fn default(min_max_pos: Vec<(f32, f32)>,
cost_function: F)
-> Settings<F, rand::XorShiftRng> {
Settings {
min_max_pos: min_max_pos,
cr_min_max: (0.0, 1.0),
cr_change_probability: 0.1,
f_min_max: (0.1, 1.0),
f_change_probability: 0.1,
pop_size: 100,
rng: rand::weak_rng(),
cost_function: cost_function,
}
}
}
#[derive(Clone)]
struct Individual {
pos: Vec<f32>,
cost: Option<f32>,
cr: f32,
f: f32,
}
pub struct Population<F, R>
where F: Fn(&[f32]) -> f32,
R: rand::Rng
{
curr: Vec<Individual>,
best: Vec<Individual>,
settings: Settings<F, R>,
best_idx: Option<usize>,
best_cost_cache: Option<f32>,
num_cost_evaluations: usize,
dim: usize,
between_popsize: Range<usize>,
between_dim: Range<usize>,
between_cr: Range<f32>,
between_f: Range<f32>,
pop_countdown: usize,
}
pub fn self_adaptive_de<F>(min_max_pos: Vec<(f32, f32)>,
cost_function: F)
-> Population<F, rand::XorShiftRng>
where F: Fn(&[f32]) -> f32
{
Population::new(Settings::default(min_max_pos, cost_function))
}
impl<F, R> Population<F, R>
where F: Fn(&[f32]) -> f32,
R: rand::Rng
{
pub fn new(s: Settings<F, R>) -> Population<F, R> {
assert!(s.min_max_pos.len() >= 1,
"need at least one element to optimize");
let dim = s.min_max_pos.len();
let dummy_individual = Individual {
pos: vec![0.0; dim],
cost: None,
cr: 0.0,
f: 0.0,
};
let mut pop = Population {
curr: vec![dummy_individual.clone(); s.pop_size],
best: vec![dummy_individual; s.pop_size],
best_idx: None,
best_cost_cache: None,
num_cost_evaluations: 0,
dim: dim,
pop_countdown: s.pop_size,
between_popsize: Range::new(0, s.pop_size),
between_dim: Range::new(0, dim),
between_cr: Range::new(s.cr_min_max.0, s.cr_min_max.1),
between_f: Range::new(s.f_min_max.0, s.f_min_max.1),
settings: s,
};
for ind in &mut pop.curr {
ind.cr = pop.between_cr.ind_sample(&mut pop.settings.rng);
ind.f = pop.between_f.ind_sample(&mut pop.settings.rng);
for d in 0..dim {
let between_min_max = Range::new(pop.settings.min_max_pos[d].0,
pop.settings.min_max_pos[d].1);
ind.pos[d] = between_min_max.ind_sample(&mut pop.settings.rng);
}
}
pop
}
fn update_best(&mut self) {
for i in 0..self.curr.len() {
let curr = &mut self.curr[i];
let best = &mut self.best[i];
if best.cost.is_none() || curr.cost.unwrap() <= best.cost.unwrap() {
std::mem::swap(curr, best);
}
}
}
fn update_positions(&mut self) {
let rng = &mut self.settings.rng;
for i in 0..self.curr.len() {
let id1 = self.between_popsize.ind_sample(rng);
let mut id2 = self.between_popsize.ind_sample(rng);
while id2 == id1 {
id2 = self.between_popsize.ind_sample(rng);
}
let mut id3 = self.between_popsize.ind_sample(rng);
while id3 == id1 || id3 == id2 {
id3 = self.between_popsize.ind_sample(rng);
}
let curr = &mut self.curr[i];
let best = &self.best[i];
if rng.gen::<f32>() < self.settings.cr_change_probability {
curr.cr = self.between_cr.ind_sample(rng);
} else {
curr.cr = best.cr;
}
if rng.gen::<f32>() < self.settings.f_change_probability {
curr.f = self.between_f.ind_sample(rng);
} else {
curr.f = best.f;
}
let curr_pos = &mut curr.pos;
let best_pos = &best.pos;
let best1_pos = &self.best[id1].pos;
let best2_pos = &self.best[id2].pos;
let best3_pos = &self.best[id3].pos;
let forced_mutation_dim = self.between_dim.ind_sample(rng);
for d in 0..self.dim {
if d == forced_mutation_dim || rng.gen::<f32>() < curr.cr {
curr_pos[d] = best3_pos[d] + curr.f * (best1_pos[d] - best2_pos[d]);
} else {
curr_pos[d] = best_pos[d];
}
}
curr.cost = None;
}
}
pub fn best(&self) -> Option<(f32, &[f32])> {
if let Some(bi) = self.best_idx {
let curr = &self.curr[bi];
let best = &self.best[bi];
if curr.cost.is_none() {
return Some((best.cost.unwrap(), &best.pos));
}
if best.cost.is_none() {
return Some((curr.cost.unwrap(), &curr.pos));
}
if curr.cost.unwrap() < best.cost.unwrap() {
return Some((curr.cost.unwrap(), &curr.pos));
}
return Some((best.cost.unwrap(), &best.pos));
} else {
None
}
}
pub fn num_cost_evaluations(&self) -> usize {
self.num_cost_evaluations
}
pub fn eval(&mut self) -> Option<f32> {
if 0 == self.pop_countdown {
self.update_best();
self.update_positions();
self.pop_countdown = self.curr.len();
}
self.pop_countdown -= 1;
let curr = &mut self.curr[self.pop_countdown];
let cost = (self.settings.cost_function)(&curr.pos);
curr.cost = Some(cost);
self.num_cost_evaluations += 1;
if self.best_cost_cache.is_none() || cost < self.best_cost_cache.unwrap() {
self.best_cost_cache = Some(cost);
self.best_idx = Some(self.pop_countdown);
}
self.best_cost_cache
}
pub fn iter(&mut self) -> PopIter<F, R> {
PopIter { pop: self }
}
}
pub struct PopIter<'a, F, R>
where F: 'a + Fn(&[f32]) -> f32,
R: 'a + rand::Rng
{
pop: &'a mut Population<F, R>,
}
impl<'a, F, R> Iterator for PopIter<'a, F, R>
where F: 'a + Fn(&[f32]) -> f32,
R: 'a + rand::Rng
{
type Item = f32;
fn next(&mut self) -> Option<Self::Item> {
self.pop.eval()
}
}
#[cfg(test)]
mod tests {
}