use std::fmt::Debug;
use solverforge_core::domain::PlanningSolution;
use super::Acceptor;
pub struct TabuSearchAcceptor<S: PlanningSolution> {
tabu_size: usize,
tabu_list: Vec<S::Score>,
aspiration_enabled: bool,
best_score: Option<S::Score>,
}
impl<S: PlanningSolution> Debug for TabuSearchAcceptor<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TabuSearchAcceptor")
.field("tabu_size", &self.tabu_size)
.field("tabu_list_len", &self.tabu_list.len())
.field("aspiration_enabled", &self.aspiration_enabled)
.finish()
}
}
impl<S: PlanningSolution> Clone for TabuSearchAcceptor<S> {
fn clone(&self) -> Self {
Self {
tabu_size: self.tabu_size,
tabu_list: self.tabu_list.clone(),
aspiration_enabled: self.aspiration_enabled,
best_score: self.best_score,
}
}
}
impl<S: PlanningSolution> TabuSearchAcceptor<S> {
pub fn new(tabu_size: usize) -> Self {
assert!(tabu_size > 0, "tabu_size must be > 0, got 0");
Self {
tabu_size,
tabu_list: Vec::with_capacity(tabu_size),
aspiration_enabled: true,
best_score: None,
}
}
pub fn without_aspiration(tabu_size: usize) -> Self {
assert!(tabu_size > 0, "tabu_size must be > 0, got 0");
Self {
tabu_size,
tabu_list: Vec::with_capacity(tabu_size),
aspiration_enabled: false,
best_score: None,
}
}
fn is_tabu(&self, score: &S::Score) -> bool {
self.tabu_list.iter().any(|s| s == score)
}
fn add_to_tabu(&mut self, score: S::Score) {
if self.tabu_list.len() >= self.tabu_size {
self.tabu_list.remove(0);
}
self.tabu_list.push(score);
}
}
impl<S: PlanningSolution> Default for TabuSearchAcceptor<S> {
fn default() -> Self {
Self::new(7) }
}
impl<S: PlanningSolution> Acceptor<S> for TabuSearchAcceptor<S> {
fn is_accepted(&mut self, last_step_score: &S::Score, move_score: &S::Score) -> bool {
if self.aspiration_enabled {
if let Some(best) = &self.best_score {
if move_score > best {
return true; }
}
}
if self.is_tabu(move_score) {
return false;
}
if move_score > last_step_score {
return true;
}
if move_score >= last_step_score {
return true;
}
false
}
fn phase_started(&mut self, initial_score: &S::Score) {
self.tabu_list.clear();
self.best_score = Some(*initial_score);
}
fn phase_ended(&mut self) {
self.tabu_list.clear();
}
fn step_ended(&mut self, step_score: &S::Score) {
self.add_to_tabu(*step_score);
if let Some(best) = &self.best_score {
if step_score > best {
self.best_score = Some(*step_score);
}
} else {
self.best_score = Some(*step_score);
}
}
}