From 01128355417294d7dc3eaaa9189f0954315d9922 Mon Sep 17 00:00:00 2001 From: Bubbler-4 Date: Mon, 30 Dec 2024 13:56:29 +0000 Subject: [PATCH 1/6] change Cache --- ddo/src/abstraction/cache.rs | 12 +++++------ ddo/src/abstraction/mdd.rs | 2 +- ddo/src/implementation/cache/empty.rs | 20 +++++++++---------- ddo/src/implementation/cache/simple.rs | 22 ++++++++++----------- ddo/src/implementation/solver/parallel.rs | 8 ++++---- ddo/src/implementation/solver/sequential.rs | 6 +++--- 6 files changed, 32 insertions(+), 38 deletions(-) diff --git a/ddo/src/abstraction/cache.rs b/ddo/src/abstraction/cache.rs index 75f9c9dc..f2de5d78 100644 --- a/ddo/src/abstraction/cache.rs +++ b/ddo/src/abstraction/cache.rs @@ -24,12 +24,10 @@ use crate::{Threshold, SubProblem, Problem}; /// This trait abstracts away the implementation details of the solver cache. /// That is, a Cache represents the data structure that stores thresholds that /// condition the re-exploration of nodes with a state already reached previously. -pub trait Cache { - type State; - +pub trait Cache { /// Returns true if the subproblem still must be explored, /// given the thresholds contained in the cache. - fn must_explore(&self, subproblem: &SubProblem) -> bool { + fn must_explore(&self, subproblem: &SubProblem) -> bool { let threshold = self.get_threshold(subproblem.state.as_ref(), subproblem.depth); if let Some(threshold) = threshold { subproblem.value > threshold.value || (subproblem.value == threshold.value && !threshold.explored) @@ -39,13 +37,13 @@ pub trait Cache { } /// Prepare the cache to be used with the given problem - fn initialize(&mut self, problem: &dyn Problem); + fn initialize(&mut self, problem: &dyn Problem); /// Returns the threshold currently associated with the given state, if any. - fn get_threshold(&self, state: &Self::State, depth: usize) -> Option; + fn get_threshold(&self, state: &State, depth: usize) -> Option; /// Updates the threshold associated with the given state, only if it is increased. - fn update_threshold(&self, state: Arc, depth: usize, value: isize, explored: bool); + fn update_threshold(&self, state: Arc, depth: usize, value: isize, explored: bool); /// Removes all thresholds associated with states at the given depth. fn clear_layer(&self, depth: usize); diff --git a/ddo/src/abstraction/mdd.rs b/ddo/src/abstraction/mdd.rs index 3cf13831..a1e92fcd 100644 --- a/ddo/src/abstraction/mdd.rs +++ b/ddo/src/abstraction/mdd.rs @@ -66,7 +66,7 @@ pub struct CompilationInput<'a, State> { /// The best known lower bound at the time when the dd is being compiled pub best_lb: isize, /// Data structure containing info about past compilations used to prune the search - pub cache: &'a dyn Cache, + pub cache: &'a dyn Cache, pub dominance: &'a dyn DominanceChecker, } diff --git a/ddo/src/implementation/cache/empty.rs b/ddo/src/implementation/cache/empty.rs index 1a456524..c4f068b1 100644 --- a/ddo/src/implementation/cache/empty.rs +++ b/ddo/src/implementation/cache/empty.rs @@ -30,33 +30,31 @@ use crate::*; /// Dummy implementation of Cache with no information stored at all. #[derive(Debug, Clone, Copy)] -pub struct EmptyCache { - phantom: PhantomData, +pub struct EmptyCache { + phantom: PhantomData, } -impl Default for EmptyCache { +impl Default for EmptyCache { fn default() -> Self { EmptyCache { phantom: Default::default() } } } -impl EmptyCache { +impl EmptyCache { pub fn new() -> Self { Default::default() } } -impl Cache for EmptyCache { - type State = T; - +impl Cache for EmptyCache { #[inline(always)] - fn initialize(&mut self, _: &dyn Problem) {} + fn initialize(&mut self, _: &dyn Problem) {} #[inline(always)] - fn get_threshold(&self, _: &T, _: usize) -> Option { + fn get_threshold(&self, _: &State, _: usize) -> Option { None } #[inline(always)] - fn update_threshold(&self, _: Arc, _: usize, _: isize, _: bool) {} + fn update_threshold(&self, _: Arc, _: usize, _: isize, _: bool) {} #[inline(always)] fn clear_layer(&self, _: usize) {} @@ -65,7 +63,7 @@ impl Cache for EmptyCache { fn clear(&self) {} #[inline(always)] - fn must_explore(&self, _: &SubProblem) -> bool { + fn must_explore(&self, _: &SubProblem) -> bool { true } } \ No newline at end of file diff --git a/ddo/src/implementation/cache/simple.rs b/ddo/src/implementation/cache/simple.rs index 6881c0ad..91b1c855 100644 --- a/ddo/src/implementation/cache/simple.rs +++ b/ddo/src/implementation/cache/simple.rs @@ -33,33 +33,31 @@ use crate::{Cache, Threshold}; /// Simple implementation of Cache using one hashmap for each layer, /// each protected with a read-write lock. #[derive(Debug)] -pub struct SimpleCache -where T: Hash + Eq { - thresholds_by_layer: Vec, Threshold, fxhash::FxBuildHasher>>, +pub struct SimpleCache +where State: Hash + Eq { + thresholds_by_layer: Vec, Threshold, fxhash::FxBuildHasher>>, } -impl Default for SimpleCache -where T: Hash + Eq { +impl Default for SimpleCache +where State: Hash + Eq { fn default() -> Self { Self { thresholds_by_layer: vec![] } } } -impl Cache for SimpleCache -where T: Hash + Eq { - type State = T; - - fn initialize(&mut self, problem: &dyn crate::Problem) { +impl Cache for SimpleCache +where State: Hash + Eq { + fn initialize(&mut self, problem: &dyn crate::Problem) { let nb_variables = problem.nb_variables(); for _ in 0..=nb_variables { self.thresholds_by_layer.push(Default::default()); } } - fn get_threshold(&self, state: &T, depth: usize) -> Option { + fn get_threshold(&self, state: &State, depth: usize) -> Option { self.thresholds_by_layer[depth].get(state).as_deref().copied() } - fn update_threshold(&self, state: Arc, depth: usize, value: isize, explored: bool) { + fn update_threshold(&self, state: Arc, depth: usize, value: isize, explored: bool) { self.thresholds_by_layer[depth].entry(state) .and_modify(|e| *e = Threshold { value, explored }.max(*e)) .or_insert(Threshold { value, explored }); diff --git a/ddo/src/implementation/solver/parallel.rs b/ddo/src/implementation/solver/parallel.rs index d5e5d120..5ef245e4 100644 --- a/ddo/src/implementation/solver/parallel.rs +++ b/ddo/src/implementation/solver/parallel.rs @@ -83,7 +83,7 @@ struct Critical<'a, State> { /// access to the critical data (protected by a mutex) as well as a monitor /// (condvar) to park threads in case of node-starvation. struct Shared<'a, State, C> where - C : Cache + Send + Sync + Default, + C : Cache + Send + Sync + Default, { /// A reference to the problem being solved with branch-and-bound MDD problem: &'a (dyn Problem + Send + Sync), @@ -286,7 +286,7 @@ enum WorkLoad { /// ``` pub struct ParallelSolver<'a, State, D, C> where D: DecisionDiagram + Default, - C: Cache + Send + Sync + Default, + C: Cache + Send + Sync + Default, { /// This is the shared state. Each thread is going to take a reference to it. shared: Shared<'a, State, C>, @@ -303,7 +303,7 @@ impl<'a, State, D, C> ParallelSolver<'a, State, D, C> where State: Eq + Hash + Clone, D: DecisionDiagram + Default, - C: Cache + Send + Sync + Default, + C: Cache + Send + Sync + Default, { pub fn new( problem: &'a (dyn Problem + Send + Sync), @@ -564,7 +564,7 @@ impl<'a, State, D, C> Solver for ParallelSolver<'a, State, D, C> where State: Eq + PartialEq + Hash + Clone, D: DecisionDiagram + Default, - C: Cache + Send + Sync + Default, + C: Cache + Send + Sync + Default, { /// Applies the branch and bound algorithm proposed by Bergman et al. to /// solve the problem to optimality. To do so, it spawns `nb_threads` workers diff --git a/ddo/src/implementation/solver/sequential.rs b/ddo/src/implementation/solver/sequential.rs index 153b6309..7e3c0818 100644 --- a/ddo/src/implementation/solver/sequential.rs +++ b/ddo/src/implementation/solver/sequential.rs @@ -201,7 +201,7 @@ enum WorkLoad { /// ``` pub struct SequentialSolver<'a, State, D = DefaultMDDLEL, C = EmptyCache> where D: DecisionDiagram + Default, - C: Cache + Default, + C: Cache + Default, { /// A reference to the problem being solved with branch-and-bound MDD problem: &'a (dyn Problem), @@ -258,7 +258,7 @@ impl<'a, State, D, C> SequentialSolver<'a, State, D, C> where State: Eq + Hash + Clone, D: DecisionDiagram + Default, - C: Cache + Default, + C: Cache + Default, { pub fn new( problem: &'a (dyn Problem), @@ -466,7 +466,7 @@ impl<'a, State, D, C> Solver for SequentialSolver<'a, State, D, C> where State: Eq + PartialEq + Hash + Clone, D: DecisionDiagram + Default, - C: Cache + Default, + C: Cache + Default, { /// Applies the branch and bound algorithm proposed by Bergman et al. to /// solve the problem to optimality. To do so, it spawns `nb_threads` workers From 4a39f277c903fc735fc404900e59efa7864bb987 Mon Sep 17 00:00:00 2001 From: Bubbler-4 Date: Mon, 30 Dec 2024 14:02:02 +0000 Subject: [PATCH 2/6] change DominanceChecker --- ddo/src/abstraction/dominance.rs | 8 +++----- ddo/src/implementation/dominance/empty.rs | 14 ++++++-------- ddo/src/implementation/dominance/simple.rs | 8 +++----- ddo/src/implementation/solver/parallel.rs | 6 +++--- ddo/src/implementation/solver/sequential.rs | 6 +++--- 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/ddo/src/abstraction/dominance.rs b/ddo/src/abstraction/dominance.rs index fd50a959..7b6a2cd2 100644 --- a/ddo/src/abstraction/dominance.rs +++ b/ddo/src/abstraction/dominance.rs @@ -110,18 +110,16 @@ pub struct DominanceCheckResult { pub threshold: Option, } -pub trait DominanceChecker { - type State; - +pub trait DominanceChecker { /// Removes all entries associated with states at the given depth. fn clear_layer(&self, depth: usize); /// Returns true if the state is dominated by a stored one, and a potential /// pruning threshold, and inserts the (key, value) pair otherwise - fn is_dominated_or_insert(&self, state: Arc, depth: usize, value: isize) -> DominanceCheckResult; + fn is_dominated_or_insert(&self, state: Arc, depth: usize, value: isize) -> DominanceCheckResult; /// Comparator to order states by increasing value, regardless of their key - fn cmp(&self, a: &Self::State, val_a: isize, b: &Self::State, val_b: isize) -> Ordering; + fn cmp(&self, a: &State, val_a: isize, b: &State, val_b: isize) -> Ordering; } diff --git a/ddo/src/implementation/dominance/empty.rs b/ddo/src/implementation/dominance/empty.rs index 998f811d..a0e0955b 100644 --- a/ddo/src/implementation/dominance/empty.rs +++ b/ddo/src/implementation/dominance/empty.rs @@ -21,27 +21,25 @@ use std::{marker::PhantomData, cmp::Ordering, sync::Arc}; use crate::{DominanceChecker, DominanceCheckResult}; /// Implementation of a dominance checker that never detects any dominance relation -pub struct EmptyDominanceChecker +pub struct EmptyDominanceChecker { - _phantom: PhantomData, + _phantom: PhantomData, } -impl Default for EmptyDominanceChecker { +impl Default for EmptyDominanceChecker { fn default() -> Self { Self { _phantom: Default::default() } } } -impl DominanceChecker for EmptyDominanceChecker { - type State = T; - +impl DominanceChecker for EmptyDominanceChecker { fn clear_layer(&self, _: usize) {} - fn is_dominated_or_insert(&self, _: Arc, _: usize, _: isize) -> DominanceCheckResult { + fn is_dominated_or_insert(&self, _: Arc, _: usize, _: isize) -> DominanceCheckResult { DominanceCheckResult { dominated: false, threshold: None } } - fn cmp(&self, _: &Self::State, _: isize, _: &Self::State, _: isize) -> Ordering { + fn cmp(&self, _: &State, _: isize, _: &State, _: isize) -> Ordering { Ordering::Equal } } \ No newline at end of file diff --git a/ddo/src/implementation/dominance/simple.rs b/ddo/src/implementation/dominance/simple.rs index b9a732a9..c205cdda 100644 --- a/ddo/src/implementation/dominance/simple.rs +++ b/ddo/src/implementation/dominance/simple.rs @@ -57,18 +57,16 @@ where } } -impl DominanceChecker for SimpleDominanceChecker +impl DominanceChecker for SimpleDominanceChecker where D: Dominance, D::Key: Eq + PartialEq + Hash, { - type State = D::State; - fn clear_layer(&self, depth: usize) { self.data[depth].clear(); } - fn is_dominated_or_insert(&self, state: Arc, depth: usize, value: isize) -> DominanceCheckResult { + fn is_dominated_or_insert(&self, state: Arc, depth: usize, value: isize) -> DominanceCheckResult { if let Some(key) = self.dominance.get_key(state.clone()) { match self.data[depth].entry(key) { Entry::Occupied(mut e) => { @@ -110,7 +108,7 @@ where } } - fn cmp(&self, a: &Self::State, val_a: isize, b: &Self::State, val_b: isize) -> Ordering { + fn cmp(&self, a: &D::State, val_a: isize, b: &D::State, val_b: isize) -> Ordering { self.dominance.cmp(a, val_a, b, val_b) } } diff --git a/ddo/src/implementation/solver/parallel.rs b/ddo/src/implementation/solver/parallel.rs index 5ef245e4..56860eb9 100644 --- a/ddo/src/implementation/solver/parallel.rs +++ b/ddo/src/implementation/solver/parallel.rs @@ -101,7 +101,7 @@ struct Shared<'a, State, C> where /// Data structure containing info about past compilations used to prune the search cache: C, - dominance: &'a (dyn DominanceChecker + Send + Sync), + dominance: &'a (dyn DominanceChecker + Send + Sync), /// This is the shared state data which can only be accessed within critical /// sections. Therefore, it is protected by a mutex which prevents concurrent @@ -310,7 +310,7 @@ where relaxation: &'a (dyn Relaxation + Send + Sync), ranking: &'a (dyn StateRanking + Send + Sync), width: &'a (dyn WidthHeuristic + Send + Sync), - dominance: &'a (dyn DominanceChecker + Send + Sync), + dominance: &'a (dyn DominanceChecker + Send + Sync), cutoff: &'a (dyn Cutoff + Send + Sync), fringe: &'a mut (dyn Fringe + Send + Sync), ) -> Self { @@ -322,7 +322,7 @@ where relaxation: &'a (dyn Relaxation + Send + Sync), ranking: &'a (dyn StateRanking + Send + Sync), width_heu: &'a (dyn WidthHeuristic + Send + Sync), - dominance: &'a (dyn DominanceChecker + Send + Sync), + dominance: &'a (dyn DominanceChecker + Send + Sync), cutoff: &'a (dyn Cutoff + Send + Sync), fringe: &'a mut (dyn Fringe + Send + Sync), nb_threads: usize, diff --git a/ddo/src/implementation/solver/sequential.rs b/ddo/src/implementation/solver/sequential.rs index 7e3c0818..880bc62d 100644 --- a/ddo/src/implementation/solver/sequential.rs +++ b/ddo/src/implementation/solver/sequential.rs @@ -251,7 +251,7 @@ where D: DecisionDiagram + Default, mdd: D, /// Data structure containing info about past compilations used to prune the search cache: C, - dominance: &'a (dyn DominanceChecker), + dominance: &'a (dyn DominanceChecker), } impl<'a, State, D, C> SequentialSolver<'a, State, D, C> @@ -265,7 +265,7 @@ where relaxation: &'a (dyn Relaxation), ranking: &'a (dyn StateRanking), width: &'a (dyn WidthHeuristic), - dominance: &'a (dyn DominanceChecker), + dominance: &'a (dyn DominanceChecker), cutoff: &'a (dyn Cutoff), fringe: &'a mut (dyn Fringe), ) -> Self { @@ -277,7 +277,7 @@ where relaxation: &'a (dyn Relaxation), ranking: &'a (dyn StateRanking), width_heu: &'a (dyn WidthHeuristic), - dominance: &'a (dyn DominanceChecker), + dominance: &'a (dyn DominanceChecker), cutoff: &'a (dyn Cutoff), fringe: &'a mut (dyn Fringe), ) -> Self { From db3226990394c6a34b01f5a7d5920ce3ba2a01e8 Mon Sep 17 00:00:00 2001 From: Bubbler-4 Date: Mon, 30 Dec 2024 14:03:02 +0000 Subject: [PATCH 3/6] change DominanceChecker (missed 1) --- ddo/src/abstraction/mdd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddo/src/abstraction/mdd.rs b/ddo/src/abstraction/mdd.rs index a1e92fcd..f7a8dd64 100644 --- a/ddo/src/abstraction/mdd.rs +++ b/ddo/src/abstraction/mdd.rs @@ -67,7 +67,7 @@ pub struct CompilationInput<'a, State> { pub best_lb: isize, /// Data structure containing info about past compilations used to prune the search pub cache: &'a dyn Cache, - pub dominance: &'a dyn DominanceChecker, + pub dominance: &'a dyn DominanceChecker, } /// This trait describes the operations that can be expected from an abstract From cc0c13d5c593f9ec25d055fd8b06f964f0e489de Mon Sep 17 00:00:00 2001 From: Bubbler-4 Date: Mon, 30 Dec 2024 14:05:30 +0000 Subject: [PATCH 4/6] change Fringe --- ddo/src/abstraction/fringe.rs | 8 +++----- ddo/src/implementation/fringe/no_duplicate.rs | 6 ++---- ddo/src/implementation/fringe/simple.rs | 8 +++----- ddo/src/implementation/solver/parallel.rs | 6 +++--- ddo/src/implementation/solver/sequential.rs | 6 +++--- 5 files changed, 14 insertions(+), 20 deletions(-) diff --git a/ddo/src/abstraction/fringe.rs b/ddo/src/abstraction/fringe.rs index e82cf238..406323f9 100644 --- a/ddo/src/abstraction/fringe.rs +++ b/ddo/src/abstraction/fringe.rs @@ -23,17 +23,15 @@ use crate::SubProblem; /// This trait abstracts away the implementation details of the solver fringe. /// That is, a Fringe represents the global priority queue which stores all /// the nodes remaining to explore. -pub trait Fringe { - type State; - +pub trait Fringe { /// This is how you push a node onto the fringe. - fn push(&mut self, node: SubProblem); + fn push(&mut self, node: SubProblem); /// This method yields the most promising node from the fringe. /// # Note: /// The solvers rely on the assumption that a fringe will pop nodes in /// descending upper bound order. Hence, it is a requirement for any fringe /// implementation to enforce that requirement. - fn pop(&mut self) -> Option>; + fn pop(&mut self) -> Option>; /// This method clears the fringe: it removes all nodes from the queue. fn clear(&mut self); /// Yields the length of the queue. diff --git a/ddo/src/implementation/fringe/no_duplicate.rs b/ddo/src/implementation/fringe/no_duplicate.rs index 1dce664e..ba35db7b 100644 --- a/ddo/src/implementation/fringe/no_duplicate.rs +++ b/ddo/src/implementation/fringe/no_duplicate.rs @@ -68,13 +68,11 @@ where recycle_bin: Vec, } -impl Fringe for NoDupFringe +impl Fringe for NoDupFringe where O: SubProblemRanking, O::State: Eq + Hash + Clone, { - type State = O::State; - /// Pushes one node onto the heap while ensuring that only one copy of the /// node (identified by its state) is kept in the heap. /// @@ -141,7 +139,7 @@ where /// Pops the best node out of the heap. Here, the best is defined as the /// node having the best upper bound, with the longest `value`. - fn pop(&mut self) -> Option> { + fn pop(&mut self) -> Option> { if self.is_empty() { return None; } diff --git a/ddo/src/implementation/fringe/simple.rs b/ddo/src/implementation/fringe/simple.rs index 2bba2ac4..1b2b6f3d 100644 --- a/ddo/src/implementation/fringe/simple.rs +++ b/ddo/src/implementation/fringe/simple.rs @@ -41,14 +41,12 @@ impl SimpleFringe where O: SubProblemRanking { Self{ heap: BinaryHeap::from_vec_cmp(vec![], CompareSubProblem::new(o)) } } } -impl Fringe for SimpleFringe where O: SubProblemRanking { - type State = O::State; - - fn push(&mut self, node: SubProblem) { +impl Fringe for SimpleFringe where O: SubProblemRanking { + fn push(&mut self, node: SubProblem) { self.heap.push(node) } - fn pop(&mut self) -> Option> { + fn pop(&mut self) -> Option> { self.heap.pop() } diff --git a/ddo/src/implementation/solver/parallel.rs b/ddo/src/implementation/solver/parallel.rs index 56860eb9..10b96f93 100644 --- a/ddo/src/implementation/solver/parallel.rs +++ b/ddo/src/implementation/solver/parallel.rs @@ -40,7 +40,7 @@ struct Critical<'a, State> { /// any of the nodes remaining on the fringe. As a consequence, the /// exploration can be stopped as soon as a node with an ub <= current best /// lower bound is popped. - fringe: &'a mut (dyn Fringe + Send + Sync), + fringe: &'a mut (dyn Fringe + Send + Sync), /// This is the number of nodes that are currently being explored. /// /// # Note @@ -312,7 +312,7 @@ where width: &'a (dyn WidthHeuristic + Send + Sync), dominance: &'a (dyn DominanceChecker + Send + Sync), cutoff: &'a (dyn Cutoff + Send + Sync), - fringe: &'a mut (dyn Fringe + Send + Sync), + fringe: &'a mut (dyn Fringe + Send + Sync), ) -> Self { Self::custom(problem, relaxation, ranking, width, dominance, cutoff, fringe, num_cpus::get()) } @@ -324,7 +324,7 @@ where width_heu: &'a (dyn WidthHeuristic + Send + Sync), dominance: &'a (dyn DominanceChecker + Send + Sync), cutoff: &'a (dyn Cutoff + Send + Sync), - fringe: &'a mut (dyn Fringe + Send + Sync), + fringe: &'a mut (dyn Fringe + Send + Sync), nb_threads: usize, ) -> Self { ParallelSolver { diff --git a/ddo/src/implementation/solver/sequential.rs b/ddo/src/implementation/solver/sequential.rs index 880bc62d..1bc2080f 100644 --- a/ddo/src/implementation/solver/sequential.rs +++ b/ddo/src/implementation/solver/sequential.rs @@ -227,7 +227,7 @@ where D: DecisionDiagram + Default, /// any of the nodes remaining on the fringe. As a consequence, the /// exploration can be stopped as soon as a node with an ub <= current best /// lower bound is popped. - fringe: &'a mut (dyn Fringe), + fringe: &'a mut (dyn Fringe), /// This is a counter that tracks the number of nodes that have effectively /// been explored. That is, the number of nodes that have been popped from /// the fringe, and for which a restricted and relaxed mdd have been developed. @@ -267,7 +267,7 @@ where width: &'a (dyn WidthHeuristic), dominance: &'a (dyn DominanceChecker), cutoff: &'a (dyn Cutoff), - fringe: &'a mut (dyn Fringe), + fringe: &'a mut (dyn Fringe), ) -> Self { Self::custom(problem, relaxation, ranking, width, dominance, cutoff, fringe) } @@ -279,7 +279,7 @@ where width_heu: &'a (dyn WidthHeuristic), dominance: &'a (dyn DominanceChecker), cutoff: &'a (dyn Cutoff), - fringe: &'a mut (dyn Fringe), + fringe: &'a mut (dyn Fringe), ) -> Self { SequentialSolver { problem, From ffd3b6fb455b31cd38a5ad94b4301e7d03946c63 Mon Sep 17 00:00:00 2001 From: Bubbler-4 Date: Mon, 30 Dec 2024 14:12:12 +0000 Subject: [PATCH 5/6] change DecisionDiagram --- ddo/src/abstraction/mdd.rs | 10 +++----- ddo/src/implementation/mdd/clean.rs | 26 ++++++++++----------- ddo/src/implementation/mdd/pooled.rs | 26 ++++++++++----------- ddo/src/implementation/solver/parallel.rs | 6 ++--- ddo/src/implementation/solver/sequential.rs | 6 ++--- 5 files changed, 33 insertions(+), 41 deletions(-) diff --git a/ddo/src/abstraction/mdd.rs b/ddo/src/abstraction/mdd.rs index f7a8dd64..a97a6c87 100644 --- a/ddo/src/abstraction/mdd.rs +++ b/ddo/src/abstraction/mdd.rs @@ -72,14 +72,10 @@ pub struct CompilationInput<'a, State> { /// This trait describes the operations that can be expected from an abstract /// decision diagram regardless of the way it is implemented. -pub trait DecisionDiagram { - /// This associated type corresponds to the `State` type of the problems - /// that can be solved when using this DD. - type State; - +pub trait DecisionDiagram { /// This method provokes the compilation of the DD based on the given /// compilation input (compilation type, and root subproblem) - fn compile(&mut self, input: &CompilationInput) + fn compile(&mut self, input: &CompilationInput) -> Result; /// Returns true iff the DD which has been compiled is an exact DD. fn is_exact(&self) -> bool; @@ -110,5 +106,5 @@ pub trait DecisionDiagram { /// this method will be called at most once per relaxed DD compilation. fn drain_cutset(&mut self, func: F) where - F: FnMut(SubProblem); + F: FnMut(SubProblem); } \ No newline at end of file diff --git a/ddo/src/implementation/mdd/clean.rs b/ddo/src/implementation/mdd/clean.rs index 8660d586..7fb47580 100644 --- a/ddo/src/implementation/mdd/clean.rs +++ b/ddo/src/implementation/mdd/clean.rs @@ -34,9 +34,9 @@ struct LayerId(usize); /// Represents an effective node from the decision diagram #[derive(Debug, Clone)] -struct Node { +struct Node { /// The state associated to this node - state: Arc, + state: Arc, /// The length of the longest path between the problem root and this /// specific node value_top: isize, @@ -112,9 +112,9 @@ struct Layer { /// - Relaxed: either the last exact layer of the frontier cut-set can be chosen /// within the CompilationInput #[derive(Debug, Clone)] -pub struct Mdd +pub struct Mdd where - T: Eq + PartialEq + Hash + Clone, + State: Eq + PartialEq + Hash + Clone, { /// This vector stores the information about the structure of all the layers /// in this decision diagram @@ -122,7 +122,7 @@ where /// All the nodes composing this decision diagram. The vector comprises /// nodes from all layers in the DD. A nice property is that all nodes /// belonging to one same layer form a sequence in the ‘nodes‘ vector. - nodes: Vec>, + nodes: Vec>, /// This vector stores the information about all edges connecting the nodes /// of the decision diagram. edges: Vec, @@ -140,7 +140,7 @@ where /// The rationale being that two transitions to the same state in the same /// layer should lead to the same node. This indexation helps ensuring /// the uniqueness constraint in amortized O(1). - next_l: FxHashMap, NodeId>, + next_l: FxHashMap, NodeId>, /// The depth of the layer currently being expanded curr_depth: usize, @@ -219,22 +219,20 @@ macro_rules! append_edge_to { }; } -impl Default for Mdd +impl Default for Mdd where - T: Eq + PartialEq + Hash + Clone, + State: Eq + PartialEq + Hash + Clone, { fn default() -> Self { Self::new() } } -impl DecisionDiagram for Mdd +impl DecisionDiagram for Mdd where - T: Eq + PartialEq + Hash + Clone, + State: Eq + PartialEq + Hash + Clone, { - type State = T; - - fn compile(&mut self, input: &CompilationInput) -> Result { + fn compile(&mut self, input: &CompilationInput) -> Result { self._compile(input) } @@ -260,7 +258,7 @@ where fn drain_cutset(&mut self, func: F) where - F: FnMut(SubProblem) { + F: FnMut(SubProblem) { self._drain_cutset(func) } } diff --git a/ddo/src/implementation/mdd/pooled.rs b/ddo/src/implementation/mdd/pooled.rs index ecc5a670..4459d03c 100644 --- a/ddo/src/implementation/mdd/pooled.rs +++ b/ddo/src/implementation/mdd/pooled.rs @@ -33,9 +33,9 @@ struct LayerId(usize); /// Represents an effective node from the decision diagram #[derive(Debug, Clone)] -struct Node { +struct Node { /// The state associated to this node - state: Arc, + state: Arc, /// The length of the longest path between the problem root and this /// specific node value_top: isize, @@ -114,9 +114,9 @@ struct Layer { /// - Relaxed: either the last exact layer of the frontier cut-set can be chosen /// within the CompilationInput #[derive(Debug, Clone)] -pub struct Pooled +pub struct Pooled where - T: Eq + PartialEq + Hash + Clone, + State: Eq + PartialEq + Hash + Clone, { /// This map stores the information about the structure of all the layers /// in this decision diagram @@ -124,7 +124,7 @@ where /// All the nodes composing this decision diagram. The vector comprises /// nodes from all layers in the DD. A nice property is that all nodes /// belonging to one same layer form a sequence in the ‘nodes‘ vector. - nodes: Vec>, + nodes: Vec>, /// This vector stores the information about all edges connecting the nodes /// of the decision diagram. edges: Vec, @@ -139,7 +139,7 @@ where /// The rationale being that two transitions to the same state in the same /// layer should lead to the same node. This indexation helps ensuring /// the uniqueness constraint in amortized O(1). - pool: FxHashMap, NodeId>, + pool: FxHashMap, NodeId>, /// Keeps track of the decisions that have been taken to reach the root /// of this DD, starting from the problem root. @@ -212,22 +212,20 @@ macro_rules! append_edge_to { }; } -impl Default for Pooled +impl Default for Pooled where - T: Eq + PartialEq + Hash + Clone, + State: Eq + PartialEq + Hash + Clone, { fn default() -> Self { Self::new() } } -impl DecisionDiagram for Pooled +impl DecisionDiagram for Pooled where - T: Eq + PartialEq + Hash + Clone, + State: Eq + PartialEq + Hash + Clone, { - type State = T; - - fn compile(&mut self, input: &CompilationInput) -> Result { + fn compile(&mut self, input: &CompilationInput) -> Result { self._compile(input) } @@ -253,7 +251,7 @@ where fn drain_cutset(&mut self, func: F) where - F: FnMut(SubProblem) { + F: FnMut(SubProblem) { self._drain_cutset(func) } } diff --git a/ddo/src/implementation/solver/parallel.rs b/ddo/src/implementation/solver/parallel.rs index 10b96f93..e09807eb 100644 --- a/ddo/src/implementation/solver/parallel.rs +++ b/ddo/src/implementation/solver/parallel.rs @@ -285,7 +285,7 @@ enum WorkLoad { /// } /// ``` pub struct ParallelSolver<'a, State, D, C> -where D: DecisionDiagram + Default, +where D: DecisionDiagram + Default, C: Cache + Send + Sync + Default, { /// This is the shared state. Each thread is going to take a reference to it. @@ -302,7 +302,7 @@ where D: DecisionDiagram + Default, impl<'a, State, D, C> ParallelSolver<'a, State, D, C> where State: Eq + Hash + Clone, - D: DecisionDiagram + Default, + D: DecisionDiagram + Default, C: Cache + Send + Sync + Default, { pub fn new( @@ -563,7 +563,7 @@ where impl<'a, State, D, C> Solver for ParallelSolver<'a, State, D, C> where State: Eq + PartialEq + Hash + Clone, - D: DecisionDiagram + Default, + D: DecisionDiagram + Default, C: Cache + Send + Sync + Default, { /// Applies the branch and bound algorithm proposed by Bergman et al. to diff --git a/ddo/src/implementation/solver/sequential.rs b/ddo/src/implementation/solver/sequential.rs index 1bc2080f..61d97db8 100644 --- a/ddo/src/implementation/solver/sequential.rs +++ b/ddo/src/implementation/solver/sequential.rs @@ -200,7 +200,7 @@ enum WorkLoad { /// } /// ``` pub struct SequentialSolver<'a, State, D = DefaultMDDLEL, C = EmptyCache> -where D: DecisionDiagram + Default, +where D: DecisionDiagram + Default, C: Cache + Default, { /// A reference to the problem being solved with branch-and-bound MDD @@ -257,7 +257,7 @@ where D: DecisionDiagram + Default, impl<'a, State, D, C> SequentialSolver<'a, State, D, C> where State: Eq + Hash + Clone, - D: DecisionDiagram + Default, + D: DecisionDiagram + Default, C: Cache + Default, { pub fn new( @@ -465,7 +465,7 @@ where impl<'a, State, D, C> Solver for SequentialSolver<'a, State, D, C> where State: Eq + PartialEq + Hash + Clone, - D: DecisionDiagram + Default, + D: DecisionDiagram + Default, C: Cache + Default, { /// Applies the branch and bound algorithm proposed by Bergman et al. to From 6223d5381fe591d79dd243dc3a9166016d00ead2 Mon Sep 17 00:00:00 2001 From: Bubbler-4 Date: Mon, 30 Dec 2024 14:40:13 +0000 Subject: [PATCH 6/6] change SubProblemRanking --- ddo/src/abstraction/heuristics.rs | 8 +--- ddo/src/implementation/fringe/no_duplicate.rs | 38 +++++++++---------- ddo/src/implementation/fringe/simple.rs | 22 +++++++---- .../heuristics/subproblem_ranking.rs | 4 +- ddo/src/implementation/heuristics/utils.rs | 26 +++++++------ 5 files changed, 51 insertions(+), 47 deletions(-) diff --git a/ddo/src/abstraction/heuristics.rs b/ddo/src/abstraction/heuristics.rs index bf842677..507b39bc 100644 --- a/ddo/src/abstraction/heuristics.rs +++ b/ddo/src/abstraction/heuristics.rs @@ -85,15 +85,11 @@ pub trait StateRanking { /// sub-problems on the solver fringe. This order is used by the framework /// as a means to impose a given ordering on the nodes that are popped from /// the solver fringe. -pub trait SubProblemRanking { - /// As is the case for `Problem` and `Relaxation`, a `SubProblemRanking` - /// must tell the kind of states it is able to operate on. - type State; - +pub trait SubProblemRanking { /// This method compares two sub-problems and determines which is the one /// that needs to be popped off the fringe first. In this ordering, greater /// means more likely to be popped first. - fn compare(&self, a: &SubProblem, b: &SubProblem) -> Ordering; + fn compare(&self, a: &SubProblem, b: &SubProblem) -> Ordering; } /// This trait encapsulates a criterion (external to the solver) which imposes diff --git a/ddo/src/implementation/fringe/no_duplicate.rs b/ddo/src/implementation/fringe/no_duplicate.rs index ba35db7b..0a8f3a34 100644 --- a/ddo/src/implementation/fringe/no_duplicate.rs +++ b/ddo/src/implementation/fringe/no_duplicate.rs @@ -49,17 +49,17 @@ enum Action { /// items remain ordered in the priority queue while guaranteeing that a /// given state will only ever be present *ONCE* in the priority queue (the /// node with the longest path to state is the only kept copy). -pub struct NoDupFringe +pub struct NoDupFringe where - O: SubProblemRanking, - O::State: Eq + Hash + Clone, + Ranking: SubProblemRanking, + State: Eq + Hash + Clone, { /// This is the comparator used to order the nodes in the binary heap - cmp: CompareSubProblem, + cmp: CompareSubProblem, /// A mapping that associates some state to a node identifier. - states: FxHashMap, NodeId>, + states: FxHashMap, NodeId>, /// The actual payload (nodes) ordered in the list - nodes: Vec>, + nodes: Vec>, /// The position of the items in the heap pos: Vec, /// This is the actual heap which orders nodes. @@ -68,10 +68,10 @@ where recycle_bin: Vec, } -impl Fringe for NoDupFringe +impl Fringe for NoDupFringe where - O: SubProblemRanking, - O::State: Eq + Hash + Clone, + Ranking: SubProblemRanking, + State: Eq + Hash + Clone, { /// Pushes one node onto the heap while ensuring that only one copy of the /// node (identified by its state) is kept in the heap. @@ -83,7 +83,7 @@ where /// UB and or longer longest path), the priority of the node will be /// increased. As always, in the event where the newly pushed node has a /// longer longest path than the pre-existing node, that one will be kept. - fn push(&mut self, mut node: SubProblem) { + fn push(&mut self, mut node: SubProblem) { let state = Arc::clone(&node.state); let action = match self.states.entry(state) { @@ -139,7 +139,7 @@ where /// Pops the best node out of the heap. Here, the best is defined as the /// node having the best upper bound, with the longest `value`. - fn pop(&mut self) -> Option> { + fn pop(&mut self) -> Option> { if self.is_empty() { return None; } @@ -178,14 +178,14 @@ where } } -impl NoDupFringe +impl NoDupFringe where - O: SubProblemRanking, - O::State: Eq + Hash + Clone, + Ranking: SubProblemRanking, + State: Eq + Hash + Clone, { /// Creates a new instance of the no dup heap which uses cmp as /// comparison criterion. - pub fn new(ranking: O) -> Self { + pub fn new(ranking: Ranking) -> Self { Self { cmp: CompareSubProblem::new(ranking), states: Default::default(), @@ -533,10 +533,10 @@ mod test_no_dup_fringe { } - fn empty_fringe() -> NoDupFringe> { + fn empty_fringe() -> NoDupFringe> { NoDupFringe::new(MaxUB::new(&UsizeRanking)) } - fn non_empty_fringe() -> NoDupFringe> { + fn non_empty_fringe() -> NoDupFringe> { let mut fringe = empty_fringe(); fringe.push(SubProblem{ state: Arc::new(42), @@ -637,12 +637,12 @@ mod test_no_dup_fringe { assert!(heap.is_empty()); } - fn push_all>(heap: &mut NoDupFringe, nodes: &[SubProblem]) { + fn push_all>(heap: &mut NoDupFringe, nodes: &[SubProblem]) { for n in nodes.iter() { heap.push(n.clone()); } } - fn pop_all>(heap: &mut NoDupFringe) -> Vec { + fn pop_all>(heap: &mut NoDupFringe) -> Vec { let mut popped = vec![]; while let Some(n) = heap.pop() { popped.push(*n.state) diff --git a/ddo/src/implementation/fringe/simple.rs b/ddo/src/implementation/fringe/simple.rs index 1b2b6f3d..5eccd74e 100644 --- a/ddo/src/implementation/fringe/simple.rs +++ b/ddo/src/implementation/fringe/simple.rs @@ -32,21 +32,27 @@ use crate::*; /// solvers. Hence, you don't need to take any action in order to use the /// `SimpleFringe`. /// -pub struct SimpleFringe { - heap: BinaryHeap, CompareSubProblem> +pub struct SimpleFringe where + Ranking: SubProblemRanking +{ + heap: BinaryHeap, CompareSubProblem> } -impl SimpleFringe where O: SubProblemRanking { +impl SimpleFringe where + Ranking: SubProblemRanking +{ /// This creates a new simple fringe which uses a custom fringe order. - pub fn new(o: O) -> Self { - Self{ heap: BinaryHeap::from_vec_cmp(vec![], CompareSubProblem::new(o)) } + pub fn new(ranking: Ranking) -> Self { + Self{ heap: BinaryHeap::from_vec_cmp(vec![], CompareSubProblem::new(ranking)) } } } -impl Fringe for SimpleFringe where O: SubProblemRanking { - fn push(&mut self, node: SubProblem) { +impl Fringe for SimpleFringe +where Ranking: SubProblemRanking +{ + fn push(&mut self, node: SubProblem) { self.heap.push(node) } - fn pop(&mut self) -> Option> { + fn pop(&mut self) -> Option> { self.heap.pop() } diff --git a/ddo/src/implementation/heuristics/subproblem_ranking.rs b/ddo/src/implementation/heuristics/subproblem_ranking.rs index f0cf7417..00bdf803 100644 --- a/ddo/src/implementation/heuristics/subproblem_ranking.rs +++ b/ddo/src/implementation/heuristics/subproblem_ranking.rs @@ -80,9 +80,7 @@ impl <'a, O: StateRanking> MaxUB<'a, O> { Self(x) } } -impl SubProblemRanking for MaxUB<'_, O> { - type State = O::State; - +impl SubProblemRanking for MaxUB<'_, O> { fn compare(&self, l: &SubProblem, r: &SubProblem) -> Ordering { l.ub.cmp(&r.ub) .then_with(|| l.value.cmp(&r.value)) diff --git a/ddo/src/implementation/heuristics/utils.rs b/ddo/src/implementation/heuristics/utils.rs index 54052819..32d31083 100644 --- a/ddo/src/implementation/heuristics/utils.rs +++ b/ddo/src/implementation/heuristics/utils.rs @@ -19,7 +19,7 @@ //! This module provide some convenient utilities to work with used defined heuristics. -use std::cmp::Ordering; +use std::{cmp::Ordering, marker::PhantomData}; use compare::Compare; @@ -68,16 +68,22 @@ use crate::{SubProblemRanking, SubProblem}; /// let heap = BinaryHeap::from_vec_cmp(vec![], comparator); /// ``` #[derive(Debug, Clone, Copy)] -pub struct CompareSubProblem(X); -impl CompareSubProblem { +pub struct CompareSubProblem> { + ranking: Ranking, + _state: PhantomData, +} +impl > CompareSubProblem { /// Creates a new instance - pub fn new(x: X) -> Self { - Self(x) + pub fn new(ranking: Ranking) -> Self { + Self { + ranking, + _state: Default::default(), + } } } -impl Compare> for CompareSubProblem { - fn compare(&self, l: &SubProblem, r: &SubProblem) -> Ordering { - self.0.compare(l, r) +impl > Compare> for CompareSubProblem { + fn compare(&self, l: &SubProblem, r: &SubProblem) -> Ordering { + self.ranking.compare(l, r) } } @@ -96,9 +102,7 @@ mod test { a.cmp(b) } } - impl SubProblemRanking for CharRanking { - type State = char; - + impl SubProblemRanking for CharRanking { fn compare(&self, a: &SubProblem, b: &SubProblem) -> Ordering { ::compare(self, a.state.deref(), b.state.deref()) }