use rand::{thread_rng, Rng};
pub mod acceptance;
pub mod cooling_schedules;
pub trait Fitness {
fn fitness(&self) -> f32;
}
pub trait Neighbours<T: Iterator<Item = Self::Neighbour>> {
type Neighbour;
fn neighbours(&self) -> T;
fn apply_neighbour(&mut self, Self::Neighbour);
fn neighbour_fitness(&self, &Self::Neighbour) -> f32;
}
pub trait Temperature<T> {
fn update(self, &T) -> Self;
fn temperature(&self) -> f32;
fn stop(&self) -> bool;
}
pub fn simulated_annealing<T, V, U, N>(
intial_solution: T,
initial_temperature: V,
acceptance: U,
) -> Option<T>
where
T: Fitness + Clone + Neighbours<N>,
V: Temperature<T>,
U: Fn(f32, f32) -> f32,
N: Iterator<Item = T::Neighbour>,
{
let mut s = intial_solution;
let mut t = initial_temperature;
let mut old_fitness = s.fitness();
let mut best_solution = s.clone();
let mut best_fitness = old_fitness;
loop {
if t.stop() {
return Some(best_solution);
}
let (new_fitness, n) = {
let mut iter = s.neighbours();
loop {
if let Some(n) = iter.next() {
let new_fitness = s.neighbour_fitness(&n);
let energy_diff = new_fitness - old_fitness;
if energy_diff < 0.0
|| acceptance(energy_diff, t.temperature())
< thread_rng().gen_range::<f32>(0.0, 1.0)
{
break (new_fitness, n);
}
} else {
return Some(best_solution);
}
}
};
s.apply_neighbour(n);
t = t.update(&s);
if new_fitness < best_fitness {
best_solution = s.clone();
best_fitness = new_fitness;
}
old_fitness = new_fitness;
}
}