From 7f7f0cf0b5b290d45d36f9e57482a128a3a408e9 Mon Sep 17 00:00:00 2001 From: Imko Marijnissen <50290518+ImkoMarijnissen@users.noreply.github.com> Date: Mon, 10 Feb 2025 16:54:07 +0100 Subject: [PATCH] feat: add method for retrieving relevant brancher events (#138) For some instances (e.g. `2023/code-generator/unison.mzn`, with `2023/code-generator/mips_gcc.flow.find_regno_partial.dzn`), it showed up in profiling that the dynamic brancher went through a lot of branchers upon certain events (in this case the `on_unassign_integer` event, even though only a single brancher was interested in these events). To alleviate this issue, this PR adds a method for `Brancher`s, `VariableSelector`s, and `ValueSelector`s which allows them to indicate which events they are interested in; this allows the `DynamicBrancher` to only call the `Brancher`s which are interested in it. --- Cargo.lock | 21 +++++ pumpkin-solver/Cargo.toml | 1 + pumpkin-solver/src/branching/brancher.rs | 51 ++++++++++++ .../branchers/alternating_brancher.rs | 11 +++ .../branching/branchers/autonomous_search.rs | 14 ++++ .../branching/branchers/dynamic_brancher.rs | 81 ++++++++++++++----- .../independent_variable_value_brancher.rs | 9 +++ pumpkin-solver/src/branching/mod.rs | 2 +- .../value_selection/dynamic_value_selector.rs | 5 ++ .../value_selection/in_domain_interval.rs | 5 ++ .../value_selection/in_domain_max.rs | 5 ++ .../value_selection/in_domain_median.rs | 5 ++ .../value_selection/in_domain_middle.rs | 5 ++ .../value_selection/in_domain_min.rs | 5 ++ .../value_selection/in_domain_random.rs | 9 +++ .../value_selection/in_domain_split.rs | 5 ++ .../value_selection/in_domain_split_random.rs | 5 ++ .../value_selection/out_domain_max.rs | 5 ++ .../value_selection/out_domain_median.rs | 5 ++ .../value_selection/out_domain_min.rs | 5 ++ .../value_selection/out_domain_random.rs | 5 ++ .../value_selection/random_splitter.rs | 5 ++ .../reverse_in_domain_split.rs | 5 ++ .../value_selection/value_selector.rs | 17 ++++ .../variable_selection/anti_first_fail.rs | 5 ++ .../dynamic_variable_selector.rs | 5 ++ .../variable_selection/first_fail.rs | 5 ++ .../variable_selection/input_order.rs | 9 +++ .../branching/variable_selection/largest.rs | 5 ++ .../variable_selection/max_regret.rs | 5 ++ .../variable_selection/most_constrained.rs | 5 ++ .../variable_selection/occurrence.rs | 5 ++ .../proportional_domain_size.rs | 5 ++ .../branching/variable_selection/random.rs | 5 ++ .../branching/variable_selection/smallest.rs | 5 ++ .../variable_selection/variable_selector.rs | 23 ++++++ .../engine/constraint_satisfaction_solver.rs | 5 ++ 37 files changed, 350 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bfefb046..09ff64ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -223,6 +223,26 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "enumset" version = "1.1.5" @@ -521,6 +541,7 @@ dependencies = [ "convert_case", "downcast-rs", "drcp-format 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "enum-map", "enumset", "env_logger", "flatzinc", diff --git a/pumpkin-solver/Cargo.toml b/pumpkin-solver/Cargo.toml index 3cf1181b..cae2025c 100644 --- a/pumpkin-solver/Cargo.toml +++ b/pumpkin-solver/Cargo.toml @@ -26,6 +26,7 @@ clap = { version = "4.5.17", features = ["derive"] } env_logger = "0.10.0" bitfield-struct = "0.9.2" num = "0.4.3" +enum-map = "2.7.3" [dev-dependencies] clap = { version = "4.5.17", features = ["derive"] } diff --git a/pumpkin-solver/src/branching/brancher.rs b/pumpkin-solver/src/branching/brancher.rs index ecdeb006..b79240e1 100644 --- a/pumpkin-solver/src/branching/brancher.rs +++ b/pumpkin-solver/src/branching/brancher.rs @@ -1,9 +1,13 @@ +use enum_map::Enum; + #[cfg(doc)] use crate::basic_types::Random; use crate::basic_types::SolutionReference; #[cfg(doc)] use crate::branching; #[cfg(doc)] +use crate::branching::branchers::dynamic_brancher::DynamicBrancher; +#[cfg(doc)] use crate::branching::value_selection::ValueSelector; #[cfg(doc)] use crate::branching::variable_selection::VariableSelector; @@ -39,15 +43,24 @@ pub trait Brancher { /// A function which is called after a conflict has been found and processed but (currently) /// does not provide any additional information. + /// + /// To receive information about this event, use [`BrancherEvent::Conflict`] in + /// [`Self::subscribe_to_events`] fn on_conflict(&mut self) {} /// A function which is called whenever a backtrack occurs in the [`Solver`]. + /// + /// To receive information about this event, use [`BrancherEvent::Backtrack`] in + /// [`Self::subscribe_to_events`] fn on_backtrack(&mut self) {} /// This method is called when a solution is found; this will either be called when a new /// incumbent solution is found (i.e. a solution with a better objective value than previously /// known) or when a new solution is found when iterating over solutions using /// [`SolutionIterator`]. + /// + /// To receive information about this event, use [`BrancherEvent::Solution`] in + /// [`Self::subscribe_to_events`] fn on_solution(&mut self, _solution: SolutionReference) {} /// A function which is called after a [`DomainId`] is unassigned during backtracking (i.e. when @@ -55,17 +68,28 @@ pub trait Brancher { /// [`DomainId`] which has been reset and `value` which is the value to which the variable was /// previously fixed. This method could thus be called multiple times in a single /// backtracking operation by the solver. + /// + /// To receive information about this event, use [`BrancherEvent::UnassignInteger`] in + /// [`Self::subscribe_to_events`] fn on_unassign_integer(&mut self, _variable: DomainId, _value: i32) {} /// A function which is called when a [`Predicate`] appears in a conflict during conflict /// analysis. + /// + /// To receive information about this event, use + /// [`BrancherEvent::AppearanceInConflictPredicate`] in [`Self::subscribe_to_events`] fn on_appearance_in_conflict_predicate(&mut self, _predicate: Predicate) {} /// This method is called whenever a restart is performed. + /// To receive information about this event, use [`BrancherEvent::Restart`] in + /// [`Self::subscribe_to_events`] fn on_restart(&mut self) {} /// Called after backtracking. /// Used to reset internal data structures to account for the backtrack. + /// + /// To receive information about this event, use [`BrancherEvent::Synchronise`] in + /// [`Self::subscribe_to_events`] fn synchronise(&mut self, _assignments: &Assignments) {} /// This method returns whether a restart is *currently* pointless for the [`Brancher`]. @@ -81,4 +105,31 @@ pub trait Brancher { fn is_restart_pointless(&mut self) -> bool { true } + + /// Indicates which [`BrancherEvent`] are relevant for this particular [`Brancher`]. + /// + /// This can be used by [`Brancher::subscribe_to_events`] to determine upon which + /// events which [`VariableSelector`] should be called. + fn subscribe_to_events(&self) -> Vec; +} + +/// The events which can occur for a [`Brancher`]. Used for returning which events are relevant in +/// [`Brancher::subscribe_to_events`], [`VariableSelector::subscribe_to_events`], +/// and [`ValueSelector::subscribe_to_events`]. +#[derive(Debug, Clone, Copy, Enum, Hash, PartialEq, Eq)] +pub enum BrancherEvent { + /// Event for when a conflict is detected + Conflict, + /// Event for when a backtrack is performed + Backtrack, + /// Event for when a solution has been found + Solution, + /// Event for when an integer variable has become unassigned + UnassignInteger, + /// Event for when a predicate appears during conflict analysis + AppearanceInConflictPredicate, + /// Event for when a restart occurs + Restart, + /// Event which is called with the new state after a backtrack has occurred + Synchronise, } diff --git a/pumpkin-solver/src/branching/branchers/alternating_brancher.rs b/pumpkin-solver/src/branching/branchers/alternating_brancher.rs index e42a45d8..b00bddec 100644 --- a/pumpkin-solver/src/branching/branchers/alternating_brancher.rs +++ b/pumpkin-solver/src/branching/branchers/alternating_brancher.rs @@ -2,6 +2,7 @@ //! on the strategy specified in [`AlternatingStrategy`]. use crate::basic_types::SolutionReference; +use crate::branching::brancher::BrancherEvent; use crate::branching::Brancher; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; @@ -208,6 +209,16 @@ impl Brancher for AlternatingBrancher { self.other_brancher.synchronise(assignments); } } + + fn subscribe_to_events(&self) -> Vec { + // We require the restart event and on solution event for the alternating brancher itself; + // additionally, it will be interested in the events of its sub-branchers + [BrancherEvent::Restart, BrancherEvent::Solution] + .into_iter() + .chain(self.default_brancher.subscribe_to_events()) + .chain(self.other_brancher.subscribe_to_events()) + .collect() + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/branchers/autonomous_search.rs b/pumpkin-solver/src/branching/branchers/autonomous_search.rs index 97b9fdf2..2002b24d 100644 --- a/pumpkin-solver/src/branching/branchers/autonomous_search.rs +++ b/pumpkin-solver/src/branching/branchers/autonomous_search.rs @@ -5,6 +5,7 @@ use crate::basic_types::SolutionReference; use crate::branching::value_selection::RandomSplitter; use crate::branching::variable_selection::RandomSelector; use crate::branching::Brancher; +use crate::branching::BrancherEvent; use crate::branching::SelectionContext; use crate::containers::KeyValueHeap; use crate::containers::StorageKey; @@ -292,6 +293,19 @@ impl Brancher for AutonomousSearch { fn is_restart_pointless(&mut self) -> bool { false } + + fn subscribe_to_events(&self) -> Vec { + [ + BrancherEvent::Solution, + BrancherEvent::Conflict, + BrancherEvent::Backtrack, + BrancherEvent::Synchronise, + BrancherEvent::AppearanceInConflictPredicate, + ] + .into_iter() + .chain(self.backup_brancher.subscribe_to_events()) + .collect() + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/branchers/dynamic_brancher.rs b/pumpkin-solver/src/branching/branchers/dynamic_brancher.rs index 22d18e7a..a8fa3d3c 100644 --- a/pumpkin-solver/src/branching/branchers/dynamic_brancher.rs +++ b/pumpkin-solver/src/branching/branchers/dynamic_brancher.rs @@ -6,7 +6,11 @@ use std::cmp::min; use std::fmt::Debug; +use enum_map::EnumMap; + +use crate::basic_types::HashSet; use crate::basic_types::SolutionReference; +use crate::branching::brancher::BrancherEvent; use crate::branching::Brancher; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; @@ -28,6 +32,9 @@ use crate::engine::Assignments; pub struct DynamicBrancher { branchers: Vec>, brancher_index: usize, + + relevant_event_to_index: EnumMap>, + relevant_events: Vec, } impl Debug for DynamicBrancher { @@ -40,14 +47,36 @@ impl DynamicBrancher { /// Creates a new [`DynamicBrancher`] with the provided `branchers`. It will attempt to use the /// `branchers` in the order in which they were provided. pub fn new(branchers: Vec>) -> Self { + let mut relevant_event_to_index: EnumMap> = EnumMap::default(); + let mut relevant_events = HashSet::new(); + + // The dynamic brancher will reset the indices upon these events so they should be called + let _ = relevant_events.insert(BrancherEvent::Solution); + let _ = relevant_events.insert(BrancherEvent::Conflict); + + branchers.iter().enumerate().for_each(|(index, brancher)| { + for event in brancher.subscribe_to_events() { + relevant_event_to_index[event].push(index); + let _ = relevant_events.insert(event); + } + }); Self { branchers, brancher_index: 0, + + relevant_event_to_index, + relevant_events: relevant_events.into_iter().collect(), } } pub fn add_brancher(&mut self, brancher: Box) { - self.branchers.push(brancher) + for event in brancher.subscribe_to_events() { + self.relevant_event_to_index[event].push(self.branchers.len()); + if !self.relevant_events.contains(&event) { + self.relevant_events.push(event); + } + } + self.branchers.push(brancher); } } @@ -70,46 +99,50 @@ impl Brancher for DynamicBrancher { // A conflict has occurred, we do not know which brancher now can select a variable, reset // to the first one self.brancher_index = 0; - self.branchers - .iter_mut() - .for_each(|brancher| brancher.on_conflict()); + self.relevant_event_to_index[BrancherEvent::Conflict] + .iter() + .for_each(|&brancher_index| self.branchers[brancher_index].on_conflict()); } fn on_backtrack(&mut self) { - self.branchers - .iter_mut() - .for_each(|brancher| brancher.on_backtrack()); + self.relevant_event_to_index[BrancherEvent::Backtrack] + .iter() + .for_each(|&brancher_index| self.branchers[brancher_index].on_backtrack()); } fn on_unassign_integer(&mut self, variable: DomainId, value: i32) { - self.branchers - .iter_mut() - .for_each(|brancher| brancher.on_unassign_integer(variable, value)); + self.relevant_event_to_index[BrancherEvent::UnassignInteger] + .iter() + .for_each(|&brancher_index| { + self.branchers[brancher_index].on_unassign_integer(variable, value) + }); } fn on_appearance_in_conflict_predicate(&mut self, predicate: Predicate) { - self.branchers - .iter_mut() - .for_each(|brancher| brancher.on_appearance_in_conflict_predicate(predicate)); + self.relevant_event_to_index[BrancherEvent::AppearanceInConflictPredicate] + .iter() + .for_each(|&brancher_index| { + self.branchers[brancher_index].on_appearance_in_conflict_predicate(predicate) + }); } fn on_solution(&mut self, solution: SolutionReference) { self.brancher_index = 0; - self.branchers - .iter_mut() - .for_each(|brancher| brancher.on_solution(solution)); + self.relevant_event_to_index[BrancherEvent::Solution] + .iter() + .for_each(|&brancher_index| self.branchers[brancher_index].on_solution(solution)); } fn on_restart(&mut self) { - self.branchers - .iter_mut() - .for_each(|brancher| brancher.on_restart()); + self.relevant_event_to_index[BrancherEvent::Restart] + .iter() + .for_each(|&brancher_index| self.branchers[brancher_index].on_restart()); } fn synchronise(&mut self, assignments: &Assignments) { - self.branchers - .iter_mut() - .for_each(|brancher| brancher.synchronise(assignments)); + self.relevant_event_to_index[BrancherEvent::Synchronise] + .iter() + .for_each(|&brancher_index| self.branchers[brancher_index].synchronise(assignments)); } fn is_restart_pointless(&mut self) -> bool { @@ -120,4 +153,8 @@ impl Brancher for DynamicBrancher { .iter_mut() .all(|brancher| brancher.is_restart_pointless()) } + + fn subscribe_to_events(&self) -> Vec { + self.relevant_events.clone() + } } diff --git a/pumpkin-solver/src/branching/branchers/independent_variable_value_brancher.rs b/pumpkin-solver/src/branching/branchers/independent_variable_value_brancher.rs index 03062f2a..784523a2 100644 --- a/pumpkin-solver/src/branching/branchers/independent_variable_value_brancher.rs +++ b/pumpkin-solver/src/branching/branchers/independent_variable_value_brancher.rs @@ -4,6 +4,7 @@ use std::marker::PhantomData; use crate::basic_types::SolutionReference; +use crate::branching::brancher::BrancherEvent; use crate::branching::value_selection::ValueSelector; use crate::branching::variable_selection::VariableSelector; use crate::branching::Brancher; @@ -89,4 +90,12 @@ where fn is_restart_pointless(&mut self) -> bool { self.variable_selector.is_restart_pointless() && self.value_selector.is_restart_pointless() } + + fn subscribe_to_events(&self) -> Vec { + self.variable_selector + .subscribe_to_events() + .into_iter() + .chain(self.value_selector.subscribe_to_events()) + .collect() + } } diff --git a/pumpkin-solver/src/branching/mod.rs b/pumpkin-solver/src/branching/mod.rs index 0e11ffb7..6bd55b5e 100644 --- a/pumpkin-solver/src/branching/mod.rs +++ b/pumpkin-solver/src/branching/mod.rs @@ -71,7 +71,7 @@ pub mod tie_breaking; pub mod value_selection; pub mod variable_selection; -pub use brancher::Brancher; +pub use brancher::*; pub use selection_context::SelectionContext; #[cfg(doc)] diff --git a/pumpkin-solver/src/branching/value_selection/dynamic_value_selector.rs b/pumpkin-solver/src/branching/value_selection/dynamic_value_selector.rs index 5b31cd0b..d4b51834 100644 --- a/pumpkin-solver/src/branching/value_selection/dynamic_value_selector.rs +++ b/pumpkin-solver/src/branching/value_selection/dynamic_value_selector.rs @@ -2,6 +2,7 @@ use std::fmt::Debug; use super::ValueSelector; use crate::basic_types::SolutionReference; +use crate::branching::brancher::BrancherEvent; #[cfg(doc)] use crate::branching::branchers::dynamic_brancher::DynamicBrancher; use crate::branching::SelectionContext; @@ -46,4 +47,8 @@ impl ValueSelector for DynamicValueSelector { fn is_restart_pointless(&mut self) -> bool { self.selector.is_restart_pointless() } + + fn subscribe_to_events(&self) -> Vec { + self.selector.subscribe_to_events() + } } diff --git a/pumpkin-solver/src/branching/value_selection/in_domain_interval.rs b/pumpkin-solver/src/branching/value_selection/in_domain_interval.rs index a7315855..d3ec87e9 100644 --- a/pumpkin-solver/src/branching/value_selection/in_domain_interval.rs +++ b/pumpkin-solver/src/branching/value_selection/in_domain_interval.rs @@ -1,4 +1,5 @@ use super::InDomainSplit; +use crate::branching::brancher::BrancherEvent; use crate::branching::value_selection::ValueSelector; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; @@ -38,6 +39,10 @@ impl ValueSelector for InDomainInterval { InDomainSplit::get_predicate_excluding_upper_half(context, decision_variable) } } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/value_selection/in_domain_max.rs b/pumpkin-solver/src/branching/value_selection/in_domain_max.rs index bca36bd1..1addc9bb 100644 --- a/pumpkin-solver/src/branching/value_selection/in_domain_max.rs +++ b/pumpkin-solver/src/branching/value_selection/in_domain_max.rs @@ -1,3 +1,4 @@ +use crate::branching::brancher::BrancherEvent; use crate::branching::value_selection::ValueSelector; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; @@ -16,6 +17,10 @@ impl ValueSelector for InDomainMax { ) -> Predicate { predicate!(decision_variable >= context.upper_bound(decision_variable)) } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/value_selection/in_domain_median.rs b/pumpkin-solver/src/branching/value_selection/in_domain_median.rs index 3cd61a36..7bcb61ae 100644 --- a/pumpkin-solver/src/branching/value_selection/in_domain_median.rs +++ b/pumpkin-solver/src/branching/value_selection/in_domain_median.rs @@ -1,3 +1,4 @@ +use crate::branching::brancher::BrancherEvent; use crate::branching::value_selection::ValueSelector; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; @@ -21,6 +22,10 @@ impl ValueSelector for InDomainMedian { .collect::>(); predicate!(decision_variable == values_in_domain[values_in_domain.len() / 2]) } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/value_selection/in_domain_middle.rs b/pumpkin-solver/src/branching/value_selection/in_domain_middle.rs index 937e73f7..ff6acd48 100644 --- a/pumpkin-solver/src/branching/value_selection/in_domain_middle.rs +++ b/pumpkin-solver/src/branching/value_selection/in_domain_middle.rs @@ -1,3 +1,4 @@ +use crate::branching::brancher::BrancherEvent; #[cfg(doc)] use crate::branching::value_selection::InDomainMedian; use crate::branching::value_selection::ValueSelector; @@ -45,6 +46,10 @@ impl ValueSelector for InDomainMiddle { } unreachable!("There should be at least 1 selectable variable in the domain"); } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/value_selection/in_domain_min.rs b/pumpkin-solver/src/branching/value_selection/in_domain_min.rs index 3a320e18..77e0b82f 100644 --- a/pumpkin-solver/src/branching/value_selection/in_domain_min.rs +++ b/pumpkin-solver/src/branching/value_selection/in_domain_min.rs @@ -1,4 +1,5 @@ use super::ValueSelector; +use crate::branching::brancher::BrancherEvent; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; use crate::engine::variables::IntegerVariable; @@ -16,6 +17,10 @@ impl ValueSelector for InDomainMin { ) -> Predicate { predicate!(decision_variable <= context.lower_bound(decision_variable)) } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/value_selection/in_domain_random.rs b/pumpkin-solver/src/branching/value_selection/in_domain_random.rs index 1172f65b..d0beeec1 100644 --- a/pumpkin-solver/src/branching/value_selection/in_domain_random.rs +++ b/pumpkin-solver/src/branching/value_selection/in_domain_random.rs @@ -1,3 +1,4 @@ +use crate::branching::brancher::BrancherEvent; use crate::branching::value_selection::ValueSelector; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; @@ -28,6 +29,10 @@ impl ValueSelector for InDomainRandom { fn is_restart_pointless(&mut self) -> bool { false } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } impl ValueSelector for InDomainRandom { @@ -46,6 +51,10 @@ impl ValueSelector for InDomainRandom { fn is_restart_pointless(&mut self) -> bool { false } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/value_selection/in_domain_split.rs b/pumpkin-solver/src/branching/value_selection/in_domain_split.rs index 4752798f..b25c82e2 100644 --- a/pumpkin-solver/src/branching/value_selection/in_domain_split.rs +++ b/pumpkin-solver/src/branching/value_selection/in_domain_split.rs @@ -1,3 +1,4 @@ +use crate::branching::brancher::BrancherEvent; use crate::branching::value_selection::ValueSelector; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; @@ -21,6 +22,10 @@ impl ValueSelector for InDomainSplit { ) -> Predicate { InDomainSplit::get_predicate_excluding_upper_half(context, decision_variable) } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } impl InDomainSplit { diff --git a/pumpkin-solver/src/branching/value_selection/in_domain_split_random.rs b/pumpkin-solver/src/branching/value_selection/in_domain_split_random.rs index e179bcf4..597dede1 100644 --- a/pumpkin-solver/src/branching/value_selection/in_domain_split_random.rs +++ b/pumpkin-solver/src/branching/value_selection/in_domain_split_random.rs @@ -1,3 +1,4 @@ +use crate::branching::brancher::BrancherEvent; use crate::branching::value_selection::ValueSelector; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; @@ -29,6 +30,10 @@ impl ValueSelector for InDomainSplitRandom { fn is_restart_pointless(&mut self) -> bool { false } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/value_selection/out_domain_max.rs b/pumpkin-solver/src/branching/value_selection/out_domain_max.rs index 0f645448..fd7522b0 100644 --- a/pumpkin-solver/src/branching/value_selection/out_domain_max.rs +++ b/pumpkin-solver/src/branching/value_selection/out_domain_max.rs @@ -1,3 +1,4 @@ +use crate::branching::brancher::BrancherEvent; use crate::branching::value_selection::ValueSelector; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; @@ -16,6 +17,10 @@ impl ValueSelector for OutDomainMax { ) -> Predicate { predicate!(decision_variable <= context.upper_bound(decision_variable) - 1) } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/value_selection/out_domain_median.rs b/pumpkin-solver/src/branching/value_selection/out_domain_median.rs index 4af50c50..eccb51c0 100644 --- a/pumpkin-solver/src/branching/value_selection/out_domain_median.rs +++ b/pumpkin-solver/src/branching/value_selection/out_domain_median.rs @@ -1,3 +1,4 @@ +use crate::branching::brancher::BrancherEvent; use crate::branching::value_selection::ValueSelector; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; @@ -20,6 +21,10 @@ impl ValueSelector for OutDomainMedian { .collect::>(); predicate!(decision_variable != values_in_domain[values_in_domain.len() / 2]) } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/value_selection/out_domain_min.rs b/pumpkin-solver/src/branching/value_selection/out_domain_min.rs index 348f2754..c944781a 100644 --- a/pumpkin-solver/src/branching/value_selection/out_domain_min.rs +++ b/pumpkin-solver/src/branching/value_selection/out_domain_min.rs @@ -1,3 +1,4 @@ +use crate::branching::brancher::BrancherEvent; use crate::branching::value_selection::ValueSelector; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; @@ -16,6 +17,10 @@ impl ValueSelector for OutDomainMin { ) -> Predicate { predicate!(decision_variable >= context.lower_bound(decision_variable) + 1) } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/value_selection/out_domain_random.rs b/pumpkin-solver/src/branching/value_selection/out_domain_random.rs index 1f746822..a4e97d4f 100644 --- a/pumpkin-solver/src/branching/value_selection/out_domain_random.rs +++ b/pumpkin-solver/src/branching/value_selection/out_domain_random.rs @@ -1,3 +1,4 @@ +use crate::branching::brancher::BrancherEvent; use crate::branching::value_selection::ValueSelector; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; @@ -27,6 +28,10 @@ impl ValueSelector for OutDomainRandom { fn is_restart_pointless(&mut self) -> bool { false } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/value_selection/random_splitter.rs b/pumpkin-solver/src/branching/value_selection/random_splitter.rs index fe97fe2e..6c95655c 100644 --- a/pumpkin-solver/src/branching/value_selection/random_splitter.rs +++ b/pumpkin-solver/src/branching/value_selection/random_splitter.rs @@ -1,4 +1,5 @@ use crate::branching::value_selection::ValueSelector; +use crate::branching::BrancherEvent; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; use crate::engine::variables::DomainId; @@ -44,6 +45,10 @@ impl ValueSelector for RandomSplitter { fn is_restart_pointless(&mut self) -> bool { false } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/value_selection/reverse_in_domain_split.rs b/pumpkin-solver/src/branching/value_selection/reverse_in_domain_split.rs index b357233a..6f6cdfcb 100644 --- a/pumpkin-solver/src/branching/value_selection/reverse_in_domain_split.rs +++ b/pumpkin-solver/src/branching/value_selection/reverse_in_domain_split.rs @@ -1,3 +1,4 @@ +use crate::branching::brancher::BrancherEvent; use crate::branching::value_selection::ValueSelector; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; @@ -32,6 +33,10 @@ impl ValueSelector for ReverseInDomainSplit { ); predicate!(decision_variable >= bound) } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/value_selection/value_selector.rs b/pumpkin-solver/src/branching/value_selection/value_selector.rs index c423dfc1..6f05650c 100644 --- a/pumpkin-solver/src/branching/value_selection/value_selector.rs +++ b/pumpkin-solver/src/branching/value_selection/value_selector.rs @@ -1,8 +1,13 @@ use crate::basic_types::SolutionReference; +use crate::branching::brancher::BrancherEvent; +#[cfg(doc)] +use crate::branching::branchers::dynamic_brancher::DynamicBrancher; #[cfg(doc)] use crate::branching::value_selection::InDomainMin; #[cfg(doc)] use crate::branching::value_selection::InDomainRandom; +#[cfg(doc)] +use crate::branching::Brancher; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; use crate::engine::variables::DomainId; @@ -25,11 +30,17 @@ pub trait ValueSelector { /// [`DomainId`] which has been reset and `value` which is the value to which the variable was /// previously fixed. This method could thus be called multiple times in a single /// backtracking operation by the solver. + /// + /// To receive information about this event, use [`BrancherEvent::UnassignInteger`] in + /// [`Self::subscribe_to_events`] fn on_unassign_integer(&mut self, _variable: DomainId, _value: i32) {} /// This method is called when a solution is found; either when iterating over all solutions in /// the case of a satisfiable problem or on solutions of increasing quality when solving an /// optimisation problem. + /// + /// To receive information about this event, use [`BrancherEvent::Solution`] in + /// [`Self::subscribe_to_events`] fn on_solution(&mut self, _solution: SolutionReference) {} /// This method returns whether a restart is *currently* pointless for the [`ValueSelector`]. @@ -43,4 +54,10 @@ pub trait ValueSelector { fn is_restart_pointless(&mut self) -> bool { true } + + /// Indicates which [`BrancherEvent`] are relevant for this particular [`ValueSelector`]. + /// + /// This can be used by [`Brancher::subscribe_to_events`] to determine upon which + /// events which [`ValueSelector`] should be called. + fn subscribe_to_events(&self) -> Vec; } diff --git a/pumpkin-solver/src/branching/variable_selection/anti_first_fail.rs b/pumpkin-solver/src/branching/variable_selection/anti_first_fail.rs index 98dc0457..b38577c9 100644 --- a/pumpkin-solver/src/branching/variable_selection/anti_first_fail.rs +++ b/pumpkin-solver/src/branching/variable_selection/anti_first_fail.rs @@ -1,5 +1,6 @@ use log::warn; +use crate::branching::brancher::BrancherEvent; use crate::branching::tie_breaking::Direction; use crate::branching::tie_breaking::InOrderTieBreaker; use crate::branching::tie_breaking::TieBreaker; @@ -72,6 +73,10 @@ impl> VariableSelector }); self.tie_breaker.select() } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/variable_selection/dynamic_variable_selector.rs b/pumpkin-solver/src/branching/variable_selection/dynamic_variable_selector.rs index aa98b48f..c4f83dda 100644 --- a/pumpkin-solver/src/branching/variable_selection/dynamic_variable_selector.rs +++ b/pumpkin-solver/src/branching/variable_selection/dynamic_variable_selector.rs @@ -1,6 +1,7 @@ use std::fmt::Debug; use super::VariableSelector; +use crate::branching::brancher::BrancherEvent; #[cfg(doc)] use crate::branching::branchers::dynamic_brancher::DynamicBrancher; use crate::branching::SelectionContext; @@ -45,4 +46,8 @@ impl VariableSelector for DynamicVariableSelector { fn is_restart_pointless(&mut self) -> bool { self.selector.is_restart_pointless() } + + fn subscribe_to_events(&self) -> Vec { + self.selector.subscribe_to_events() + } } diff --git a/pumpkin-solver/src/branching/variable_selection/first_fail.rs b/pumpkin-solver/src/branching/variable_selection/first_fail.rs index 577e05fe..00c61707 100644 --- a/pumpkin-solver/src/branching/variable_selection/first_fail.rs +++ b/pumpkin-solver/src/branching/variable_selection/first_fail.rs @@ -1,5 +1,6 @@ use log::warn; +use crate::branching::brancher::BrancherEvent; use crate::branching::tie_breaking::Direction; use crate::branching::tie_breaking::InOrderTieBreaker; use crate::branching::tie_breaking::TieBreaker; @@ -73,6 +74,10 @@ where }); self.tie_breaker.select() } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/variable_selection/input_order.rs b/pumpkin-solver/src/branching/variable_selection/input_order.rs index 49f8840c..8a60596f 100644 --- a/pumpkin-solver/src/branching/variable_selection/input_order.rs +++ b/pumpkin-solver/src/branching/variable_selection/input_order.rs @@ -1,5 +1,6 @@ use log::warn; +use crate::branching::brancher::BrancherEvent; use crate::branching::variable_selection::VariableSelector; use crate::branching::SelectionContext; use crate::engine::variables::DomainId; @@ -30,6 +31,10 @@ impl VariableSelector for InputOrder { .find(|variable| !context.is_integer_fixed(**variable)) .copied() } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } impl VariableSelector for InputOrder { @@ -39,6 +44,10 @@ impl VariableSelector for InputOrder { .find(|&variable| !context.is_predicate_assigned(variable.get_true_predicate())) .copied() } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/variable_selection/largest.rs b/pumpkin-solver/src/branching/variable_selection/largest.rs index 79f0eb7c..fbe6feca 100644 --- a/pumpkin-solver/src/branching/variable_selection/largest.rs +++ b/pumpkin-solver/src/branching/variable_selection/largest.rs @@ -1,5 +1,6 @@ use log::warn; +use crate::branching::brancher::BrancherEvent; use crate::branching::tie_breaking::Direction; use crate::branching::tie_breaking::InOrderTieBreaker; use crate::branching::tie_breaking::TieBreaker; @@ -76,6 +77,10 @@ where }); self.tie_breaker.select() } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/variable_selection/max_regret.rs b/pumpkin-solver/src/branching/variable_selection/max_regret.rs index 2568a331..02bdffda 100644 --- a/pumpkin-solver/src/branching/variable_selection/max_regret.rs +++ b/pumpkin-solver/src/branching/variable_selection/max_regret.rs @@ -1,5 +1,6 @@ use log::warn; +use crate::branching::brancher::BrancherEvent; use crate::branching::tie_breaking::Direction; use crate::branching::tie_breaking::InOrderTieBreaker; use crate::branching::tie_breaking::TieBreaker; @@ -90,6 +91,10 @@ where }); self.tie_breaker.select() } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/variable_selection/most_constrained.rs b/pumpkin-solver/src/branching/variable_selection/most_constrained.rs index 78febf7f..678a0542 100644 --- a/pumpkin-solver/src/branching/variable_selection/most_constrained.rs +++ b/pumpkin-solver/src/branching/variable_selection/most_constrained.rs @@ -2,6 +2,7 @@ use std::cmp::Ordering; use log::warn; +use crate::branching::brancher::BrancherEvent; use crate::branching::tie_breaking::Direction; use crate::branching::tie_breaking::InOrderTieBreaker; use crate::branching::tie_breaking::TieBreaker; @@ -89,6 +90,10 @@ where self.tie_breaker.select() } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/variable_selection/occurrence.rs b/pumpkin-solver/src/branching/variable_selection/occurrence.rs index 72d20bf5..963f5693 100644 --- a/pumpkin-solver/src/branching/variable_selection/occurrence.rs +++ b/pumpkin-solver/src/branching/variable_selection/occurrence.rs @@ -1,5 +1,6 @@ use log::warn; +use crate::branching::brancher::BrancherEvent; use crate::branching::tie_breaking::Direction; use crate::branching::tie_breaking::InOrderTieBreaker; use crate::branching::tie_breaking::TieBreaker; @@ -55,6 +56,10 @@ where }); self.tie_breaker.select() } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/variable_selection/proportional_domain_size.rs b/pumpkin-solver/src/branching/variable_selection/proportional_domain_size.rs index f75cfcfe..346ebfae 100644 --- a/pumpkin-solver/src/branching/variable_selection/proportional_domain_size.rs +++ b/pumpkin-solver/src/branching/variable_selection/proportional_domain_size.rs @@ -1,4 +1,5 @@ use super::VariableSelector; +use crate::branching::brancher::BrancherEvent; use crate::branching::SelectionContext; use crate::pumpkin_assert_extreme; use crate::variables::DomainId; @@ -67,4 +68,8 @@ impl VariableSelector for ProportionalDomainSize { self.weights_idx_to_variables.push(idx); } } + + fn subscribe_to_events(&self) -> Vec { + vec![BrancherEvent::Backtrack] + } } diff --git a/pumpkin-solver/src/branching/variable_selection/random.rs b/pumpkin-solver/src/branching/variable_selection/random.rs index 6a3a5b4a..c6bf12a4 100644 --- a/pumpkin-solver/src/branching/variable_selection/random.rs +++ b/pumpkin-solver/src/branching/variable_selection/random.rs @@ -1,4 +1,5 @@ use super::VariableSelector; +use crate::branching::BrancherEvent; use crate::branching::SelectionContext; use crate::containers::SparseSet; use crate::containers::StorageKey; @@ -56,6 +57,10 @@ impl VariableSelector for RandomSelector { fn is_restart_pointless(&mut self) -> bool { false } + + fn subscribe_to_events(&self) -> Vec { + vec![BrancherEvent::UnassignInteger] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/variable_selection/smallest.rs b/pumpkin-solver/src/branching/variable_selection/smallest.rs index 6807c0c6..5628039e 100644 --- a/pumpkin-solver/src/branching/variable_selection/smallest.rs +++ b/pumpkin-solver/src/branching/variable_selection/smallest.rs @@ -1,6 +1,7 @@ use log::warn; use super::VariableSelector; +use crate::branching::brancher::BrancherEvent; use crate::branching::tie_breaking::Direction; use crate::branching::tie_breaking::InOrderTieBreaker; use crate::branching::tie_breaking::TieBreaker; @@ -72,6 +73,10 @@ where }); self.tie_breaker.select() } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/variable_selection/variable_selector.rs b/pumpkin-solver/src/branching/variable_selection/variable_selector.rs index 0c1ccd70..1b626073 100644 --- a/pumpkin-solver/src/branching/variable_selection/variable_selector.rs +++ b/pumpkin-solver/src/branching/variable_selection/variable_selector.rs @@ -1,5 +1,10 @@ +use crate::branching::brancher::BrancherEvent; +#[cfg(doc)] +use crate::branching::branchers::dynamic_brancher::DynamicBrancher; #[cfg(doc)] use crate::branching::variable_selection::Smallest; +#[cfg(doc)] +use crate::branching::Brancher; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; use crate::engine::variables::DomainId; @@ -18,19 +23,31 @@ pub trait VariableSelector { /// A function which is called after a conflict has been found and processed but (currently) /// does not provide any additional information. + /// + /// To receive information about this event, use [`BrancherEvent::Conflict`] in + /// [`Self::subscribe_to_events`] fn on_conflict(&mut self) {} /// A function which is called whenever a backtrack occurs in the solver. + /// + /// To receive information about this event, use [`BrancherEvent::Backtrack`] in + /// [`Self::subscribe_to_events`] fn on_backtrack(&mut self) {} /// A function which is called after a [`DomainId`] is unassigned during backtracking (i.e. when /// it was fixed but is no longer), specifically, it provides `variable` which is the /// [`DomainId`] which has been reset. This method could thus be called multiple times in a /// single backtracking operation by the solver. + /// + /// To receive information about this event, use [`BrancherEvent::UnassignInteger`] in + /// [`Self::subscribe_to_events`] fn on_unassign_integer(&mut self, _variable: DomainId, _value: i32) {} /// A function which is called when a [`Predicate`] appears in a conflict during conflict /// analysis. + /// + /// To receive information about this event, use + /// [`BrancherEvent::AppearanceInConflictPredicate`] in [`Self::subscribe_to_events`] fn on_appearance_in_conflict_predicate(&mut self, _predicate: Predicate) {} /// This method returns whether a restart is *currently* pointless for the [`VariableSelector`]. @@ -44,4 +61,10 @@ pub trait VariableSelector { fn is_restart_pointless(&mut self) -> bool { true } + + /// Indicates which [`BrancherEvent`] are relevant for this particular [`VariableSelector`]. + /// + /// This can be used by [`Brancher::subscribe_to_events`] to determine upon which + /// events which [`VariableSelector`] should be called. + fn subscribe_to_events(&self) -> Vec; } diff --git a/pumpkin-solver/src/engine/constraint_satisfaction_solver.rs b/pumpkin-solver/src/engine/constraint_satisfaction_solver.rs index fa88f7fb..34985844 100644 --- a/pumpkin-solver/src/engine/constraint_satisfaction_solver.rs +++ b/pumpkin-solver/src/engine/constraint_satisfaction_solver.rs @@ -35,6 +35,7 @@ use crate::basic_types::Random; use crate::basic_types::SolutionReference; use crate::basic_types::StoredConflictInfo; use crate::branching::Brancher; +use crate::branching::BrancherEvent; use crate::branching::SelectionContext; use crate::engine::conflict_analysis::ConflictResolver as Resolver; use crate::engine::cp::PropagatorQueue; @@ -1668,6 +1669,10 @@ impl Brancher for DummyBrancher { fn next_decision(&mut self, _context: &mut SelectionContext) -> Option { todo!() } + + fn subscribe_to_events(&self) -> Vec { + todo!() + } } #[cfg(test)]