From 37191dc4ab506048322e3c4d10f3ad7d00638270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=9C=20=E4=B8=96=E6=A9=8B=20Du=20Shiqiao?= Date: Sun, 8 Sep 2024 11:43:47 +0900 Subject: [PATCH 1/2] feat!: tabu list implementation to use associated types --- examples/tsp_model.rs | 5 +++-- src/optim/tabu_search.rs | 39 +++++++++++++++++++---------------- src/tests/test_tabu_search.rs | 5 +++-- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/examples/tsp_model.rs b/examples/tsp_model.rs index b141350..a2c0ab7 100644 --- a/examples/tsp_model.rs +++ b/examples/tsp_model.rs @@ -184,7 +184,8 @@ impl Default for DequeTabuList { } } -impl TabuList for DequeTabuList { +impl TabuList for DequeTabuList { + type Item = TransitionType; fn contains(&self, transition: &TransitionType) -> bool { let (_, inserted_edges) = transition; inserted_edges @@ -285,7 +286,7 @@ fn main() { pb.reset(); println!("run tabu search"); - let optimizer = TabuSearchOptimizer::::new(patience, 200, 10, 20); + let optimizer = TabuSearchOptimizer::::new(patience, 200, 10, 20); let (final_solution, final_score) = optimizer .run_with_callback( &tsp_model, diff --git a/src/optim/tabu_search.rs b/src/optim/tabu_search.rs index d878771..575a782 100644 --- a/src/optim/tabu_search.rs +++ b/src/optim/tabu_search.rs @@ -10,35 +10,37 @@ use crate::{ use super::LocalSearchOptimizer; /// Trait that a tabu list must satisfies -pub trait TabuList: Default { +pub trait TabuList: Default { + /// The type of item stored in the tabu list. + type Item: Clone + Sync + Send; + /// Set the length of the tabu list fn set_size(&mut self, n: usize); /// Check if the item is a Tabu - fn contains(&self, transition: &M::TransitionType) -> bool; + fn contains(&self, transition: &Self::Item) -> bool; /// Append the item to the list - fn append(&mut self, transition: M::TransitionType); + fn append(&mut self, transition: Self::Item); } /// Optimizer that implements the tabu search algorithm -pub struct TabuSearchOptimizer> { +pub struct TabuSearchOptimizer { patience: usize, n_trials: usize, return_iter: usize, default_tabu_size: usize, - phantom: PhantomData<(M, T)>, + phantom: PhantomData, } -fn find_accepted_solution( - samples: Vec<(M::SolutionType, M::TransitionType, O)>, +fn find_accepted_solution( + samples: Vec<(M::SolutionType, M::TransitionType, M::ScoreType)>, tabu_list: &L, - best_score: O, -) -> Option<(M::SolutionType, M::TransitionType, O)> + best_score: M::ScoreType, +) -> Option<(M::SolutionType, M::TransitionType, M::ScoreType)> where M: OptModel, - L: TabuList, - O: Ord, + L: TabuList, { for (solution, transition, score) in samples.into_iter() { #[allow(unused_parens)] @@ -55,7 +57,7 @@ where None } -impl> TabuSearchOptimizer { +impl TabuSearchOptimizer { /// Constructor of TabuSearchOptimizer /// /// - `patience` : the optimizer will give up @@ -78,10 +80,9 @@ impl> TabuSearchOptimizer { } } -impl TabuSearchOptimizer +impl TabuSearchOptimizer where - M: OptModel, - T: TabuList, + T: TabuList, { #[allow(clippy::too_many_arguments)] /// Start optimization @@ -93,7 +94,7 @@ where /// - `time_limit`: maximum iteration time /// - `callback` : callback function that will be invoked at the end of each iteration /// - `tabu_list` : initial tabu list - fn optimize_with_tabu_list( + fn optimize_with_tabu_list>( &self, model: &M, initial_solution: M::SolutionType, @@ -132,7 +133,7 @@ where samples.sort_unstable_by_key(|(_, _, score)| *score); - let res = find_accepted_solution(samples, &tabu_list, best_score); + let res = find_accepted_solution::(samples, &tabu_list, best_score); if let Some((solution, trans, score)) = res { if score < best_score { @@ -168,7 +169,9 @@ where } } -impl> LocalSearchOptimizer for TabuSearchOptimizer { +impl> LocalSearchOptimizer + for TabuSearchOptimizer +{ #[doc = " Start optimization"] fn optimize( &self, diff --git a/src/tests/test_tabu_search.rs b/src/tests/test_tabu_search.rs index e67b7c1..3773322 100644 --- a/src/tests/test_tabu_search.rs +++ b/src/tests/test_tabu_search.rs @@ -27,7 +27,8 @@ impl Default for MyTabuList { } } -impl TabuList for MyTabuList { +impl TabuList for MyTabuList { + type Item = TransitionType; fn set_size(&mut self, n: usize) { self.buff = RingBuffer::new(n); } @@ -47,7 +48,7 @@ impl TabuList for MyTabuList { #[test] fn test() { let model = QuadraticModel::new(3, vec![2.0, 0.0, -3.5], (-10.0, 10.0)); - let opt = TabuSearchOptimizer::::new(1000, 25, 5, 10); + let opt = TabuSearchOptimizer::::new(1000, 25, 5, 10); let (final_solution, final_score) = opt .run(&model, None, 10000, Duration::from_secs(10)) .unwrap(); From 135f89fc6646f08febb494f4ea98cce8651a28df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=9C=20=E4=B8=96=E6=A9=8B=20Du=20Shiqiao?= Date: Sun, 8 Sep 2024 11:44:51 +0900 Subject: [PATCH 2/2] bump version to 0.17.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index db48aad..a0fb6d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ categories = ["algorithms"] repository = "https://github.com/lucidfrontier45/localsearch" license-file = "LICENSE" readme = "README.md" -version = "0.16.1" +version = "0.17.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html