use std::time::{Duration, SystemTime};
pub mod algorithms;
pub mod selectors;
pub mod termination;
#[cfg(test)]
mod test;
pub trait Evaluate {
fn evaluate(&self) -> f32;
}
pub trait Operator {
type Solution: Evaluate;
#[allow(unused_variables)]
fn construct_neighborhood(
&self,
solution: Self::Solution,
) -> Box<dyn Iterator<Item = Self::Solution>> {
todo!()
}
fn find_best_neighbor(&self, solution: Self::Solution) -> Self::Solution {
let mut winner;
let mut iterator = self.construct_neighborhood(solution);
if let Some(x) = iterator.next() {
winner = x
} else {
panic!("neighborhood was empty")
}
for neighbor in iterator {
if neighbor.evaluate() < winner.evaluate() {
winner = neighbor;
}
}
winner
}
#[allow(unused_variables)]
fn shake(&self, solution: Self::Solution, rng: &mut dyn rand::RngCore) -> Self::Solution {
todo!()
}
}
pub struct Outcome<T> {
solution: T,
duration: std::time::Duration,
}
pub trait ImprovingHeuristic<Solution> {
fn propose_candidate(&self, incumbent: Solution) -> Solution
where
Solution: Evaluate;
fn accept_candidate(&self, candidate: &Solution, incumbent: &Solution) -> bool
where
Solution: Evaluate;
fn should_terminate(&self, incumbent: &Solution) -> bool;
fn optimize(self, initial: Solution) -> Solution
where
Solution: Clone + Evaluate,
Self: Sized,
{
let mut incumbent = initial;
let mut best_solution = incumbent.clone();
loop {
let candidate = self.propose_candidate(incumbent.clone());
if candidate.evaluate() < best_solution.evaluate() {
self.callback_candidate_improved_best(&candidate, &incumbent);
best_solution = candidate.clone();
}
if self.accept_candidate(&candidate, &incumbent) {
self.callback_candidate_accepted(&candidate, &incumbent);
incumbent = candidate;
} else {
self.callback_candidate_rejected(&candidate, &incumbent);
}
if self.should_terminate(&incumbent) {
break;
}
}
best_solution
}
#[allow(unused_variables)]
fn callback_candidate_improved_best(&self, candidate: &Solution, incumbent: &Solution) {}
#[allow(unused_variables)]
fn callback_candidate_accepted(&self, candidate: &Solution, incumbent: &Solution) {}
#[allow(unused_variables)]
fn callback_candidate_rejected(&self, candidate: &Solution, incumbent: &Solution) {}
fn optimize_timed(self, solution: Solution) -> Outcome<Solution>
where
Solution: Clone + Evaluate,
Self: Sized,
{
let now = SystemTime::now();
let solution = self.optimize(solution);
let duration = now.elapsed().expect("failed to time for duration");
let outcome = Outcome { duration, solution };
outcome
}
}
pub enum ProposalEvaluation {
ImprovedBest,
Accept,
Reject,
}
impl<T> Outcome<T> {
pub fn new(solution: T, duration: Duration) -> Self {
Self { solution, duration }
}
pub fn solution(&self) -> &T {
&self.solution
}
pub fn duration(&self) -> Duration {
self.duration
}
}