use std::fmt::Debug;
use solverforge_config::AcceptorConfig;
use solverforge_core::domain::PlanningSolution;
use solverforge_core::score::{ParseableScore, Score};
use crate::phase::localsearch::{
Acceptor, GreatDelugeAcceptor, HillClimbingAcceptor, LateAcceptanceAcceptor,
SimulatedAnnealingAcceptor, TabuSearchAcceptor,
};
#[allow(clippy::large_enum_variant)]
pub enum AnyAcceptor<S: PlanningSolution> {
HillClimbing(HillClimbingAcceptor),
TabuSearch(TabuSearchAcceptor<S>),
SimulatedAnnealing(SimulatedAnnealingAcceptor),
LateAcceptance(LateAcceptanceAcceptor<S>),
GreatDeluge(GreatDelugeAcceptor<S>),
}
impl<S: PlanningSolution> Debug for AnyAcceptor<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::HillClimbing(a) => write!(f, "AnyAcceptor::HillClimbing({a:?})"),
Self::TabuSearch(a) => write!(f, "AnyAcceptor::TabuSearch({a:?})"),
Self::SimulatedAnnealing(a) => write!(f, "AnyAcceptor::SimulatedAnnealing({a:?})"),
Self::LateAcceptance(a) => write!(f, "AnyAcceptor::LateAcceptance({a:?})"),
Self::GreatDeluge(a) => write!(f, "AnyAcceptor::GreatDeluge({a:?})"),
}
}
}
impl<S: PlanningSolution> Clone for AnyAcceptor<S>
where
S::Score: Clone,
{
fn clone(&self) -> Self {
match self {
Self::HillClimbing(a) => Self::HillClimbing(a.clone()),
Self::TabuSearch(a) => Self::TabuSearch(a.clone()),
Self::SimulatedAnnealing(a) => Self::SimulatedAnnealing(a.clone()),
Self::LateAcceptance(a) => Self::LateAcceptance(a.clone()),
Self::GreatDeluge(a) => Self::GreatDeluge(a.clone()),
}
}
}
impl<S: PlanningSolution> Acceptor<S> for AnyAcceptor<S>
where
S::Score: Score,
{
fn is_accepted(&mut self, last_step_score: &S::Score, move_score: &S::Score) -> bool {
match self {
Self::HillClimbing(a) => Acceptor::<S>::is_accepted(a, last_step_score, move_score),
Self::TabuSearch(a) => Acceptor::<S>::is_accepted(a, last_step_score, move_score),
Self::SimulatedAnnealing(a) => {
Acceptor::<S>::is_accepted(a, last_step_score, move_score)
}
Self::LateAcceptance(a) => Acceptor::<S>::is_accepted(a, last_step_score, move_score),
Self::GreatDeluge(a) => Acceptor::<S>::is_accepted(a, last_step_score, move_score),
}
}
fn phase_started(&mut self, initial_score: &S::Score) {
match self {
Self::HillClimbing(a) => Acceptor::<S>::phase_started(a, initial_score),
Self::TabuSearch(a) => Acceptor::<S>::phase_started(a, initial_score),
Self::SimulatedAnnealing(a) => Acceptor::<S>::phase_started(a, initial_score),
Self::LateAcceptance(a) => Acceptor::<S>::phase_started(a, initial_score),
Self::GreatDeluge(a) => Acceptor::<S>::phase_started(a, initial_score),
}
}
fn phase_ended(&mut self) {
match self {
Self::HillClimbing(a) => Acceptor::<S>::phase_ended(a),
Self::TabuSearch(a) => Acceptor::<S>::phase_ended(a),
Self::SimulatedAnnealing(a) => Acceptor::<S>::phase_ended(a),
Self::LateAcceptance(a) => Acceptor::<S>::phase_ended(a),
Self::GreatDeluge(a) => Acceptor::<S>::phase_ended(a),
}
}
fn step_started(&mut self) {
match self {
Self::HillClimbing(a) => Acceptor::<S>::step_started(a),
Self::TabuSearch(a) => Acceptor::<S>::step_started(a),
Self::SimulatedAnnealing(a) => Acceptor::<S>::step_started(a),
Self::LateAcceptance(a) => Acceptor::<S>::step_started(a),
Self::GreatDeluge(a) => Acceptor::<S>::step_started(a),
}
}
fn step_ended(&mut self, step_score: &S::Score) {
match self {
Self::HillClimbing(a) => Acceptor::<S>::step_ended(a, step_score),
Self::TabuSearch(a) => Acceptor::<S>::step_ended(a, step_score),
Self::SimulatedAnnealing(a) => Acceptor::<S>::step_ended(a, step_score),
Self::LateAcceptance(a) => Acceptor::<S>::step_ended(a, step_score),
Self::GreatDeluge(a) => Acceptor::<S>::step_ended(a, step_score),
}
}
}
pub struct AcceptorBuilder;
impl AcceptorBuilder {
pub fn build<S: PlanningSolution>(config: &AcceptorConfig) -> AnyAcceptor<S>
where
S::Score: Score + ParseableScore,
{
Self::build_with_seed(config, None)
}
pub fn build_with_seed<S: PlanningSolution>(
config: &AcceptorConfig,
random_seed: Option<u64>,
) -> AnyAcceptor<S>
where
S::Score: Score + ParseableScore,
{
match config {
AcceptorConfig::HillClimbing => AnyAcceptor::HillClimbing(HillClimbingAcceptor::new()),
AcceptorConfig::TabuSearch(tabu_config) => {
let tabu_size = tabu_config
.entity_tabu_size
.or(tabu_config.move_tabu_size)
.unwrap_or(7);
AnyAcceptor::TabuSearch(TabuSearchAcceptor::<S>::new(tabu_size))
}
AcceptorConfig::SimulatedAnnealing(sa_config) => {
let starting_temp = sa_config.starting_temperature.as_ref().map(|s| {
s.parse::<f64>()
.ok()
.or_else(|| S::Score::parse(s).ok().map(|score| score.to_scalar().abs()))
.unwrap_or_else(|| {
panic!("Invalid starting_temperature '{}': expected scalar or score string", s)
})
});
AnyAcceptor::SimulatedAnnealing(match (starting_temp, random_seed) {
(Some(temp), Some(seed)) => {
SimulatedAnnealingAcceptor::with_seed(temp, 0.999985, seed)
}
(Some(temp), None) => SimulatedAnnealingAcceptor::new(temp, 0.999985),
(None, Some(seed)) => {
SimulatedAnnealingAcceptor::auto_calibrate_with_seed(0.999985, seed)
}
(None, None) => SimulatedAnnealingAcceptor::auto_calibrate(0.999985),
})
}
AcceptorConfig::LateAcceptance(la_config) => {
let size = la_config.late_acceptance_size.unwrap_or(400);
AnyAcceptor::LateAcceptance(LateAcceptanceAcceptor::<S>::new(size))
}
AcceptorConfig::GreatDeluge(gd_config) => {
let rain_speed = gd_config.water_level_increase_ratio.unwrap_or(0.001);
AnyAcceptor::GreatDeluge(GreatDelugeAcceptor::<S>::new(rain_speed))
}
}
}
pub fn hill_climbing<S: PlanningSolution>() -> HillClimbingAcceptor {
HillClimbingAcceptor::new()
}
pub fn tabu_search<S: PlanningSolution>(tabu_size: usize) -> TabuSearchAcceptor<S> {
TabuSearchAcceptor::<S>::new(tabu_size)
}
pub fn simulated_annealing(starting_temp: f64, decay_rate: f64) -> SimulatedAnnealingAcceptor {
SimulatedAnnealingAcceptor::new(starting_temp, decay_rate)
}
pub fn late_acceptance<S: PlanningSolution>(size: usize) -> LateAcceptanceAcceptor<S> {
LateAcceptanceAcceptor::<S>::new(size)
}
}
#[cfg(test)]
#[path = "acceptor_tests.rs"]
mod tests;