use crate::core::energy::Energy;
use crate::core::schedule::Schedule;
use crate::core::state::State;
use crate::core::transition;
use rand::rngs::StdRng;
use std::fmt;
#[derive(Clone)]
pub struct AnnealingResult<S: State> {
pub best_state: S,
pub best_energy: f64,
pub final_state: S,
pub final_energy: f64,
pub iterations: usize,
pub accepted_moves: usize,
pub rejected_moves: usize,
pub initial_temp: f64,
pub final_temp: f64,
}
impl<S: State> fmt::Debug for AnnealingResult<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AnnealingResult")
.field("best_energy", &self.best_energy)
.field("final_energy", &self.final_energy)
.field("iterations", &self.iterations)
.field("accepted_moves", &self.accepted_moves)
.field("rejected_moves", &self.rejected_moves)
.field(
"acceptance_ratio",
&(self.accepted_moves as f64 / self.iterations as f64),
)
.field("initial_temp", &self.initial_temp)
.field("final_temp", &self.final_temp)
.finish()
}
}
pub struct Annealer<S, E, Sch>
where
S: State,
E: Energy<State = S>,
Sch: Schedule,
{
pub state: S,
pub energy: E,
pub schedule: Sch,
pub rng: StdRng,
pub max_iters: usize,
best_state: Option<S>,
best_energy: f64,
collect_stats: bool,
accepted_moves: usize,
rejected_moves: usize,
}
impl<S, E, Sch> Annealer<S, E, Sch>
where
S: State,
E: Energy<State = S>,
Sch: Schedule,
{
pub fn new(initial_state: S, energy: E, schedule: Sch, rng: StdRng, max_iters: usize) -> Self {
let initial_energy = energy.cost(&initial_state);
Self {
state: initial_state,
energy,
schedule,
rng,
max_iters,
best_state: None,
best_energy: initial_energy,
collect_stats: false,
accepted_moves: 0,
rejected_moves: 0,
}
}
pub fn with_stats(mut self) -> Self {
self.collect_stats = true;
self
}
pub fn run(&mut self) -> (S, f64) {
let result = self.run_with_stats();
(result.best_state, result.best_energy)
}
pub fn run_with_stats(&mut self) -> AnnealingResult<S> {
let initial_temp = self.schedule.initial_temp();
let mut current_temp = initial_temp;
let mut current_energy = self.energy.cost(&self.state);
self.best_state = Some(self.state.clone());
self.best_energy = current_energy;
self.accepted_moves = 0;
self.rejected_moves = 0;
for i in 0..self.max_iters {
let new_state = self.state.neighbor(&mut self.rng);
let new_energy = self.energy.cost(&new_state);
let delta = new_energy - current_energy;
if transition::accept(delta, current_temp, &mut self.rng) {
self.state = new_state;
current_energy = new_energy;
self.accepted_moves += 1;
if new_energy < self.best_energy {
self.best_state = Some(self.state.clone());
self.best_energy = new_energy;
}
} else {
self.rejected_moves += 1;
}
current_temp = self.schedule.next_temp(current_temp, i);
}
AnnealingResult {
best_state: self.best_state.as_ref().unwrap().clone(),
best_energy: self.best_energy,
final_state: self.state.clone(),
final_energy: current_energy,
iterations: self.max_iters,
accepted_moves: self.accepted_moves,
rejected_moves: self.rejected_moves,
initial_temp,
final_temp: current_temp,
}
}
}