@@ 12,6 12,132 @@ pub trait TerminatingCondition<TInput, TResult>
) -> bool;
}
+// Generic terminating conditions that group multiple conditions
+pub struct AndTerminatingConditions<'a, TInput, TResult> {
+ terminating_conditions: Vec<&'a mut dyn TerminatingCondition<TInput, TResult>>
+}
+
+impl<'a, TInput, TResult> AndTerminatingConditions<'a, TInput, TResult> {
+ pub fn new(terminating_conditions: Vec<&'a mut dyn TerminatingCondition<TInput, TResult>>) -> Self {
+ Self {
+ terminating_conditions
+ }
+ }
+
+ pub fn conditions(&self) -> &Vec<&'a mut dyn TerminatingCondition<TInput, TResult>> {
+ &self.terminating_conditions
+ }
+}
+
+impl<'a, TInput, TResult> TerminatingCondition<TInput, TResult> for AndTerminatingConditions<'a, TInput, TResult> {
+ fn should_terminate(
+ self: &mut Self,
+ candidate: &LocalSearchCandidate<TInput, TResult>,
+ stats: &LocalSearchStats<TInput, TResult>,
+ cycle: usize
+ ) -> bool {
+ return self.terminating_conditions.iter_mut()
+ .all(
+ |cond| cond.should_terminate(candidate, stats, cycle)
+ )
+ }
+}
+
+pub struct OrTerminatingConditions<'a, TInput, TResult> {
+ terminating_conditions: Vec<&'a mut dyn TerminatingCondition<TInput, TResult>>
+}
+
+impl<'a, TInput, TResult> OrTerminatingConditions<'a, TInput, TResult> {
+ pub fn new(terminating_conditions: Vec<&'a mut dyn TerminatingCondition<TInput, TResult>>) -> Self {
+ Self {
+ terminating_conditions
+ }
+ }
+
+ pub fn conditions(&self) -> &Vec<&'a mut dyn TerminatingCondition<TInput, TResult>> {
+ &self.terminating_conditions
+ }
+}
+
+impl<'a, TInput, TResult> TerminatingCondition<TInput, TResult> for OrTerminatingConditions<'a, TInput, TResult> {
+ fn should_terminate(
+ self: &mut Self,
+ candidate: &LocalSearchCandidate<TInput, TResult>,
+ stats: &LocalSearchStats<TInput, TResult>,
+ cycle: usize
+ ) -> bool {
+ return self.terminating_conditions.iter_mut()
+ .any(
+ |cond| cond.should_terminate(candidate, stats, cycle)
+ )
+ }
+}
+
+// Simple terminating conditions based on the number of cycles
+pub struct NoBetterForCyclesTerminatingCondition {
+ cycles: usize
+}
+
+impl NoBetterForCyclesTerminatingCondition {
+ pub fn new(cycles: usize) -> Self {
+ Self {
+ cycles
+ }
+ }
+}
+
+impl<TInput, TResult> TerminatingCondition<TInput, TResult> for NoBetterForCyclesTerminatingCondition {
+ fn should_terminate (
+ self: &mut Self,
+ candidate: &LocalSearchCandidate<TInput, TResult>,
+ _: &LocalSearchStats<TInput, TResult>,
+ cycle: usize
+ ) -> bool {
+ (cycle - candidate.cycle) > self.cycles
+ }
+}
+
+pub struct MaximumCyclesTerminatingCondition {
+ cycles: usize
+}
+
+impl MaximumCyclesTerminatingCondition {
+ pub fn new(cycles: usize) -> Self {
+ Self {
+ cycles
+ }
+ }
+}
+
+impl<TInput, TResult> TerminatingCondition<TInput, TResult> for MaximumCyclesTerminatingCondition {
+ fn should_terminate (
+ self: &mut Self,
+ _: &LocalSearchCandidate<TInput, TResult>,
+ _: &LocalSearchStats<TInput, TResult>,
+ cycle: usize
+ ) -> bool {
+ cycle > self.cycles
+ }
+}
+
+// Terminating conditions based on the absolute fitness of the solution
+pub struct LowerThanTerminatingCondition<T: PartialOrd> {
+ target: T
+}
+
+impl<TInput, TResult: PartialOrd> TerminatingCondition<TInput, TResult> for LowerThanTerminatingCondition<TResult> {
+ fn should_terminate(
+ self: &mut Self,
+ candidate: &LocalSearchCandidate<TInput, TResult>,
+ _: &LocalSearchStats<TInput, TResult>,
+ _: usize
+ ) -> bool {
+ candidate.fit < self.target
+ }
+}
+
+// The following conditions are mainly meant for test environment where the optimal result is known
+// beforehand.
pub struct EqualTerminatingCondition<T> {
target: T,
remember_match: bool,
@@ 114,51 240,108 @@ where
}
}
-pub struct NoBetterForCyclesTerminatingCondition {
- cycles: usize
-}
+#[cfg(test)]
+pub mod tests {
+ use crate::local_search::{LocalSearchCandidate, LocalSearchStats};
-impl NoBetterForCyclesTerminatingCondition {
- pub fn new(cycles: usize) -> Self {
- Self {
- cycles
- }
+ use super::{AndTerminatingConditions, EqualTerminatingCondition, NoBetterForCyclesTerminatingCondition, TerminatingCondition};
+
+ #[test]
+ fn test_no_better_for_cycles() {
+ let mut no_better_for_cycles = NoBetterForCyclesTerminatingCondition::new(10);
+
+ let stats = LocalSearchStats::new();
+
+ assert!(!no_better_for_cycles.should_terminate(&LocalSearchCandidate::new((), (), 0), &stats, 0));
+
+ assert!(!no_better_for_cycles.should_terminate(&LocalSearchCandidate::new((), (), 0), &stats, 1));
+ assert!(!no_better_for_cycles.should_terminate(&LocalSearchCandidate::new((), (), 0), &stats, 2));
+ assert!(!no_better_for_cycles.should_terminate(&LocalSearchCandidate::new((), (), 0), &stats, 10));
+ assert!(no_better_for_cycles.should_terminate(&LocalSearchCandidate::new((), (), 0), &stats, 11));
+
+ assert!(!no_better_for_cycles.should_terminate(&LocalSearchCandidate::new((), (), 10), &stats, 10));
+ assert!(!no_better_for_cycles.should_terminate(&LocalSearchCandidate::new((), (), 10), &stats, 20));
+ assert!(no_better_for_cycles.should_terminate(&LocalSearchCandidate::new((), (), 10), &stats, 22));
+
+ assert!(no_better_for_cycles.should_terminate(&LocalSearchCandidate::new((), (), 50), &stats, 126));
}
-}
-pub struct AndTerminatingConditions<'a, TInput, TResult> {
- terminating_conditions: Vec<&'a mut dyn TerminatingCondition<TInput, TResult>>
-}
+ #[test]
+ fn test_equal() {
+ let mut equal = EqualTerminatingCondition::new(10);
+ let stats = LocalSearchStats::new();
+ assert!(!equal.should_terminate(&LocalSearchCandidate::new((), 5, 0), &stats, 0));
+ assert!(equal.should_terminate(&LocalSearchCandidate::new((), 10, 0), &stats, 0));
+ assert!(!equal.should_terminate(&LocalSearchCandidate::new((), 11, 0), &stats, 0));
+ }
-impl<'a, TInput, TResult> AndTerminatingConditions<'a, TInput, TResult> {
- pub fn new(terminating_conditions: Vec<&'a mut dyn TerminatingCondition<TInput, TResult>>) -> Self {
- Self {
- terminating_conditions
+ #[test]
+ fn test_equal_remembering() {
+ let mut equal = EqualTerminatingCondition::new_remembered(10);
+ let stats = LocalSearchStats::new();
+ assert!(!equal.should_terminate(&LocalSearchCandidate::new((), 5, 0), &stats, 0));
+ assert!(equal.should_terminate(&LocalSearchCandidate::new((), 10, 0), &stats, 0));
+ assert!(equal.should_terminate(&LocalSearchCandidate::new((), 11, 0), &stats, 0));
+
+ equal.reset_match();
+ assert!(!equal.should_terminate(&LocalSearchCandidate::new((), 11, 0), &stats, 0));
+ assert!(equal.should_terminate(&LocalSearchCandidate::new((), 10, 0), &stats, 0));
+ }
+
+ struct DummyTerminatingCondition {
+ idx: usize
+ }
+
+ impl DummyTerminatingCondition {
+ fn new(idx: usize) -> Self {
+ Self {
+ idx
+ }
}
}
-}
-impl<'a, TInput, TResult> TerminatingCondition<TInput, TResult> for AndTerminatingConditions<'a, TInput, TResult> {
- fn should_terminate(
- self: &mut Self,
- candidate: &LocalSearchCandidate<TInput, TResult>,
- stats: &LocalSearchStats<TInput, TResult>,
- cycle: usize
- ) -> bool {
- return self.terminating_conditions.iter_mut()
- .all(
- |cond| cond.should_terminate(candidate, stats, cycle)
- )
+ impl TerminatingCondition<Vec<bool>, ()> for DummyTerminatingCondition {
+ fn should_terminate(
+ self: &mut Self,
+ candidate: &crate::local_search::LocalSearchCandidate<Vec<bool>, ()>,
+ _: &crate::local_search::LocalSearchStats<Vec<bool>, ()>,
+ _: usize
+ ) -> bool {
+ candidate.pos[self.idx]
+ }
}
-}
-impl<TInput, TResult> TerminatingCondition<TInput, TResult> for NoBetterForCyclesTerminatingCondition {
- fn should_terminate (
- self: &mut Self,
- candidate: &LocalSearchCandidate<TInput, TResult>,
- _: &LocalSearchStats<TInput, TResult>,
- cycle: usize
- ) -> bool {
- (cycle - candidate.cycle) > self.cycles
+ #[test]
+ fn test_and() {
+ let mut first_dummy = DummyTerminatingCondition::new(0);
+ let mut second_dummy = DummyTerminatingCondition::new(1);
+ let mut and_cond = AndTerminatingConditions::<Vec<bool>, ()>::new(vec![&mut first_dummy, &mut second_dummy]);
+ let stats = LocalSearchStats::new();
+
+ let mut terminates = vec![false, false];
+
+ assert_eq!(and_cond.conditions().len(), 2);
+
+ assert!(!and_cond.should_terminate(&LocalSearchCandidate::new((), terminates.clone(), 0), &stats, 0));
+
+ terminates[0] = true;
+ terminates[1] = true;
+
+ assert!(and_cond.should_terminate(&LocalSearchCandidate::new((), terminates.clone(), 0), &stats, 0));
+
+ terminates[0] = true;
+ terminates[1] = false;
+
+ assert!(!and_cond.should_terminate(&LocalSearchCandidate::new((), terminates.clone(), 0), &stats, 0));
+
+ terminates[0] = false;
+ terminates[1] = true;
+
+ assert!(!and_cond.should_terminate(&LocalSearchCandidate::new((), terminates.clone(), 0), &stats, 0));
+
+ terminates[0] = false;
+ terminates[1] = false;
+
+ assert!(!and_cond.should_terminate(&LocalSearchCandidate::new((), terminates.clone(), 0), &stats, 0));
}
}