From 9d56afdb2c51ae2d62f5a173a6fcfbbc2a5be327 Mon Sep 17 00:00:00 2001 From: ImkoMarijnissen Date: Wed, 22 Jan 2025 17:06:57 +0100 Subject: [PATCH 1/4] feat: adding initial setup of brancher events --- Cargo.lock | 21 ++++++ pumpkin-solver/Cargo.toml | 1 + pumpkin-solver/src/branching/brancher.rs | 17 +++++ .../branchers/alternating_brancher.rs | 9 +++ .../branching/branchers/autonomous_search.rs | 11 +++ .../branching/branchers/dynamic_brancher.rs | 69 +++++++++++++------ .../independent_variable_value_brancher.rs | 9 +++ .../value_selection/dynamic_value_selector.rs | 5 ++ .../value_selection/value_selector.rs | 5 ++ .../dynamic_variable_selector.rs | 5 ++ .../proportional_domain_size.rs | 5 ++ .../variable_selection/variable_selector.rs | 5 ++ 12 files changed, 141 insertions(+), 21 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..bf91b1a7 100644 --- a/pumpkin-solver/src/branching/brancher.rs +++ b/pumpkin-solver/src/branching/brancher.rs @@ -1,3 +1,5 @@ +use enum_map::Enum; + #[cfg(doc)] use crate::basic_types::Random; use crate::basic_types::SolutionReference; @@ -81,4 +83,19 @@ pub trait Brancher { fn is_restart_pointless(&mut self) -> bool { true } + + fn get_relevant_brancher_events(&self) -> Vec { + vec![] + } +} + +#[derive(Debug, Clone, Copy, Enum, Hash, PartialEq, Eq)] +pub enum BrancherEvents { + Conflict, + Backtrack, + Solution, + UnassignInteger, + AppearanceInConflictPredicate, + Restart, + Synchronise, } diff --git a/pumpkin-solver/src/branching/branchers/alternating_brancher.rs b/pumpkin-solver/src/branching/branchers/alternating_brancher.rs index e42a45d8..08239958 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::BrancherEvents; use crate::branching::Brancher; use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; @@ -208,6 +209,14 @@ impl Brancher for AlternatingBrancher { self.other_brancher.synchronise(assignments); } } + + fn get_relevant_brancher_events(&self) -> Vec { + self.default_brancher + .get_relevant_brancher_events() + .into_iter() + .chain(self.other_brancher.get_relevant_brancher_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 0522129a..e5b1e266 100644 --- a/pumpkin-solver/src/branching/branchers/autonomous_search.rs +++ b/pumpkin-solver/src/branching/branchers/autonomous_search.rs @@ -2,6 +2,7 @@ use super::independent_variable_value_brancher::IndependentVariableValueBrancher use crate::basic_types::PredicateId; use crate::basic_types::PredicateIdGenerator; use crate::basic_types::SolutionReference; +use crate::branching::brancher::BrancherEvents; use crate::branching::value_selection::InDomainMin; use crate::branching::variable_selection::Smallest; use crate::branching::Brancher; @@ -297,6 +298,16 @@ impl Brancher for AutonomousSearch { fn is_restart_pointless(&mut self) -> bool { false } + + fn get_relevant_brancher_events(&self) -> Vec { + vec![ + BrancherEvents::Solution, + BrancherEvents::Conflict, + BrancherEvents::Backtrack, + BrancherEvents::Synchronise, + BrancherEvents::AppearanceInConflictPredicate, + ] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/branchers/dynamic_brancher.rs b/pumpkin-solver/src/branching/branchers/dynamic_brancher.rs index 22d18e7a..e796bb3b 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::BrancherEvents; 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,9 +47,21 @@ 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(); + + branchers.iter().enumerate().for_each(|(index, brancher)| { + for event in brancher.get_relevant_brancher_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(), } } @@ -70,46 +89,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[BrancherEvents::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[BrancherEvents::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[BrancherEvents::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[BrancherEvents::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[BrancherEvents::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[BrancherEvents::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[BrancherEvents::Synchronise] + .iter() + .for_each(|&brancher_index| self.branchers[brancher_index].synchronise(assignments)); } fn is_restart_pointless(&mut self) -> bool { @@ -120,4 +143,8 @@ impl Brancher for DynamicBrancher { .iter_mut() .all(|brancher| brancher.is_restart_pointless()) } + + fn get_relevant_brancher_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..7a9f1d06 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::BrancherEvents; 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 get_relevant_brancher_events(&self) -> Vec { + self.variable_selector + .get_relevant_brancher_events() + .into_iter() + .chain(self.value_selector.get_relevant_brancher_events()) + .collect() + } } 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..f049ed26 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::BrancherEvents; #[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 get_relevant_brancher_events(&self) -> Vec { + self.selector.get_relevant_brancher_events() + } } diff --git a/pumpkin-solver/src/branching/value_selection/value_selector.rs b/pumpkin-solver/src/branching/value_selection/value_selector.rs index c423dfc1..0da955ea 100644 --- a/pumpkin-solver/src/branching/value_selection/value_selector.rs +++ b/pumpkin-solver/src/branching/value_selection/value_selector.rs @@ -1,4 +1,5 @@ use crate::basic_types::SolutionReference; +use crate::branching::brancher::BrancherEvents; #[cfg(doc)] use crate::branching::value_selection::InDomainMin; #[cfg(doc)] @@ -43,4 +44,8 @@ pub trait ValueSelector { fn is_restart_pointless(&mut self) -> bool { true } + + fn get_relevant_brancher_events(&self) -> Vec { + vec![] + } } 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..a2cd5017 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::BrancherEvents; #[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 get_relevant_brancher_events(&self) -> Vec { + self.selector.get_relevant_brancher_events() + } } 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..f66fabf3 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::BrancherEvents; 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 get_relevant_brancher_events(&self) -> Vec { + vec![BrancherEvents::Backtrack] + } } diff --git a/pumpkin-solver/src/branching/variable_selection/variable_selector.rs b/pumpkin-solver/src/branching/variable_selection/variable_selector.rs index 0c1ccd70..e543b80e 100644 --- a/pumpkin-solver/src/branching/variable_selection/variable_selector.rs +++ b/pumpkin-solver/src/branching/variable_selection/variable_selector.rs @@ -1,3 +1,4 @@ +use crate::branching::brancher::BrancherEvents; #[cfg(doc)] use crate::branching::variable_selection::Smallest; use crate::branching::SelectionContext; @@ -44,4 +45,8 @@ pub trait VariableSelector { fn is_restart_pointless(&mut self) -> bool { true } + + fn get_relevant_brancher_events(&self) -> Vec { + vec![] + } } From 0f06a52890e401a6eb189c83bef92c8ba5e4936f Mon Sep 17 00:00:00 2001 From: ImkoMarijnissen Date: Thu, 23 Jan 2025 09:00:50 +0100 Subject: [PATCH 2/4] refactor: adding documentation + making default to be subscribed to all brancher events --- pumpkin-solver/src/branching/brancher.rs | 28 ++++++++++++++++++- .../branchers/alternating_brancher.rs | 6 ++-- pumpkin-solver/src/branching/mod.rs | 2 +- .../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 ++++ .../reverse_in_domain_split.rs | 5 ++++ .../value_selection/value_selector.rs | 20 ++++++++++++- .../variable_selection/anti_first_fail.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 ++++ .../branching/variable_selection/smallest.rs | 5 ++++ .../variable_selection/variable_selector.rs | 20 ++++++++++++- 26 files changed, 183 insertions(+), 6 deletions(-) diff --git a/pumpkin-solver/src/branching/brancher.rs b/pumpkin-solver/src/branching/brancher.rs index bf91b1a7..813f480f 100644 --- a/pumpkin-solver/src/branching/brancher.rs +++ b/pumpkin-solver/src/branching/brancher.rs @@ -6,6 +6,8 @@ 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; @@ -84,18 +86,42 @@ pub trait Brancher { true } + /// Indicates which [`BrancherEvents`] are relevant for this particular [`Brancher`]. + /// + /// This can be used by [`Brancher`]s such as the [`DynamicBrancher`] to determine upon which + /// events which [`Brancher`] should be called. + /// + /// By default, a [`Brancher`] is subscribed to all events. fn get_relevant_brancher_events(&self) -> Vec { - vec![] + vec![ + BrancherEvents::Conflict, + BrancherEvents::Backtrack, + BrancherEvents::Solution, + BrancherEvents::UnassignInteger, + BrancherEvents::AppearanceInConflictPredicate, + BrancherEvents::Restart, + BrancherEvents::Synchronise, + ] } } +/// The events which can occur for a [`Brancher`]. Used for returning which events are relevant in +/// [`Brancher::get_relevant_brancher_events`], [`VariableSelector::get_relevant_brancher_events`], +/// and [`ValueSelector::get_relevant_brancher_events`]. #[derive(Debug, Clone, Copy, Enum, Hash, PartialEq, Eq)] pub enum BrancherEvents { + /// 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 08239958..52a42a03 100644 --- a/pumpkin-solver/src/branching/branchers/alternating_brancher.rs +++ b/pumpkin-solver/src/branching/branchers/alternating_brancher.rs @@ -211,9 +211,11 @@ impl Brancher for AlternatingBrancher { } fn get_relevant_brancher_events(&self) -> Vec { - self.default_brancher - .get_relevant_brancher_events() + // 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 + [BrancherEvents::Restart, BrancherEvents::Solution] .into_iter() + .chain(self.default_brancher.get_relevant_brancher_events()) .chain(self.other_brancher.get_relevant_brancher_events()) .collect() } diff --git a/pumpkin-solver/src/branching/mod.rs b/pumpkin-solver/src/branching/mod.rs index be69b65f..c19ffdf2 100644 --- a/pumpkin-solver/src/branching/mod.rs +++ b/pumpkin-solver/src/branching/mod.rs @@ -72,7 +72,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/in_domain_interval.rs b/pumpkin-solver/src/branching/value_selection/in_domain_interval.rs index a7315855..9916e4ec 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::BrancherEvents; 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 get_relevant_brancher_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..88e5c8fb 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::BrancherEvents; 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 get_relevant_brancher_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..fcad238d 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::BrancherEvents; 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 get_relevant_brancher_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..b41ddef5 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::BrancherEvents; #[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 get_relevant_brancher_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..93bd1d38 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::BrancherEvents; 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 get_relevant_brancher_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..d089419e 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::BrancherEvents; 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 get_relevant_brancher_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 get_relevant_brancher_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..b537e119 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::BrancherEvents; 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 get_relevant_brancher_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..0b463b5e 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::BrancherEvents; 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 get_relevant_brancher_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..a732cd3d 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::BrancherEvents; 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 get_relevant_brancher_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..6531d372 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::BrancherEvents; 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 get_relevant_brancher_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..bcc48575 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::BrancherEvents; 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 get_relevant_brancher_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..bc7de676 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::BrancherEvents; 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 get_relevant_brancher_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..9a73962b 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::BrancherEvents; 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 get_relevant_brancher_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 0da955ea..42541731 100644 --- a/pumpkin-solver/src/branching/value_selection/value_selector.rs +++ b/pumpkin-solver/src/branching/value_selection/value_selector.rs @@ -1,9 +1,13 @@ use crate::basic_types::SolutionReference; use crate::branching::brancher::BrancherEvents; #[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; @@ -45,7 +49,21 @@ pub trait ValueSelector { true } + /// Indicates which [`BrancherEvents`] are relevant for this particular [`ValueSelector`]. + /// + /// This can be used by [`Brancher`]s such as the [`DynamicBrancher`] to determine upon which + /// events which [`ValueSelector`] should be called. + /// + /// By default, a [`ValueSelector`] is subscribed to all events. fn get_relevant_brancher_events(&self) -> Vec { - vec![] + vec![ + BrancherEvents::Conflict, + BrancherEvents::Backtrack, + BrancherEvents::Solution, + BrancherEvents::UnassignInteger, + BrancherEvents::AppearanceInConflictPredicate, + BrancherEvents::Restart, + BrancherEvents::Synchronise, + ] } } 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..340e7165 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::BrancherEvents; 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 get_relevant_brancher_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/variable_selection/first_fail.rs b/pumpkin-solver/src/branching/variable_selection/first_fail.rs index 577e05fe..49ec8012 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::BrancherEvents; 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 get_relevant_brancher_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..e752cd5f 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::BrancherEvents; 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 get_relevant_brancher_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 get_relevant_brancher_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..f87cc562 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::BrancherEvents; 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 get_relevant_brancher_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..1f90c6f9 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::BrancherEvents; 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 get_relevant_brancher_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..e753d495 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::BrancherEvents; 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 get_relevant_brancher_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..a1c53bf7 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::BrancherEvents; 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 get_relevant_brancher_events(&self) -> Vec { + vec![] + } } #[cfg(test)] diff --git a/pumpkin-solver/src/branching/variable_selection/smallest.rs b/pumpkin-solver/src/branching/variable_selection/smallest.rs index 6807c0c6..50d395ae 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::BrancherEvents; 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 get_relevant_brancher_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 e543b80e..ec27160b 100644 --- a/pumpkin-solver/src/branching/variable_selection/variable_selector.rs +++ b/pumpkin-solver/src/branching/variable_selection/variable_selector.rs @@ -1,6 +1,10 @@ use crate::branching::brancher::BrancherEvents; #[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; @@ -46,7 +50,21 @@ pub trait VariableSelector { true } + /// Indicates which [`BrancherEvents`] are relevant for this particular [`VariableSelector`]. + /// + /// This can be used by [`Brancher`]s such as the [`DynamicBrancher`] to determine upon which + /// events which [`VariableSelector`] should be called. + /// + /// By default, a [`VariableSelector`] is subscribed to all events. fn get_relevant_brancher_events(&self) -> Vec { - vec![] + vec![ + BrancherEvents::Conflict, + BrancherEvents::Backtrack, + BrancherEvents::Solution, + BrancherEvents::UnassignInteger, + BrancherEvents::AppearanceInConflictPredicate, + BrancherEvents::Restart, + BrancherEvents::Synchronise, + ] } } From 87a9815c327dd3a8a39ab849c3ac249c3b3b8a84 Mon Sep 17 00:00:00 2001 From: ImkoMarijnissen Date: Thu, 23 Jan 2025 10:26:54 +0100 Subject: [PATCH 3/4] fix: adding solution and conflict events to dynamic brancher + adding events when add_brancher method is used --- .../src/branching/branchers/dynamic_brancher.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pumpkin-solver/src/branching/branchers/dynamic_brancher.rs b/pumpkin-solver/src/branching/branchers/dynamic_brancher.rs index e796bb3b..546f826b 100644 --- a/pumpkin-solver/src/branching/branchers/dynamic_brancher.rs +++ b/pumpkin-solver/src/branching/branchers/dynamic_brancher.rs @@ -50,6 +50,10 @@ impl DynamicBrancher { 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(BrancherEvents::Solution); + let _ = relevant_events.insert(BrancherEvents::Conflict); + branchers.iter().enumerate().for_each(|(index, brancher)| { for event in brancher.get_relevant_brancher_events() { relevant_event_to_index[event].push(index); @@ -66,7 +70,13 @@ impl DynamicBrancher { } pub fn add_brancher(&mut self, brancher: Box) { - self.branchers.push(brancher) + for event in brancher.get_relevant_brancher_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); } } From 15786410764284f119524b197d011d4aa4522764 Mon Sep 17 00:00:00 2001 From: ImkoMarijnissen Date: Thu, 23 Jan 2025 11:42:49 +0100 Subject: [PATCH 4/4] fix: also include the branching events of the backup selector in autonomous search when getting the relevant events --- pumpkin-solver/src/branching/branchers/autonomous_search.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pumpkin-solver/src/branching/branchers/autonomous_search.rs b/pumpkin-solver/src/branching/branchers/autonomous_search.rs index e5b1e266..d18939a8 100644 --- a/pumpkin-solver/src/branching/branchers/autonomous_search.rs +++ b/pumpkin-solver/src/branching/branchers/autonomous_search.rs @@ -300,13 +300,16 @@ impl Brancher for AutonomousSearch { } fn get_relevant_brancher_events(&self) -> Vec { - vec![ + [ BrancherEvents::Solution, BrancherEvents::Conflict, BrancherEvents::Backtrack, BrancherEvents::Synchronise, BrancherEvents::AppearanceInConflictPredicate, ] + .into_iter() + .chain(self.backup_brancher.get_relevant_brancher_events()) + .collect() } }