From 39f97fd8f43b705a4fb54e6ed814d67bdf7fdc66 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Wed, 27 May 2026 22:09:21 +1000 Subject: [PATCH 01/12] PropagatatorConstructor::create now returns an EventRegistration --- pumpkin-crates/core/src/engine/state.rs | 2 +- .../core/src/propagation/constructor.rs | 6 +- .../src/propagation/event_registration.rs | 99 +++++++++++++++++++ pumpkin-crates/core/src/propagation/mod.rs | 3 + .../hypercube_linear/propagator.rs | 14 ++- .../propagators/nogoods/nogood_propagator.rs | 20 ++-- .../src/propagators/reified_propagator.rs | 14 ++- 7 files changed, 138 insertions(+), 20 deletions(-) create mode 100644 pumpkin-crates/core/src/propagation/event_registration.rs diff --git a/pumpkin-crates/core/src/engine/state.rs b/pumpkin-crates/core/src/engine/state.rs index 9e358fab6..cb3fe554a 100644 --- a/pumpkin-crates/core/src/engine/state.rs +++ b/pumpkin-crates/core/src/engine/state.rs @@ -341,7 +341,7 @@ impl State { let constructor_context = PropagatorConstructorContext::new(original_handle.propagator_id(), self); - let propagator = constructor.create(constructor_context); + let (registration, propagator) = constructor.create(constructor_context); pumpkin_assert_simple!( propagator.priority() as u8 <= 3, diff --git a/pumpkin-crates/core/src/propagation/constructor.rs b/pumpkin-crates/core/src/propagation/constructor.rs index 791627880..e968ee61f 100644 --- a/pumpkin-crates/core/src/propagation/constructor.rs +++ b/pumpkin-crates/core/src/propagation/constructor.rs @@ -25,6 +25,7 @@ use crate::proof::InferenceCode; #[cfg(doc)] use crate::propagation::DomainEvent; use crate::propagation::DomainEvents; +use crate::propagation::EventRegistration; use crate::propagators::reified_propagator::ReifiedChecker; use crate::variables::IntegerVariable; use crate::variables::Literal; @@ -48,7 +49,10 @@ pub trait PropagatorConstructor { fn add_inference_checkers(&self, _checkers: InferenceCheckers<'_>) {} /// Create the propagator instance from `Self`. - fn create(self, context: PropagatorConstructorContext) -> Self::PropagatorImpl; + fn create( + self, + context: PropagatorConstructorContext, + ) -> (EventRegistration, Self::PropagatorImpl); } /// Interface used to add [`InferenceChecker`]s to the [`State`]. diff --git a/pumpkin-crates/core/src/propagation/event_registration.rs b/pumpkin-crates/core/src/propagation/event_registration.rs new file mode 100644 index 000000000..cc096ea42 --- /dev/null +++ b/pumpkin-crates/core/src/propagation/event_registration.rs @@ -0,0 +1,99 @@ +use crate::propagation::DomainEvents; +use crate::propagation::LocalId; +use crate::variables::DomainId; + +/// Anything that can subscribe to domain events. +pub trait EventTarget { + /// Add a registration of self for the given domain events with a local id. + fn register( + &self, + registration: &mut EventRegistration, + events: DomainEvents, + local_id: LocalId, + ); +} + +/// Contains all the events and domains that a propagator needs to be enqueued for. +#[derive(Clone, Debug)] +pub struct EventRegistration(Vec<(DomainId, DomainEvents, LocalId)>); + +impl EventRegistration { + /// Create an [`EventRegistration`] without any variables. + /// + /// This is the uncommon case. Without registering for variable events, a propagator will never + /// be enqueued. + pub fn empty() -> EventRegistration { + EventRegistration(vec![]) + } + + /// Create a new [`EventRegistrationBuilder`]. + /// + /// If no event registrations will be made, use [`EventRegistration::empty`] instead. + /// Calling [`EventRegistrationBuilder::build`] without any registrations will cause a panic. + /// + /// # Example + /// + /// ``` + /// let registration = EventRegistration::builder() + /// .add(LocalId::from(0), &v1) + /// .add(LocalId::from(1), &v2) + /// .build(); + /// ``` + pub fn builder() -> EventRegistrationBuilder { + EventRegistrationBuilder { + registrations: EventRegistration(vec![]), + } + } + + /// Add a new event registration. + pub fn add( + &mut self, + target: &impl EventTarget, + domain_events: DomainEvents, + local_id: LocalId, + ) { + target.register(self, domain_events, local_id); + } + + /// Register the [`DomainId`] with the given [`LocalId`] on the given [`DomainEvents`]. + /// + /// When creating the [`EventRegistration`] in a propagator, prefer to use + /// [`EventRegistration::add`] to deal with domain views. + pub fn with_domain(&mut self, domain_id: DomainId, events: DomainEvents, local_id: LocalId) { + self.0.push((domain_id, events, local_id)); + } +} + +/// Used to construct an [`EventRegistration`] for heterogeneous [`EventTarget`] implementations. +/// +/// See [`EventRegistration::builder`] for a usage example. +#[derive(Clone, Debug)] +pub struct EventRegistrationBuilder { + registrations: EventRegistration, +} + +impl EventRegistrationBuilder { + /// Add a new event registration. + pub fn add( + mut self, + target: &impl EventTarget, + domain_events: DomainEvents, + local_id: LocalId, + ) -> Self { + self.registrations.add(target, domain_events, local_id); + self + } + + /// Finish constructing the [`EventRegistration`]. + /// + /// If no variables are registered, then this panics. If no variables can be registered during + /// construction, use [`EventRegistration::empty`]. + pub fn build(self) -> EventRegistration { + assert!( + !self.registrations.0.is_empty(), + "did not register for any events" + ); + + self.registrations + } +} diff --git a/pumpkin-crates/core/src/propagation/mod.rs b/pumpkin-crates/core/src/propagation/mod.rs index 7c4b5b02b..c74ee7106 100644 --- a/pumpkin-crates/core/src/propagation/mod.rs +++ b/pumpkin-crates/core/src/propagation/mod.rs @@ -69,6 +69,7 @@ mod constructor; mod contexts; mod domains; +mod event_registration; mod local_id; mod propagator; @@ -76,6 +77,8 @@ pub(crate) mod propagator_id; pub(crate) mod propagator_var_id; pub(crate) mod store; +pub use event_registration::*; + mod reexports { // Re-exports of types not in this module according to the file tree. // These will probably be be moved at some point, but for now they are simply re-exported here diff --git a/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs b/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs index fc1e0a3f7..0f6068935 100644 --- a/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs +++ b/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs @@ -7,6 +7,7 @@ use crate::predicates::PropositionalConjunction; use crate::proof::ConstraintTag; use crate::proof::InferenceCode; use crate::propagation::DomainEvents; +use crate::propagation::EventRegistration; use crate::propagation::InferenceCheckers; use crate::propagation::LocalId; use crate::propagation::PropagationContext; @@ -44,7 +45,10 @@ impl PropagatorConstructor for HypercubeLinearConstructor { ); } - fn create(self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create( + self, + mut context: PropagatorConstructorContext, + ) -> (EventRegistration, Self::PropagatorImpl) { let HypercubeLinearConstructor { hypercube, linear, @@ -65,13 +69,17 @@ impl PropagatorConstructor for HypercubeLinearConstructor { ] }; - HypercubeLinearPropagator { + let propagator = HypercubeLinearPropagator { linear, hypercube_predicates, watched_predicates, inference_code: InferenceCode::new(constraint_tag, HypercubeLinear), - } + }; + + let registration = EventRegistration::builder().build(); + + (registration, propagator) } } diff --git a/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs b/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs index 4312f65cd..e310d340c 100644 --- a/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs +++ b/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs @@ -21,6 +21,7 @@ use crate::engine::reason::ReasonStore; use crate::predicate; use crate::proof::InferenceCode; use crate::propagation::EnqueueDecision; +use crate::propagation::EventRegistration; use crate::propagation::ExplanationContext; use crate::propagation::LazyExplanation; use crate::propagation::NotificationContext; @@ -101,10 +102,13 @@ impl NogoodPropagatorConstructor { impl PropagatorConstructor for NogoodPropagatorConstructor { type PropagatorImpl = NogoodPropagator; - fn create(self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create( + self, + mut context: PropagatorConstructorContext, + ) -> (EventRegistration, Self::PropagatorImpl) { context.will_not_register_any_events(); - NogoodPropagator { + let propagator = NogoodPropagator { handle: PropagatorHandle::new(context.propagator_id), parameters: self.parameters, nogood_predicates: ArenaAllocator::new(self.capacity), @@ -117,7 +121,9 @@ impl PropagatorConstructor for NogoodPropagatorConstructor { lbd_helper: Default::default(), bumped_nogoods: Default::default(), temp_nogood_reason: Default::default(), - } + }; + + (EventRegistration::empty(), propagator) } } @@ -133,14 +139,6 @@ struct Watcher { cached_predicate: PredicateId, } -impl PropagatorConstructor for NogoodPropagator { - type PropagatorImpl = Self; - - fn create(self, _: PropagatorConstructorContext) -> Self::PropagatorImpl { - self - } -} - /// Keeps track of three tiers of nogoods: /// - "low" LBD nogoods /// - "mid" LBD nogoods diff --git a/pumpkin-crates/core/src/propagators/reified_propagator.rs b/pumpkin-crates/core/src/propagators/reified_propagator.rs index 0bb782495..f30044cdd 100644 --- a/pumpkin-crates/core/src/propagators/reified_propagator.rs +++ b/pumpkin-crates/core/src/propagators/reified_propagator.rs @@ -9,6 +9,7 @@ use crate::predicates::Predicate; use crate::propagation::DomainEvents; use crate::propagation::Domains; use crate::propagation::EnqueueDecision; +use crate::propagation::EventRegistration; use crate::propagation::ExplanationContext; use crate::propagation::InferenceCheckers; use crate::propagation::LazyExplanation; @@ -38,13 +39,16 @@ where { type PropagatorImpl = ReifiedPropagator; - fn create(self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create( + self, + mut context: PropagatorConstructorContext, + ) -> (EventRegistration, Self::PropagatorImpl) { let ReifiedPropagatorArgs { propagator, reification_literal, } = self; - let propagator = propagator.create(context.reborrow()); + let (registration, propagator) = propagator.create(context.reborrow()); let reification_literal_id = context.get_next_local_id(); context.register( @@ -55,13 +59,15 @@ where let name = format!("Reified({})", propagator.name()); - ReifiedPropagator { + let propagator = ReifiedPropagator { propagator, reification_literal, reification_literal_id, name, reason_buffer: vec![], - } + }; + + (EventRegistration::builder().build(), propagator) } fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { From 4dd42225ba27f6d6758378244848285062142606 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Wed, 27 May 2026 22:26:41 +1000 Subject: [PATCH 02/12] Remove registration from PropagatorConstructorContext --- pumpkin-crates/core/src/basic_types/mod.rs | 2 - .../core/src/basic_types/ref_or_owned.rs | 49 ---------- .../core/src/engine/variables/affine_view.rs | 19 ++++ .../core/src/engine/variables/domain_id.rs | 14 +++ .../core/src/engine/variables/literal.rs | 15 +++ .../core/src/propagation/constructor.rs | 93 ------------------- .../src/propagation/event_registration.rs | 21 ++++- .../core/src/propagation/local_id.rs | 5 + .../propagators/nogoods/nogood_propagator.rs | 4 +- .../src/propagators/reified_propagator.rs | 14 ++- 10 files changed, 81 insertions(+), 155 deletions(-) delete mode 100644 pumpkin-crates/core/src/basic_types/ref_or_owned.rs diff --git a/pumpkin-crates/core/src/basic_types/mod.rs b/pumpkin-crates/core/src/basic_types/mod.rs index b87c9a856..b1fd78c9c 100644 --- a/pumpkin-crates/core/src/basic_types/mod.rs +++ b/pumpkin-crates/core/src/basic_types/mod.rs @@ -4,7 +4,6 @@ mod function; mod predicate_id_generators; mod propositional_conjunction; mod random; -mod ref_or_owned; pub(crate) mod sequence_generators; mod solution; mod stored_conflict_info; @@ -19,7 +18,6 @@ pub use predicate_id_generators::PredicateId; pub use predicate_id_generators::PredicateIdGenerator; pub use propositional_conjunction::PropositionalConjunction; pub use random::*; -pub(crate) use ref_or_owned::*; pub use solution::ProblemSolution; pub use solution::Solution; pub use solution::SolutionReference; diff --git a/pumpkin-crates/core/src/basic_types/ref_or_owned.rs b/pumpkin-crates/core/src/basic_types/ref_or_owned.rs deleted file mode 100644 index f16f3d6c1..000000000 --- a/pumpkin-crates/core/src/basic_types/ref_or_owned.rs +++ /dev/null @@ -1,49 +0,0 @@ -use std::ops::Deref; -use std::ops::DerefMut; - -/// Either owns a value or has a mutable reference to a value. -/// -/// Used to store data in a reborrowed context that needs to be 'shared' with the original context -/// that was reborrowed from. For example, when dropping a reborrowed context, we want -/// [`PropagatorConstructorContext::get_next_local_id`] in the original context to 'know' about the -/// registered local ids in the reborrowed context. -#[derive(Debug)] -pub(crate) enum RefOrOwned<'a, T> { - Ref(&'a mut T), - Owned(T), -} - -impl RefOrOwned<'_, T> { - pub(crate) fn reborrow(&mut self) -> RefOrOwned<'_, T> { - match self { - RefOrOwned::Ref(ref_to_t) => RefOrOwned::Ref(ref_to_t), - RefOrOwned::Owned(value) => RefOrOwned::Ref(value), - } - } -} - -impl From for RefOrOwned<'_, T> { - fn from(value: T) -> Self { - RefOrOwned::Owned(value) - } -} - -impl Deref for RefOrOwned<'_, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - match self { - RefOrOwned::Ref(reference) => reference, - RefOrOwned::Owned(value) => value, - } - } -} - -impl DerefMut for RefOrOwned<'_, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - match self { - RefOrOwned::Ref(reference) => reference, - RefOrOwned::Owned(value) => value, - } - } -} diff --git a/pumpkin-crates/core/src/engine/variables/affine_view.rs b/pumpkin-crates/core/src/engine/variables/affine_view.rs index 3ce96acfb..c0e41a5ec 100644 --- a/pumpkin-crates/core/src/engine/variables/affine_view.rs +++ b/pumpkin-crates/core/src/engine/variables/affine_view.rs @@ -14,6 +14,9 @@ use crate::engine::predicates::predicate_constructor::PredicateConstructor; use crate::engine::variables::DomainId; use crate::engine::variables::IntegerVariable; use crate::math::num_ext::NumExt; +use crate::propagation::EventRegistration; +use crate::propagation::EventTarget; +use crate::propagation::LocalId; /// Models the constraint `y = ax + b`, by expressing the domain of `y` as a transformation of the /// domain of `x`. @@ -54,6 +57,22 @@ impl AffineView { } } +impl EventTarget for AffineView { + fn register( + &self, + registration: &mut EventRegistration, + mut events: EnumSet, + local_id: LocalId, + ) { + let bound = DomainEvent::LowerBound | DomainEvent::UpperBound; + let intersection = events.intersection(bound); + if intersection.len() == 1 && self.scale.is_negative() { + events = events.symmetric_difference(bound); + } + self.inner.register(registration, events, local_id); + } +} + impl CheckerVariable for AffineView { fn does_atomic_constrain_self(&self, atomic: &Predicate) -> bool { self.inner.does_atomic_constrain_self(atomic) diff --git a/pumpkin-crates/core/src/engine/variables/domain_id.rs b/pumpkin-crates/core/src/engine/variables/domain_id.rs index 22ede3a40..b384e749f 100644 --- a/pumpkin-crates/core/src/engine/variables/domain_id.rs +++ b/pumpkin-crates/core/src/engine/variables/domain_id.rs @@ -12,6 +12,9 @@ use crate::engine::variables::IntegerVariable; use crate::predicates::Predicate; use crate::predicates::PredicateConstructor; use crate::predicates::PredicateType; +use crate::propagation::EventRegistration; +use crate::propagation::EventTarget; +use crate::propagation::LocalId; use crate::pumpkin_assert_simple; /// A structure which represents the most basic [`IntegerVariable`]; it is simply the id which links @@ -32,6 +35,17 @@ impl DomainId { } } +impl EventTarget for DomainId { + fn register( + &self, + registration: &mut EventRegistration, + events: EnumSet, + local_id: LocalId, + ) { + registration.with_domain(*self, events, local_id); + } +} + impl CheckerVariable for DomainId { fn does_atomic_constrain_self(&self, atomic: &Predicate) -> bool { atomic.get_domain() == *self diff --git a/pumpkin-crates/core/src/engine/variables/literal.rs b/pumpkin-crates/core/src/engine/variables/literal.rs index 980ffa737..8cc72777f 100644 --- a/pumpkin-crates/core/src/engine/variables/literal.rs +++ b/pumpkin-crates/core/src/engine/variables/literal.rs @@ -15,6 +15,9 @@ use crate::engine::notifications::Watchers; use crate::engine::predicates::predicate::Predicate; use crate::engine::predicates::predicate_constructor::PredicateConstructor; use crate::engine::variables::AffineView; +use crate::propagation::EventRegistration; +use crate::propagation::EventTarget; +use crate::propagation::LocalId; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Literal { @@ -73,6 +76,18 @@ macro_rules! forward { } } +impl EventTarget for Literal { + fn register( + &self, + registration: &mut EventRegistration, + events: EnumSet, + local_id: LocalId, + ) { + self.integer_variable + .register(registration, events, local_id); + } +} + impl CheckerVariable for Literal { forward!(integer_variable, fn does_atomic_constrain_self(&self, atomic: &Predicate) -> bool); forward!(integer_variable, fn atomic_less_than(&self, value: i32) -> Predicate); diff --git a/pumpkin-crates/core/src/propagation/constructor.rs b/pumpkin-crates/core/src/propagation/constructor.rs index e968ee61f..74eb6281d 100644 --- a/pumpkin-crates/core/src/propagation/constructor.rs +++ b/pumpkin-crates/core/src/propagation/constructor.rs @@ -1,6 +1,3 @@ -use std::ops::Deref; -use std::ops::DerefMut; - use pumpkin_checking::InferenceChecker; use super::Domains; @@ -11,7 +8,6 @@ use super::PropagatorVarId; #[cfg(doc)] use crate::Solver; use crate::basic_types::PredicateId; -use crate::basic_types::RefOrOwned; use crate::engine::Assignments; use crate::engine::State; use crate::engine::TrailedValues; @@ -105,15 +101,6 @@ impl InferenceCheckers<'_> { pub struct PropagatorConstructorContext<'a> { state: &'a mut State, pub(crate) propagator_id: PropagatorId, - - /// A [`LocalId`] that is guaranteed not to be used to register any variables yet. This is - /// either a reference or an owned value, to support - /// [`PropagatorConstructorContext::reborrow`]. - next_local_id: RefOrOwned<'a, LocalId>, - - /// Marker to indicate whether the constructor registered for at least one domain event or - /// predicate becoming assigned. If not, the [`Drop`] implementation will cause a panic. - did_register: RefOrOwned<'a, bool>, } impl PropagatorConstructorContext<'_> { @@ -122,61 +109,19 @@ impl PropagatorConstructorContext<'_> { state: &'a mut State, ) -> PropagatorConstructorContext<'a> { PropagatorConstructorContext { - next_local_id: RefOrOwned::Owned(LocalId::from(0)), propagator_id, state, - did_register: RefOrOwned::Owned(false), } } - /// Indicate that the constructor is deliberately not registering the propagator to be enqueued - /// at any time. - /// - /// If this is called and later a registration happens, then the registration will still go - /// through. Calling this function only prevents the crash if no registration happens. - pub fn will_not_register_any_events(&mut self) { - *self.did_register = true; - } - /// Get domain information. pub fn domains(&mut self) -> Domains<'_> { Domains::new(&self.state.assignments, &mut self.state.trailed_values) } - /// Subscribes the propagator to the given [`DomainEvents`]. - /// - /// The domain events determine when [`Propagator::notify()`] will be called on the propagator. - /// The [`LocalId`] is internal information related to the propagator, - /// which is used when calling [`Propagator::notify()`] to identify the variable. - /// - /// Each variable *must* have a unique [`LocalId`]. Most often this would be its index of the - /// variable in the internal array of variables. - /// - /// Duplicate registrations are ignored. - pub fn register( - &mut self, - var: impl IntegerVariable, - domain_events: DomainEvents, - local_id: LocalId, - ) { - self.will_not_register_any_events(); - - let propagator_var = PropagatorVarId { - propagator: self.propagator_id, - variable: local_id, - }; - - self.update_next_local_id(local_id); - - let mut watchers = Watchers::new(propagator_var, &mut self.state.notification_engine); - var.watch_all(&mut watchers, domain_events.events()); - } - /// Register the propagator to be enqueued when the given [`Predicate`] becomes true. /// Returns the [`PredicateId`] used by the solver to track the predicate. pub fn register_predicate(&mut self, predicate: Predicate) -> PredicateId { - self.will_not_register_any_events(); - self.state.notification_engine.watch_predicate( predicate, self.propagator_id, @@ -209,25 +154,16 @@ impl PropagatorConstructorContext<'_> { variable: local_id, }; - self.update_next_local_id(local_id); - let mut watchers = Watchers::new(propagator_var, &mut self.state.notification_engine); var.watch_all_backtrack(&mut watchers, domain_events.events()); } - /// Get a new [`LocalId`] which is guaranteed to be unused. - pub(crate) fn get_next_local_id(&self) -> LocalId { - *self.next_local_id.deref() - } - /// Reborrow the current context to a new value with a shorter lifetime. Should be used when /// passing `Self` to another function that takes ownership, but the value is still needed /// afterwards. pub fn reborrow(&mut self) -> PropagatorConstructorContext<'_> { PropagatorConstructorContext { propagator_id: self.propagator_id, - next_local_id: self.next_local_id.reborrow(), - did_register: self.did_register.reborrow(), state: self.state, } } @@ -243,35 +179,6 @@ impl PropagatorConstructorContext<'_> { ) { self.state.add_inference_checker(inference_code, checker); } - - /// Set the next local id to be at least one more than the largest encountered local id. - fn update_next_local_id(&mut self, local_id: LocalId) { - let next_local_id = (*self.next_local_id.deref()).max(LocalId::from(local_id.unpack() + 1)); - - *self.next_local_id.deref_mut() = next_local_id; - } -} - -impl Drop for PropagatorConstructorContext<'_> { - fn drop(&mut self) { - if std::thread::panicking() { - // If we are already unwinding due to a panic, we do not want to trigger another one. - return; - } - - let did_register = match self.did_register { - // If we are in a reborrowed context, we do not want to enforce registration. - RefOrOwned::Ref(_) => return, - - RefOrOwned::Owned(did_register) => did_register, - }; - - if !did_register { - panic!( - "Propagator did not register to be enqueued. If this is intentional, call PropagatorConstructorContext::will_not_register_any_events()." - ); - } - } } mod private { diff --git a/pumpkin-crates/core/src/propagation/event_registration.rs b/pumpkin-crates/core/src/propagation/event_registration.rs index cc096ea42..cd115de2b 100644 --- a/pumpkin-crates/core/src/propagation/event_registration.rs +++ b/pumpkin-crates/core/src/propagation/event_registration.rs @@ -1,3 +1,6 @@ +use enumset::EnumSet; + +use crate::propagation::DomainEvent; use crate::propagation::DomainEvents; use crate::propagation::LocalId; use crate::variables::DomainId; @@ -8,14 +11,14 @@ pub trait EventTarget { fn register( &self, registration: &mut EventRegistration, - events: DomainEvents, + events: EnumSet, local_id: LocalId, ); } /// Contains all the events and domains that a propagator needs to be enqueued for. #[derive(Clone, Debug)] -pub struct EventRegistration(Vec<(DomainId, DomainEvents, LocalId)>); +pub struct EventRegistration(Vec<(DomainId, EnumSet, LocalId)>); impl EventRegistration { /// Create an [`EventRegistration`] without any variables. @@ -52,16 +55,26 @@ impl EventRegistration { domain_events: DomainEvents, local_id: LocalId, ) { - target.register(self, domain_events, local_id); + target.register(self, domain_events.events(), local_id); } /// Register the [`DomainId`] with the given [`LocalId`] on the given [`DomainEvents`]. /// /// When creating the [`EventRegistration`] in a propagator, prefer to use /// [`EventRegistration::add`] to deal with domain views. - pub fn with_domain(&mut self, domain_id: DomainId, events: DomainEvents, local_id: LocalId) { + pub fn with_domain( + &mut self, + domain_id: DomainId, + events: EnumSet, + local_id: LocalId, + ) { self.0.push((domain_id, events, local_id)); } + + /// Iterate the registrations already made. + pub fn iter(&self) -> impl ExactSizeIterator, LocalId)> { + self.0.iter().copied() + } } /// Used to construct an [`EventRegistration`] for heterogeneous [`EventTarget`] implementations. diff --git a/pumpkin-crates/core/src/propagation/local_id.rs b/pumpkin-crates/core/src/propagation/local_id.rs index b3a3334df..baff94a32 100644 --- a/pumpkin-crates/core/src/propagation/local_id.rs +++ b/pumpkin-crates/core/src/propagation/local_id.rs @@ -10,6 +10,11 @@ impl LocalId { LocalId(value) } + /// Get the next [`LocalId`]. + pub const fn successor(self) -> Self { + LocalId(self.0 + 1) + } + pub fn unpack(self) -> u32 { self.0 } diff --git a/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs b/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs index e310d340c..c8b96aeac 100644 --- a/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs +++ b/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs @@ -104,10 +104,8 @@ impl PropagatorConstructor for NogoodPropagatorConstructor { fn create( self, - mut context: PropagatorConstructorContext, + context: PropagatorConstructorContext, ) -> (EventRegistration, Self::PropagatorImpl) { - context.will_not_register_any_events(); - let propagator = NogoodPropagator { handle: PropagatorHandle::new(context.propagator_id), parameters: self.parameters, diff --git a/pumpkin-crates/core/src/propagators/reified_propagator.rs b/pumpkin-crates/core/src/propagators/reified_propagator.rs index f30044cdd..d9817753a 100644 --- a/pumpkin-crates/core/src/propagators/reified_propagator.rs +++ b/pumpkin-crates/core/src/propagators/reified_propagator.rs @@ -48,11 +48,17 @@ where reification_literal, } = self; - let (registration, propagator) = propagator.create(context.reborrow()); - let reification_literal_id = context.get_next_local_id(); + let (mut registration, propagator) = propagator.create(context.reborrow()); - context.register( - self.reification_literal, + let reification_literal_id = registration + .iter() + .map(|(_, _, lid)| lid) + .max() + .expect("cannot reify propagators that do not register all variables immediately") + .successor(); + + registration.add( + &self.reification_literal, DomainEvents::BOUNDS, reification_literal_id, ); From 27ed751b9f9c3a35c8a1ecfc0b4988ba6187ec34 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Wed, 27 May 2026 22:28:36 +1000 Subject: [PATCH 03/12] Do the registration in the state --- pumpkin-crates/core/src/engine/state.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pumpkin-crates/core/src/engine/state.rs b/pumpkin-crates/core/src/engine/state.rs index cb3fe554a..db0d786e0 100644 --- a/pumpkin-crates/core/src/engine/state.rs +++ b/pumpkin-crates/core/src/engine/state.rs @@ -36,6 +36,7 @@ use crate::propagation::Propagator; use crate::propagation::PropagatorConstructor; use crate::propagation::PropagatorConstructorContext; use crate::propagation::PropagatorId; +use crate::propagation::PropagatorVarId; use crate::propagation::store::PropagatorStore; use crate::pumpkin_assert_advanced; use crate::pumpkin_assert_eq_simple; @@ -343,6 +344,16 @@ impl State { PropagatorConstructorContext::new(original_handle.propagator_id(), self); let (registration, propagator) = constructor.create(constructor_context); + for (domain_id, events, local_id) in registration.iter() { + let propagator_var = PropagatorVarId { + propagator: original_handle.propagator_id(), + variable: local_id, + }; + + self.notification_engine + .watch_all(domain_id, events, propagator_var); + } + pumpkin_assert_simple!( propagator.priority() as u8 <= 3, "The propagator priority exceeds 3. From c5290b13ebcd469b051e904782590824b1608c89 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Wed, 27 May 2026 22:46:16 +1000 Subject: [PATCH 04/12] Remove Watchers::watch_all in favor of event target --- .../domain_event_watch_list.rs | 5 --- .../core/src/engine/variables/affine_view.rs | 13 +------ .../core/src/engine/variables/constant.rs | 23 ++++++----- .../core/src/engine/variables/domain_id.rs | 10 ++--- .../src/engine/variables/integer_variable.rs | 5 +-- .../core/src/engine/variables/literal.rs | 8 +--- .../contexts/propagation_context.rs | 38 +++++++++++++++---- .../src/propagation/event_registration.rs | 26 ++++++------- 8 files changed, 63 insertions(+), 65 deletions(-) diff --git a/pumpkin-crates/core/src/engine/notifications/domain_event_notification/domain_event_watch_list.rs b/pumpkin-crates/core/src/engine/notifications/domain_event_notification/domain_event_watch_list.rs index 9d3220c2c..6646aecad 100644 --- a/pumpkin-crates/core/src/engine/notifications/domain_event_notification/domain_event_watch_list.rs +++ b/pumpkin-crates/core/src/engine/notifications/domain_event_notification/domain_event_watch_list.rs @@ -102,11 +102,6 @@ impl<'a> Watchers<'a> { } } - pub(crate) fn watch_all(&mut self, domain: DomainId, events: EnumSet) { - self.notification_engine - .watch_all(domain, events, self.propagator_var); - } - pub(crate) fn unwatch_all(&mut self, domain: DomainId) { self.notification_engine .unwatch_all(domain, self.propagator_var); diff --git a/pumpkin-crates/core/src/engine/variables/affine_view.rs b/pumpkin-crates/core/src/engine/variables/affine_view.rs index c0e41a5ec..9142f0ba5 100644 --- a/pumpkin-crates/core/src/engine/variables/affine_view.rs +++ b/pumpkin-crates/core/src/engine/variables/affine_view.rs @@ -14,7 +14,7 @@ use crate::engine::predicates::predicate_constructor::PredicateConstructor; use crate::engine::variables::DomainId; use crate::engine::variables::IntegerVariable; use crate::math::num_ext::NumExt; -use crate::propagation::EventRegistration; +use crate::propagation::EventDispatcher; use crate::propagation::EventTarget; use crate::propagation::LocalId; @@ -60,7 +60,7 @@ impl AffineView { impl EventTarget for AffineView { fn register( &self, - registration: &mut EventRegistration, + registration: &mut impl EventDispatcher, mut events: EnumSet, local_id: LocalId, ) { @@ -284,15 +284,6 @@ where .map(|value| self.map(value)) } - fn watch_all(&self, watchers: &mut Watchers<'_>, mut events: EnumSet) { - let bound = DomainEvent::LowerBound | DomainEvent::UpperBound; - let intersection = events.intersection(bound); - if intersection.len() == 1 && self.scale.is_negative() { - events = events.symmetric_difference(bound); - } - self.inner.watch_all(watchers, events); - } - fn unwatch_all(&self, watchers: &mut Watchers<'_>) { self.inner.unwatch_all(watchers); } diff --git a/pumpkin-crates/core/src/engine/variables/constant.rs b/pumpkin-crates/core/src/engine/variables/constant.rs index 486ea28ab..af328ba3b 100644 --- a/pumpkin-crates/core/src/engine/variables/constant.rs +++ b/pumpkin-crates/core/src/engine/variables/constant.rs @@ -1,12 +1,21 @@ +use enumset::EnumSet; use pumpkin_checking::CheckerVariable; use pumpkin_checking::IntExt; use crate::engine::Assignments; use crate::predicates::Predicate; use crate::predicates::PredicateConstructor; +use crate::propagation::DomainEvent; +use crate::propagation::EventDispatcher; +use crate::propagation::EventTarget; +use crate::propagation::LocalId; use crate::variables::IntegerVariable; use crate::variables::TransformableVariable; +impl EventTarget for i32 { + fn register(&self, _: &mut impl EventDispatcher, _: EnumSet, _: LocalId) {} +} + impl IntegerVariable for i32 { type AffineView = i32; @@ -51,26 +60,16 @@ impl IntegerVariable for i32 { std::iter::once(*self) } - fn watch_all( - &self, - _watchers: &mut crate::engine::notifications::Watchers<'_>, - _events: enumset::EnumSet, - ) { - } - fn unwatch_all(&self, _watchers: &mut crate::engine::notifications::Watchers<'_>) {} fn watch_all_backtrack( &self, _watchers: &mut crate::engine::notifications::Watchers<'_>, - _events: enumset::EnumSet, + _events: EnumSet, ) { } - fn unpack_event( - &self, - _event: crate::propagation::OpaqueDomainEvent, - ) -> crate::propagation::DomainEvent { + fn unpack_event(&self, _event: crate::propagation::OpaqueDomainEvent) -> DomainEvent { unreachable!() } diff --git a/pumpkin-crates/core/src/engine/variables/domain_id.rs b/pumpkin-crates/core/src/engine/variables/domain_id.rs index b384e749f..0d1c42b62 100644 --- a/pumpkin-crates/core/src/engine/variables/domain_id.rs +++ b/pumpkin-crates/core/src/engine/variables/domain_id.rs @@ -12,7 +12,7 @@ use crate::engine::variables::IntegerVariable; use crate::predicates::Predicate; use crate::predicates::PredicateConstructor; use crate::predicates::PredicateType; -use crate::propagation::EventRegistration; +use crate::propagation::EventDispatcher; use crate::propagation::EventTarget; use crate::propagation::LocalId; use crate::pumpkin_assert_simple; @@ -38,11 +38,11 @@ impl DomainId { impl EventTarget for DomainId { fn register( &self, - registration: &mut EventRegistration, + registration: &mut impl EventDispatcher, events: EnumSet, local_id: LocalId, ) { - registration.with_domain(*self, events, local_id); + registration.register(*self, events, local_id); } } @@ -169,10 +169,6 @@ impl IntegerVariable for DomainId { assignment.get_domain_iterator(*self) } - fn watch_all(&self, watchers: &mut Watchers<'_>, events: EnumSet) { - watchers.watch_all(*self, events); - } - fn unwatch_all(&self, watchers: &mut Watchers<'_>) { watchers.unwatch_all(*self); } diff --git a/pumpkin-crates/core/src/engine/variables/integer_variable.rs b/pumpkin-crates/core/src/engine/variables/integer_variable.rs index 09badb92e..34083bbd5 100644 --- a/pumpkin-crates/core/src/engine/variables/integer_variable.rs +++ b/pumpkin-crates/core/src/engine/variables/integer_variable.rs @@ -10,6 +10,7 @@ use crate::engine::notifications::OpaqueDomainEvent; use crate::engine::notifications::Watchers; use crate::engine::predicates::predicate_constructor::PredicateConstructor; use crate::predicates::Predicate; +use crate::propagation::EventTarget; /// A trait specifying the required behaviour of an integer variable such as retrieving a /// lower-bound ([`IntegerVariable::lower_bound`]). @@ -19,6 +20,7 @@ pub trait IntegerVariable: + TransformableVariable + Debug + CheckerVariable + + EventTarget { type AffineView: IntegerVariable; @@ -50,9 +52,6 @@ pub trait IntegerVariable: /// Iterate over the values of the domain. fn iterate_domain(&self, assignment: &Assignments) -> impl Iterator; - /// Register a watch for this variable on the given domain events. - fn watch_all(&self, watchers: &mut Watchers<'_>, events: EnumSet); - /// Remove the watcher on this variable. fn unwatch_all(&self, watchers: &mut Watchers<'_>); diff --git a/pumpkin-crates/core/src/engine/variables/literal.rs b/pumpkin-crates/core/src/engine/variables/literal.rs index 8cc72777f..3b358031f 100644 --- a/pumpkin-crates/core/src/engine/variables/literal.rs +++ b/pumpkin-crates/core/src/engine/variables/literal.rs @@ -15,7 +15,7 @@ use crate::engine::notifications::Watchers; use crate::engine::predicates::predicate::Predicate; use crate::engine::predicates::predicate_constructor::PredicateConstructor; use crate::engine::variables::AffineView; -use crate::propagation::EventRegistration; +use crate::propagation::EventDispatcher; use crate::propagation::EventTarget; use crate::propagation::LocalId; @@ -79,7 +79,7 @@ macro_rules! forward { impl EventTarget for Literal { fn register( &self, - registration: &mut EventRegistration, + registration: &mut impl EventDispatcher, events: EnumSet, local_id: LocalId, ) { @@ -179,10 +179,6 @@ impl IntegerVariable for Literal { self.integer_variable.iterate_domain(assignment) } - fn watch_all(&self, watchers: &mut Watchers<'_>, events: EnumSet) { - self.integer_variable.watch_all(watchers, events) - } - fn unwatch_all(&self, watchers: &mut Watchers<'_>) { self.integer_variable.unwatch_all(watchers) } diff --git a/pumpkin-crates/core/src/propagation/contexts/propagation_context.rs b/pumpkin-crates/core/src/propagation/contexts/propagation_context.rs index ab3218552..48d21cb67 100644 --- a/pumpkin-crates/core/src/propagation/contexts/propagation_context.rs +++ b/pumpkin-crates/core/src/propagation/contexts/propagation_context.rs @@ -1,3 +1,5 @@ +use enumset::EnumSet; + use crate::basic_types::PredicateId; use crate::engine::Assignments; use crate::engine::EmptyDomain; @@ -10,8 +12,10 @@ use crate::engine::reason::Reason; use crate::engine::reason::ReasonStore; use crate::engine::reason::StoredReason; use crate::engine::variables::Literal; +use crate::propagation::DomainEvent; use crate::propagation::DomainEvents; use crate::propagation::Domains; +use crate::propagation::EventDispatcher; use crate::propagation::HasAssignments; use crate::propagation::LocalId; #[cfg(doc)] @@ -23,6 +27,7 @@ use crate::propagation::PropagatorVarId; #[cfg(doc)] use crate::propagation::ReadDomains; use crate::pumpkin_assert_simple; +use crate::variables::DomainId; use crate::variables::IntegerVariable; /// Provided to the propagator when it is notified of a domain event. @@ -146,13 +151,14 @@ impl<'a> PropagationContext<'a> { domain_events: DomainEvents, local_id: LocalId, ) { - let propagator_var = PropagatorVarId { - propagator: self.propagator_id, - variable: local_id, - }; - - let mut watchers = Watchers::new(propagator_var, self.notification_engine); - var.watch_all(&mut watchers, domain_events.events()); + var.register( + &mut NotificationEngineWatchers { + notificaton_engine: self.notification_engine, + propagator_id: self.propagator_id, + }, + domain_events.events(), + local_id, + ); } /// Stop being enqueued for events on the given integer variable. @@ -290,3 +296,21 @@ pub(crate) fn build_reason( Reason::DynamicLazy(code) => StoredReason::DynamicLazy(code), } } + +struct NotificationEngineWatchers<'a> { + propagator_id: PropagatorId, + notificaton_engine: &'a mut NotificationEngine, +} + +impl EventDispatcher for NotificationEngineWatchers<'_> { + fn register(&mut self, domain_id: DomainId, events: EnumSet, local_id: LocalId) { + self.notificaton_engine.watch_all( + domain_id, + events, + PropagatorVarId { + propagator: self.propagator_id, + variable: local_id, + }, + ); + } +} diff --git a/pumpkin-crates/core/src/propagation/event_registration.rs b/pumpkin-crates/core/src/propagation/event_registration.rs index cd115de2b..733626f17 100644 --- a/pumpkin-crates/core/src/propagation/event_registration.rs +++ b/pumpkin-crates/core/src/propagation/event_registration.rs @@ -10,12 +10,17 @@ pub trait EventTarget { /// Add a registration of self for the given domain events with a local id. fn register( &self, - registration: &mut EventRegistration, + registration: &mut impl EventDispatcher, events: EnumSet, local_id: LocalId, ); } +pub trait EventDispatcher { + /// Register the [`DomainId`] with the given [`LocalId`] on the given [`DomainEvents`]. + fn register(&mut self, domain_id: DomainId, events: EnumSet, local_id: LocalId); +} + /// Contains all the events and domains that a propagator needs to be enqueued for. #[derive(Clone, Debug)] pub struct EventRegistration(Vec<(DomainId, EnumSet, LocalId)>); @@ -58,25 +63,18 @@ impl EventRegistration { target.register(self, domain_events.events(), local_id); } - /// Register the [`DomainId`] with the given [`LocalId`] on the given [`DomainEvents`]. - /// - /// When creating the [`EventRegistration`] in a propagator, prefer to use - /// [`EventRegistration::add`] to deal with domain views. - pub fn with_domain( - &mut self, - domain_id: DomainId, - events: EnumSet, - local_id: LocalId, - ) { - self.0.push((domain_id, events, local_id)); - } - /// Iterate the registrations already made. pub fn iter(&self) -> impl ExactSizeIterator, LocalId)> { self.0.iter().copied() } } +impl EventDispatcher for EventRegistration { + fn register(&mut self, domain_id: DomainId, events: EnumSet, local_id: LocalId) { + self.0.push((domain_id, events, local_id)); + } +} + /// Used to construct an [`EventRegistration`] for heterogeneous [`EventTarget`] implementations. /// /// See [`EventRegistration::builder`] for a usage example. From b4568923f4ccd9bbf911575cfae9039276a5cfb7 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Wed, 27 May 2026 23:18:14 +1000 Subject: [PATCH 05/12] Implement the new interface in the rest of pumpkin --- .../core/src/propagation/constructor.rs | 52 ------------------- .../src/propagation/event_registration.rs | 11 +++- .../hypercube_linear/propagator.rs | 3 +- .../src/propagators/reified_propagator.rs | 29 +++++++---- .../propagators/arithmetic/absolute_value.rs | 15 ++++-- .../arithmetic/binary/binary_equals.rs | 15 ++++-- .../arithmetic/binary/binary_not_equals.rs | 15 ++++-- .../arithmetic/integer_division.rs | 20 ++++--- .../arithmetic/integer_multiplication.rs | 17 +++--- .../arithmetic/linear_less_or_equal.rs | 20 ++++--- .../arithmetic/linear_not_equal.rs | 11 ++-- .../src/propagators/arithmetic/maximum.rs | 16 +++--- .../time_table_over_interval_incremental.rs | 10 ++-- .../time_table_per_point_incremental.rs | 10 ++-- .../time_table/time_table_over_interval.rs | 10 ++-- .../time_table/time_table_per_point.rs | 10 ++-- .../src/propagators/cumulative/utils/util.rs | 16 ++++-- .../disjunctive/disjunctive_propagator.rs | 16 +++--- .../propagators/src/propagators/element.rs | 18 ++++--- .../src/deduction_propagator.rs | 2 +- 20 files changed, 176 insertions(+), 140 deletions(-) diff --git a/pumpkin-crates/core/src/propagation/constructor.rs b/pumpkin-crates/core/src/propagation/constructor.rs index 74eb6281d..da90931c7 100644 --- a/pumpkin-crates/core/src/propagation/constructor.rs +++ b/pumpkin-crates/core/src/propagation/constructor.rs @@ -199,55 +199,3 @@ mod private { } } } - -#[cfg(test)] -mod tests { - - use super::*; - use crate::variables::DomainId; - - #[test] - #[should_panic] - fn panic_when_no_registration_happened() { - let mut state = State::default(); - state.notification_engine.grow(); - - let _c1 = PropagatorConstructorContext::new(PropagatorId(0), &mut state); - } - - #[test] - fn do_not_panic_if_told_no_registration_will_happen() { - let mut state = State::default(); - state.notification_engine.grow(); - - let mut ctx = PropagatorConstructorContext::new(PropagatorId(0), &mut state); - ctx.will_not_register_any_events(); - } - - #[test] - fn do_not_panic_if_no_registration_happens_in_reborrowed() { - let mut state = State::default(); - state.notification_engine.grow(); - - let mut ctx = PropagatorConstructorContext::new(PropagatorId(0), &mut state); - let ctx2 = ctx.reborrow(); - drop(ctx2); - - ctx.will_not_register_any_events(); - } - - #[test] - fn reborrowing_remembers_next_local_id() { - let mut state = State::default(); - state.notification_engine.grow(); - - let mut c1 = PropagatorConstructorContext::new(PropagatorId(0), &mut state); - c1.will_not_register_any_events(); - - let mut c2 = c1.reborrow(); - c2.register(DomainId::new(0), DomainEvents::ANY_INT, LocalId::from(1)); - drop(c2); - - assert_eq!(LocalId::from(2), c1.get_next_local_id()); - } -} diff --git a/pumpkin-crates/core/src/propagation/event_registration.rs b/pumpkin-crates/core/src/propagation/event_registration.rs index 733626f17..665b92ed5 100644 --- a/pumpkin-crates/core/src/propagation/event_registration.rs +++ b/pumpkin-crates/core/src/propagation/event_registration.rs @@ -42,9 +42,16 @@ impl EventRegistration { /// # Example /// /// ``` + /// use pumpkin_core::propagation::DomainEvents; + /// use pumpkin_core::propagation::EventRegistration; + /// use pumpkin_core::propagation::LocalId; + /// use pumpkin_core::variables::DomainId; + /// + /// let v1 = DomainId::new(0); + /// let v2 = DomainId::new(0); /// let registration = EventRegistration::builder() - /// .add(LocalId::from(0), &v1) - /// .add(LocalId::from(1), &v2) + /// .add(&v1, DomainEvents::ANY_INT, LocalId::from(0)) + /// .add(&v2, DomainEvents::ANY_INT, LocalId::from(1)) /// .build(); /// ``` pub fn builder() -> EventRegistrationBuilder { diff --git a/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs b/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs index 0f6068935..3b6d87d3a 100644 --- a/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs +++ b/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs @@ -77,7 +77,8 @@ impl PropagatorConstructor for HypercubeLinearConstructor { inference_code: InferenceCode::new(constraint_tag, HypercubeLinear), }; - let registration = EventRegistration::builder().build(); + // TODO: This will be expanded with registration of predicates. + let registration = EventRegistration::empty(); (registration, propagator) } diff --git a/pumpkin-crates/core/src/propagators/reified_propagator.rs b/pumpkin-crates/core/src/propagators/reified_propagator.rs index d9817753a..51e925e7c 100644 --- a/pumpkin-crates/core/src/propagators/reified_propagator.rs +++ b/pumpkin-crates/core/src/propagators/reified_propagator.rs @@ -73,7 +73,7 @@ where reason_buffer: vec![], }; - (EventRegistration::builder().build(), propagator) + (registration, propagator) } fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { @@ -308,6 +308,7 @@ mod tests { let _ = solver .new_propagator(ReifiedPropagatorArgs { propagator: GenericPropagator::new( + vec![a, b], move |_: PropagationContext| { Err(PropagatorConflict { conjunction: t1.clone(), @@ -342,6 +343,7 @@ mod tests { let propagator = solver .new_propagator(ReifiedPropagatorArgs { propagator: GenericPropagator::new( + vec![var], move |mut ctx: PropagationContext| { ctx.post( predicate![var >= 3], @@ -385,6 +387,7 @@ mod tests { let inconsistency = solver .new_propagator(ReifiedPropagatorArgs { propagator: GenericPropagator::new( + vec![var], move |_: PropagationContext| { Err(PropagatorConflict { conjunction: conjunction!([var >= 1]), @@ -426,6 +429,7 @@ mod tests { let propagator = solver .new_propagator(ReifiedPropagatorArgs { propagator: GenericPropagator::new( + vec![var], |_: PropagationContext| Ok(()), move |context: Domains| { if context.is_fixed(&var) { @@ -462,16 +466,17 @@ mod tests { { type PropagatorImpl = Self; - fn create(self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create( + self, + _: PropagatorConstructorContext, + ) -> (EventRegistration, Self::PropagatorImpl) { + let mut registration = EventRegistration::empty(); + for (index, variable) in self.variables_to_register.iter().enumerate() { - context.register( - *variable, - DomainEvents::ANY_INT, - LocalId::from(index as u32), - ); + registration.add(variable, DomainEvents::ANY_INT, LocalId::from(index as u32)); } - self + (registration, self) } } @@ -498,11 +503,15 @@ mod tests { Propagation: Fn(PropagationContext) -> PropagationStatusCP, ConsistencyCheck: Fn(Domains) -> Option, { - pub(crate) fn new(propagation: Propagation, consistency_check: ConsistencyCheck) -> Self { + pub(crate) fn new( + variables_to_register: Vec, + propagation: Propagation, + consistency_check: ConsistencyCheck, + ) -> Self { GenericPropagator { propagation, consistency_check, - variables_to_register: vec![], + variables_to_register, } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs index 2881dfa39..bd824a32b 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs @@ -8,6 +8,7 @@ use pumpkin_core::predicate; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::DomainEvents; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; @@ -45,23 +46,27 @@ where ); } - fn create(self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create(self, _: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { let AbsoluteValueArgs { signed, absolute, constraint_tag, } = self; - context.register(signed.clone(), DomainEvents::BOUNDS, LocalId::from(0)); - context.register(absolute.clone(), DomainEvents::BOUNDS, LocalId::from(1)); + let registration = EventRegistration::builder() + .add(&signed, DomainEvents::BOUNDS, LocalId::from(0)) + .add(&absolute, DomainEvents::BOUNDS, LocalId::from(1)) + .build(); let inference_code = InferenceCode::new(constraint_tag, AbsoluteValue); - AbsoluteValuePropagator { + let propagator = AbsoluteValuePropagator { signed, absolute, inference_code, - } + }; + + (registration, propagator) } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs index b95b86b46..adc34e36e 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs @@ -20,6 +20,7 @@ use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::ExplanationContext; use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LazyExplanation; @@ -64,17 +65,19 @@ where ); } - fn create(self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create(self, _: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { let BinaryEqualsPropagatorArgs { a, b, constraint_tag, } = self; - context.register(a.clone(), DomainEvents::ANY_INT, LocalId::from(0)); - context.register(b.clone(), DomainEvents::ANY_INT, LocalId::from(1)); + let registration = EventRegistration::builder() + .add(&a, DomainEvents::ANY_INT, LocalId::from(0)) + .add(&b, DomainEvents::ANY_INT, LocalId::from(1)) + .build(); - BinaryEqualsPropagator { + let propagator = BinaryEqualsPropagator { a, b, @@ -86,7 +89,9 @@ where has_backtracked: false, first_propagation_loop: true, reason: Predicate::trivially_false(), - } + }; + + (registration, propagator) } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs index b33a98543..60f7e49e3 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs @@ -8,6 +8,7 @@ use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; @@ -47,7 +48,7 @@ where ); } - fn create(self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create(self, _: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { let BinaryNotEqualsPropagatorArgs { a, b, @@ -55,15 +56,19 @@ where } = self; // We only care about the case where one of the two is assigned - context.register(a.clone(), DomainEvents::ASSIGN, LocalId::from(0)); - context.register(b.clone(), DomainEvents::ASSIGN, LocalId::from(1)); + let registration = EventRegistration::builder() + .add(&a, DomainEvents::ASSIGN, LocalId::from(0)) + .add(&b, DomainEvents::ASSIGN, LocalId::from(1)) + .build(); - BinaryNotEqualsPropagator { + let propagator = BinaryNotEqualsPropagator { a, b, inference_code: InferenceCode::new(constraint_tag, BinaryNotEquals), - } + }; + + (registration, propagator) } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs index 882495a48..b9dccaf4e 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs @@ -9,6 +9,7 @@ use pumpkin_core::predicate; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::DomainEvents; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; @@ -43,7 +44,10 @@ where { type PropagatorImpl = DivisionPropagator; - fn create(self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create( + self, + context: PropagatorConstructorContext, + ) -> (EventRegistration, Self::PropagatorImpl) { let DivisionArgs { numerator, denominator, @@ -56,18 +60,22 @@ where "Denominator cannot contain 0" ); - context.register(numerator.clone(), DomainEvents::BOUNDS, ID_NUMERATOR); - context.register(denominator.clone(), DomainEvents::BOUNDS, ID_DENOMINATOR); - context.register(rhs.clone(), DomainEvents::BOUNDS, ID_RHS); + let registration = EventRegistration::builder() + .add(&numerator, DomainEvents::BOUNDS, ID_NUMERATOR) + .add(&denominator, DomainEvents::BOUNDS, ID_DENOMINATOR) + .add(&rhs, DomainEvents::BOUNDS, ID_RHS) + .build(); let inference_code = InferenceCode::new(constraint_tag, Division); - DivisionPropagator { + let propagator = DivisionPropagator { numerator, denominator, rhs, inference_code, - } + }; + + (registration, propagator) } fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs index 9e7d8d953..65a3519b4 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs @@ -8,6 +8,7 @@ use pumpkin_core::predicate; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::DomainEvents; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; @@ -50,7 +51,7 @@ where ); } - fn create(self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create(self, _: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { let IntegerMultiplicationArgs { a, b, @@ -58,16 +59,20 @@ where constraint_tag, } = self; - context.register(a.clone(), DomainEvents::ANY_INT, ID_A); - context.register(b.clone(), DomainEvents::ANY_INT, ID_B); - context.register(c.clone(), DomainEvents::ANY_INT, ID_C); + let registration = EventRegistration::builder() + .add(&a, DomainEvents::ANY_INT, ID_A) + .add(&b, DomainEvents::ANY_INT, ID_B) + .add(&c, DomainEvents::ANY_INT, ID_C) + .build(); - IntegerMultiplicationPropagator { + let propagator = IntegerMultiplicationPropagator { a, b, c, inference_code: InferenceCode::new(constraint_tag, IntegerMultiplication), - } + }; + + (registration, propagator) } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs index 075af43d9..ab244ff96 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs @@ -13,6 +13,7 @@ use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::ExplanationContext; use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LazyExplanation; @@ -56,7 +57,10 @@ where ); } - fn create(self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create( + self, + mut context: PropagatorConstructorContext, + ) -> (EventRegistration, Self::PropagatorImpl) { let LinearLessOrEqualPropagatorArgs { x, c, @@ -66,26 +70,26 @@ where let mut lower_bound_left_hand_side = 0_i64; let mut current_bounds = vec![]; + let mut registration = EventRegistration::builder(); for (i, x_i) in x.iter().enumerate() { - context.register( - x_i.clone(), - DomainEvents::LOWER_BOUND, - LocalId::from(i as u32), - ); + registration = + registration.add(x_i, DomainEvents::LOWER_BOUND, LocalId::from(i as u32)); lower_bound_left_hand_side += context.lower_bound(x_i) as i64; current_bounds.push(context.new_trailed_integer(context.lower_bound(x_i) as i64)); } let lower_bound_left_hand_side = context.new_trailed_integer(lower_bound_left_hand_side); - LinearLessOrEqualPropagator { + let propagator = LinearLessOrEqualPropagator { x, c, lower_bound_left_hand_side, current_bounds: current_bounds.into(), inference_code: InferenceCode::new(constraint_tag, LinearBounds), reason_buffer: Vec::default(), - } + }; + + (registration.build(), propagator) } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs index 130d2b2d8..23fc766a9 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs @@ -18,6 +18,7 @@ use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::NotificationContext; @@ -60,15 +61,19 @@ where ); } - fn create(self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create( + self, + mut context: PropagatorConstructorContext, + ) -> (EventRegistration, Self::PropagatorImpl) { let LinearNotEqualPropagatorArgs { terms, rhs, constraint_tag, } = self; + let mut registration = EventRegistration::builder(); for (i, x_i) in terms.iter().enumerate() { - context.register(x_i.clone(), DomainEvents::ASSIGN, LocalId::from(i as u32)); + registration = registration.add(x_i, DomainEvents::ASSIGN, LocalId::from(i as u32)); context.register_backtrack( x_i.clone(), DomainEvents::new(enum_set!(DomainEvent::Assign | DomainEvent::Removal)), @@ -88,7 +93,7 @@ where propagator.recalculate_fixed_variables(context.domains()); - propagator + (registration.build(), propagator) } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs index 2a847bc4c..85761c40c 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs @@ -9,6 +9,7 @@ use pumpkin_core::predicates::PropositionalConjunction; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::DomainEvents; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; @@ -46,30 +47,33 @@ where ); } - fn create(self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create(self, _: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { let MaximumArgs { array, rhs, constraint_tag, } = self; + let mut registration = EventRegistration::builder(); for (idx, var) in array.iter().enumerate() { - context.register(var.clone(), DomainEvents::BOUNDS, LocalId::from(idx as u32)); + registration = registration.add(var, DomainEvents::BOUNDS, LocalId::from(idx as u32)); } - context.register( - rhs.clone(), + registration = registration.add( + &rhs, DomainEvents::BOUNDS, LocalId::from(array.len() as u32), ); let inference_code = InferenceCode::new(constraint_tag, Maximum); - MaximumPropagator { + let propagator = MaximumPropagator { array, rhs, inference_code, - } + }; + + (registration.build(), propagator) } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs index fc5d108e4..312bd2c83 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs @@ -11,6 +11,7 @@ use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::NotificationContext; @@ -129,10 +130,13 @@ impl PropagatorConstruc ); } - fn create(mut self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create( + mut self, + mut context: PropagatorConstructorContext, + ) -> (EventRegistration, Self::PropagatorImpl) { // We only register for notifications of backtrack events if incremental backtracking is // enabled - register_tasks( + let registration = register_tasks( &self.parameters.tasks, context.reborrow(), self.parameters.options.incremental_backtracking, @@ -146,7 +150,7 @@ impl PropagatorConstruc self.inference_code = Some(InferenceCode::new(self.constraint_tag, TimeTable)); - self + (registration, self) } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs index 9b4ffa1e5..2428cf98f 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs @@ -11,6 +11,7 @@ use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::NotificationContext; @@ -126,8 +127,11 @@ impl Propagator ); } - fn create(mut self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { - register_tasks(&self.parameters.tasks, context.reborrow(), true); + fn create( + mut self, + mut context: PropagatorConstructorContext, + ) -> (EventRegistration, Self::PropagatorImpl) { + let registration = register_tasks(&self.parameters.tasks, context.reborrow(), true); self.updatable_structures .reset_all_bounds_and_remove_fixed(context.domains(), &self.parameters); @@ -136,7 +140,7 @@ impl Propagator self.inference_code = Some(InferenceCode::new(self.constraint_tag, TimeTable)); - self + (registration, self) } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs index cb8b6926b..cf0792903 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs @@ -9,6 +9,7 @@ use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::NotificationContext; @@ -129,14 +130,17 @@ impl PropagatorConstructor ); } - fn create(mut self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create( + mut self, + mut context: PropagatorConstructorContext, + ) -> (EventRegistration, Self::PropagatorImpl) { self.updatable_structures .initialise_bounds_and_remove_fixed(context.domains(), &self.parameters); - register_tasks(&self.parameters.tasks, context.reborrow(), false); + let registration = register_tasks(&self.parameters.tasks, context.reborrow(), false); self.inference_code = Some(InferenceCode::new(self.constraint_tag, TimeTable)); - self + (registration, self) } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs index 5451cd01d..471f2e26e 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs @@ -11,6 +11,7 @@ use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::EnqueueDecision; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::NotificationContext; @@ -119,14 +120,17 @@ impl PropagatorConstructor for TimeTablePerPoint ); } - fn create(mut self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create( + mut self, + mut context: PropagatorConstructorContext, + ) -> (EventRegistration, Self::PropagatorImpl) { self.updatable_structures .initialise_bounds_and_remove_fixed(context.domains(), &self.parameters); - register_tasks(&self.parameters.tasks, context.reborrow(), false); + let registration = register_tasks(&self.parameters.tasks, context.reborrow(), false); self.inference_code = Some(InferenceCode::new(self.constraint_tag, TimeTable)); - self + (registration, self) } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/utils/util.rs b/pumpkin-crates/propagators/src/propagators/cumulative/utils/util.rs index 3b1d3f790..063042abf 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/utils/util.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/utils/util.rs @@ -7,6 +7,7 @@ use enumset::enum_set; use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::PropagatorConstructorContext; use pumpkin_core::propagation::ReadDomains; @@ -52,15 +53,18 @@ pub(crate) fn register_tasks( tasks: &[Rc>], mut context: PropagatorConstructorContext<'_>, register_backtrack: bool, -) { - tasks.iter().for_each(|task| { - context.register( - task.start_variable.clone(), +) -> EventRegistration { + let mut registration = EventRegistration::builder(); + + for task in tasks.iter() { + registration = registration.add( + &task.start_variable, DomainEvents::new(enum_set!( DomainEvent::LowerBound | DomainEvent::UpperBound | DomainEvent::Assign )), task.id, ); + if register_backtrack { context.register_backtrack( task.start_variable.clone(), @@ -70,7 +74,9 @@ pub(crate) fn register_tasks( task.id, ); } - }); + } + + registration.build() } /// Updates the bounds of the provided [`Task`] to those stored in diff --git a/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs b/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs index 0d2da6002..4341ab54b 100644 --- a/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs +++ b/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs @@ -8,6 +8,7 @@ use pumpkin_core::predicates::PropositionalConjunction; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::DomainEvents; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::PropagationContext; @@ -79,7 +80,7 @@ impl DisjunctiveConstructor { impl PropagatorConstructor for DisjunctiveConstructor { type PropagatorImpl = DisjunctivePropagator; - fn create(self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create(self, _: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { let tasks = self .tasks .into_iter() @@ -94,17 +95,20 @@ impl PropagatorConstructor for DisjunctiveConstr let inference_code = InferenceCode::new(self.constraint_tag, DisjunctiveEdgeFinding); - tasks.iter().for_each(|task| { - context.register(task.start_time.clone(), DomainEvents::BOUNDS, task.id); - }); + let mut registration = EventRegistration::builder(); + for task in tasks.iter() { + registration = registration.add(&task.start_time, DomainEvents::BOUNDS, task.id); + } - DisjunctivePropagator { + let propagator = DisjunctivePropagator { tasks: tasks.clone().into_boxed_slice(), sorted_tasks: tasks, theta_lambda_tree, inference_code, - } + }; + + (registration.build(), propagator) } fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { diff --git a/pumpkin-crates/propagators/src/propagators/element.rs b/pumpkin-crates/propagators/src/propagators/element.rs index 8874a259c..09f1b9271 100644 --- a/pumpkin-crates/propagators/src/propagators/element.rs +++ b/pumpkin-crates/propagators/src/propagators/element.rs @@ -17,6 +17,7 @@ use pumpkin_core::predicates::Predicate; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::DomainEvents; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::ExplanationContext; use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LazyExplanation; @@ -60,7 +61,7 @@ where ); } - fn create(self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create(self, _: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { let ElementArgs { array, index, @@ -68,26 +69,29 @@ where constraint_tag, } = self; + let mut registration = EventRegistration::builder(); for (i, x_i) in array.iter().enumerate() { - context.register( - x_i.clone(), + registration = registration.add( + x_i, DomainEvents::ANY_INT, LocalId::from(i as u32 + ID_X_OFFSET), ); } - context.register(index.clone(), DomainEvents::ANY_INT, ID_INDEX); - context.register(rhs.clone(), DomainEvents::ANY_INT, ID_RHS); + registration = registration.add(&index, DomainEvents::ANY_INT, ID_INDEX); + registration = registration.add(&rhs, DomainEvents::ANY_INT, ID_RHS); let inference_code = InferenceCode::new(constraint_tag, Element); - ElementPropagator { + let propagator = ElementPropagator { array, index, rhs, inference_code, rhs_reason_buffer: vec![], - } + }; + + (registration.build(), propagator) } } diff --git a/pumpkin-proof-processor/src/deduction_propagator.rs b/pumpkin-proof-processor/src/deduction_propagator.rs index bba962e49..503c7c59b 100644 --- a/pumpkin-proof-processor/src/deduction_propagator.rs +++ b/pumpkin-proof-processor/src/deduction_propagator.rs @@ -24,7 +24,7 @@ pub(crate) struct DeductionPropagatorConstructor { impl PropagatorConstructor for DeductionPropagatorConstructor { type PropagatorImpl = DeductionPropagator; - fn create(self, mut context: PropagatorConstructorContext) -> Self::PropagatorImpl { + fn create(self, mut context: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { declare_inference_label!(Nogood); let DeductionPropagatorConstructor { From b95ad412dfce9418666814864ef8b83df6952606 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Wed, 27 May 2026 23:29:26 +1000 Subject: [PATCH 06/12] Also update the proof processor --- pumpkin-proof-processor/src/deduction_propagator.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pumpkin-proof-processor/src/deduction_propagator.rs b/pumpkin-proof-processor/src/deduction_propagator.rs index 503c7c59b..80c5bc33c 100644 --- a/pumpkin-proof-processor/src/deduction_propagator.rs +++ b/pumpkin-proof-processor/src/deduction_propagator.rs @@ -2,6 +2,7 @@ use pumpkin_core::declare_inference_label; use pumpkin_core::predicates::PropositionalConjunction; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::PredicateId; use pumpkin_core::propagation::PropagationContext; use pumpkin_core::propagation::Propagator; @@ -24,7 +25,10 @@ pub(crate) struct DeductionPropagatorConstructor { impl PropagatorConstructor for DeductionPropagatorConstructor { type PropagatorImpl = DeductionPropagator; - fn create(self, mut context: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { + fn create( + self, + mut context: PropagatorConstructorContext, + ) -> (EventRegistration, Self::PropagatorImpl) { declare_inference_label!(Nogood); let DeductionPropagatorConstructor { @@ -37,12 +41,14 @@ impl PropagatorConstructor for DeductionPropagatorConstructor { .map(|&predicate| context.register_predicate(predicate)) .collect(); - DeductionPropagator { + let propagator = DeductionPropagator { nogood, ids, inference_code: InferenceCode::new(constraint_tag, Nogood), active: true, - } + }; + + (EventRegistration::empty(), propagator) } } From 0a66a98dbaf70da557f22aa093e91996cfebc301 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Thu, 28 May 2026 11:37:38 +1000 Subject: [PATCH 07/12] Refactor the interface to post inference checkers --- .../checking/src/inference_checker.rs | 4 +- pumpkin-crates/core/src/checkers/mod.rs | 3 + pumpkin-crates/core/src/checkers/store.rs | 44 +++++++++ .../conflict_analysis_context.rs | 10 +- .../engine/constraint_satisfaction_solver.rs | 16 ++-- .../core/src/engine/cp/test_solver.rs | 9 +- pumpkin-crates/core/src/engine/state.rs | 82 ++++++++-------- pumpkin-crates/core/src/lib.rs | 1 + .../core/src/proof/inference_code.rs | 6 ++ .../core/src/propagation/constructor.rs | 76 +++------------ pumpkin-crates/core/src/propagation/mod.rs | 2 + .../core/src/propagation/runtime_checkers.rs | 95 +++++++++++++++++++ .../hypercube_linear/propagator.rs | 35 ++++--- .../propagators/nogoods/nogood_propagator.rs | 10 +- .../src/propagators/reified_propagator.rs | 60 ++++++++---- 15 files changed, 295 insertions(+), 158 deletions(-) create mode 100644 pumpkin-crates/core/src/checkers/mod.rs create mode 100644 pumpkin-crates/core/src/checkers/store.rs create mode 100644 pumpkin-crates/core/src/propagation/runtime_checkers.rs diff --git a/pumpkin-crates/checking/src/inference_checker.rs b/pumpkin-crates/checking/src/inference_checker.rs index badb75d8e..dc6b61fda 100644 --- a/pumpkin-crates/checking/src/inference_checker.rs +++ b/pumpkin-crates/checking/src/inference_checker.rs @@ -30,8 +30,8 @@ impl Clone for BoxedChecker { } } -impl From>> for BoxedChecker { - fn from(value: Box>) -> Self { +impl BoxedChecker { + pub fn new(value: Box>) -> Self { BoxedChecker(value) } } diff --git a/pumpkin-crates/core/src/checkers/mod.rs b/pumpkin-crates/core/src/checkers/mod.rs new file mode 100644 index 000000000..fca730d27 --- /dev/null +++ b/pumpkin-crates/core/src/checkers/mod.rs @@ -0,0 +1,3 @@ +mod store; + +pub use store::*; diff --git a/pumpkin-crates/core/src/checkers/store.rs b/pumpkin-crates/core/src/checkers/store.rs new file mode 100644 index 000000000..c514d4643 --- /dev/null +++ b/pumpkin-crates/core/src/checkers/store.rs @@ -0,0 +1,44 @@ +use pumpkin_checking::BoxedChecker; + +use crate::containers::HashMap; +use crate::predicates::Predicate; +use crate::proof::InferenceCode; + +/// Owns the runtime checkers that are active. +pub trait StoresCheckers { + fn add_inference_checker( + &mut self, + inference_code: InferenceCode, + checker: BoxedChecker, + ); +} + +#[derive(Clone, Debug, Default)] +pub struct CheckerStore { + inference_codes: HashMap>>, +} + +impl CheckerStore { + pub fn for_inference_code( + &self, + inference_code: &InferenceCode, + ) -> impl ExactSizeIterator> { + self.inference_codes + .get(inference_code) + .map(|checkers| itertools::Either::Left(checkers.iter())) + .unwrap_or(itertools::Either::Right(std::iter::empty())) + } +} + +impl StoresCheckers for CheckerStore { + fn add_inference_checker( + &mut self, + inference_code: InferenceCode, + checker: BoxedChecker, + ) { + self.inference_codes + .entry(inference_code.clone()) + .or_default() + .push(checker); + } +} diff --git a/pumpkin-crates/core/src/conflict_resolving/conflict_analysis_context.rs b/pumpkin-crates/core/src/conflict_resolving/conflict_analysis_context.rs index ad5f02016..7a9edc8fb 100644 --- a/pumpkin-crates/core/src/conflict_resolving/conflict_analysis_context.rs +++ b/pumpkin-crates/core/src/conflict_resolving/conflict_analysis_context.rs @@ -279,13 +279,13 @@ impl ConflictAnalysisContext<'_> { let learned_nogood = LearnedNogood::create_from_vec(learned_nogood_predicates, self); let constraint_tag = self.log_deduction(learned_nogood.predicates.iter().copied()); - let inference_code = InferenceCode::new(constraint_tag, NogoodLabel); - self.state.add_inference_checker( - inference_code.clone(), - Box::new(NogoodChecker { + let inference_code = self.state.add_inference_checker( + constraint_tag, + NogoodLabel, + NogoodChecker { nogood: learned_nogood.predicates.clone().into(), - }), + }, ); self.restore_to(learned_nogood.backtrack_level); diff --git a/pumpkin-crates/core/src/engine/constraint_satisfaction_solver.rs b/pumpkin-crates/core/src/engine/constraint_satisfaction_solver.rs index 36e33a1aa..3d307b7f9 100644 --- a/pumpkin-crates/core/src/engine/constraint_satisfaction_solver.rs +++ b/pumpkin-crates/core/src/engine/constraint_satisfaction_solver.rs @@ -891,16 +891,17 @@ impl ConstraintSatisfactionSolver { fn add_nogood( &mut self, nogood: Vec, - inference_code: InferenceCode, + constraint_tag: ConstraintTag, ) -> Result<(), ConstraintOperationError> { pumpkin_assert_eq_simple!(self.get_checkpoint(), 0); let num_trail_entries = self.state.trail_len(); - self.state.add_inference_checker( - inference_code.clone(), - Box::new(NogoodChecker { + let inference_code = self.state.add_inference_checker( + constraint_tag, + NogoodLabel, + NogoodChecker { nogood: nogood.clone().into(), - }), + }, ); let (nogood_propagator, mut context) = self @@ -983,7 +984,6 @@ impl ConstraintSatisfactionSolver { return Err(ConstraintOperationError::InfeasibleClause); } - let inference_code = InferenceCode::new(constraint_tag, NogoodLabel); if are_all_falsified_at_root { // Since the propagation is not actually performed, we log the inference // explicitly here for the proof. @@ -992,7 +992,7 @@ impl ConstraintSatisfactionSolver { .proof_log .log_inference( &mut self.state.constraint_tags, - inference_code, + InferenceCode::new(constraint_tag, NogoodLabel), predicates.iter().copied(), None, &self.state.variable_names, @@ -1013,7 +1013,7 @@ impl ConstraintSatisfactionSolver { return Err(ConstraintOperationError::InfeasibleClause); } - if let Err(constraint_operation_error) = self.add_nogood(predicates, inference_code) { + if let Err(constraint_operation_error) = self.add_nogood(predicates, constraint_tag) { let _ = self.conclude_proof_unsat(); self.solver_state diff --git a/pumpkin-crates/core/src/engine/cp/test_solver.rs b/pumpkin-crates/core/src/engine/cp/test_solver.rs index 4bbcdf795..de5cbe117 100644 --- a/pumpkin-crates/core/src/engine/cp/test_solver.rs +++ b/pumpkin-crates/core/src/engine/cp/test_solver.rs @@ -17,6 +17,7 @@ use crate::predicate; use crate::predicates::PropositionalConjunction; use crate::proof::ConstraintTag; use crate::proof::InferenceCode; +use crate::proof::InferenceLabel; use crate::propagation::EnqueueDecision; use crate::propagation::ExplanationContext; use crate::propagation::NotificationContext; @@ -57,7 +58,11 @@ impl Default for TestSolver { #[deprecated = "Will be replaced by the state API"] impl TestSolver { - pub fn accept_inferences_by(&mut self, inference_code: InferenceCode) { + pub fn accept_inferences_by( + &mut self, + constraint_tag: ConstraintTag, + inference_label: impl InferenceLabel, + ) -> InferenceCode { #[derive(Debug, Clone, Copy)] struct Checker; @@ -73,7 +78,7 @@ impl TestSolver { } self.state - .add_inference_checker(inference_code, Box::new(Checker)); + .add_inference_checker(constraint_tag, inference_label, Checker) } pub fn new_variable(&mut self, lb: i32, ub: i32) -> DomainId { diff --git a/pumpkin-crates/core/src/engine/state.rs b/pumpkin-crates/core/src/engine/state.rs index db0d786e0..db56275bb 100644 --- a/pumpkin-crates/core/src/engine/state.rs +++ b/pumpkin-crates/core/src/engine/state.rs @@ -5,7 +5,8 @@ use pumpkin_checking::InferenceChecker; #[cfg(feature = "check-propagations")] use pumpkin_checking::VariableState; -use crate::containers::HashMap; +use crate::checkers::CheckerStore; +use crate::checkers::StoresCheckers; use crate::containers::KeyGenerator; use crate::create_statistics_struct; use crate::engine::Assignments; @@ -25,11 +26,11 @@ use crate::predicates::PredicateType; use crate::predicates::PropositionalConjunction; use crate::proof::ConstraintTag; use crate::proof::InferenceCode; +use crate::proof::InferenceLabel; +use crate::propagation::ConstructedPropagator; use crate::propagation::CurrentNogood; use crate::propagation::Domains; use crate::propagation::ExplanationContext; -#[cfg(feature = "check-propagations")] -use crate::propagation::InferenceCheckers; use crate::propagation::NotificationContext; use crate::propagation::PropagationContext; use crate::propagation::Propagator; @@ -81,8 +82,8 @@ pub struct State { statistics: StateStatistics, - /// Inference checkers to run in the propagation loop. - checkers: HashMap>>, + /// Runtime checkers to run in the propagation loop. + checkers: CheckerStore, } create_statistics_struct!(StateStatistics { @@ -112,7 +113,7 @@ impl Default for State { notification_engine: NotificationEngine::default(), statistics: StateStatistics::default(), constraint_tags: KeyGenerator::default(), - checkers: HashMap::default(), + checkers: CheckerStore::default(), }; // As a convention, the assignments contain a dummy domain_id=0, which represents a 0-1 // variable that is assigned to one. We use it to represent predicates that are @@ -334,15 +335,17 @@ impl State { Constructor: PropagatorConstructor, Constructor::PropagatorImpl: 'static, { - #[cfg(feature = "check-propagations")] - constructor.add_inference_checkers(InferenceCheckers::new(self)); - let original_handle: PropagatorHandle = self.propagators.new_propagator().key(); let constructor_context = PropagatorConstructorContext::new(original_handle.propagator_id(), self); - let (registration, propagator) = constructor.create(constructor_context); + + let ConstructedPropagator { + registration, + checkers, + propagator, + } = constructor.create(constructor_context); for (domain_id, events, local_id) in registration.iter() { let propagator_var = PropagatorVarId { @@ -354,6 +357,10 @@ impl State { .watch_all(domain_id, events, propagator_var); } + for (inference_code, checker) in checkers.into_iter() { + self.checkers.add_inference_checker(inference_code, checker); + } + pumpkin_assert_simple!( propagator.priority() as u8 <= 3, "The propagator priority exceeds 3. @@ -381,11 +388,14 @@ impl State { /// any checker accepts the inference, the inference is accepted. pub fn add_inference_checker( &mut self, - inference_code: InferenceCode, - checker: Box>, - ) { - let checkers = self.checkers.entry(inference_code).or_default(); - checkers.push(BoxedChecker::from(checker)); + constraint_tag: ConstraintTag, + inference_label: impl InferenceLabel, + checker: impl InferenceChecker + 'static, + ) -> InferenceCode { + let inference_code = InferenceCode::new(constraint_tag, inference_label); + self.checkers + .add_inference_checker(inference_code.clone(), BoxedChecker::new(Box::new(checker))); + inference_code } } @@ -781,31 +791,23 @@ impl State { ) { let premises: Vec<_> = premises.into_iter().collect(); - let checkers = self - .checkers - .get(inference_code) - .map(|vec| vec.as_slice()) - .unwrap_or(&[]); - - assert!( - !checkers.is_empty(), - "missing checker for inference code {inference_code:?}" - ); - - let any_checker_accepts_inference = checkers.iter().any(|checker| { - // Construct the variable state for the conflict check. - let variable_state = VariableState::prepare_for_conflict_check( - premises.clone(), - consequent, - ) - .unwrap_or_else(|domain| { - panic!( - "inconsistent atomics over domain {domain:?} in inference by {inference_code:?}" - ) - }); - - checker.check(variable_state, &premises, consequent.as_ref()) - }); + let any_checker_accepts_inference = + self.checkers + .for_inference_code(inference_code) + .any(|checker| { + // Construct the variable state for the conflict check. + let variable_state = VariableState::prepare_for_conflict_check( + premises.clone(), + consequent, + ) + .unwrap_or_else(|domain| { + panic!( + "inconsistent atomics over domain {domain:?} in inference by {inference_code:?}" + ) + }); + + checker.check(variable_state, &premises, consequent.as_ref()) + }); assert!( any_checker_accepts_inference, diff --git a/pumpkin-crates/core/src/lib.rs b/pumpkin-crates/core/src/lib.rs index 2a8680544..19d6fcefb 100644 --- a/pumpkin-crates/core/src/lib.rs +++ b/pumpkin-crates/core/src/lib.rs @@ -12,6 +12,7 @@ use crate::branching::Brancher; use crate::termination::TerminationCondition; pub mod branching; +pub mod checkers; pub mod conflict_resolving; pub mod constraints; pub mod optimisation; diff --git a/pumpkin-crates/core/src/proof/inference_code.rs b/pumpkin-crates/core/src/proof/inference_code.rs index afc4a4ece..78ca55a88 100644 --- a/pumpkin-crates/core/src/proof/inference_code.rs +++ b/pumpkin-crates/core/src/proof/inference_code.rs @@ -140,4 +140,10 @@ pub trait InferenceLabel { fn to_str(&self) -> Arc; } +impl InferenceLabel for Arc { + fn to_str(&self) -> Arc { + Arc::clone(self) + } +} + declare_inference_label!(pub Unknown); diff --git a/pumpkin-crates/core/src/propagation/constructor.rs b/pumpkin-crates/core/src/propagation/constructor.rs index da90931c7..bc85e72ce 100644 --- a/pumpkin-crates/core/src/propagation/constructor.rs +++ b/pumpkin-crates/core/src/propagation/constructor.rs @@ -1,5 +1,3 @@ -use pumpkin_checking::InferenceChecker; - use super::Domains; use super::LocalId; use super::Propagator; @@ -17,14 +15,12 @@ use crate::engine::variables::AffineView; #[cfg(doc)] use crate::engine::variables::DomainId; use crate::predicates::Predicate; -use crate::proof::InferenceCode; #[cfg(doc)] use crate::propagation::DomainEvent; use crate::propagation::DomainEvents; use crate::propagation::EventRegistration; -use crate::propagators::reified_propagator::ReifiedChecker; +use crate::propagation::runtime_checkers::RuntimeCheckers; use crate::variables::IntegerVariable; -use crate::variables::Literal; /// A propagator constructor creates a fully initialized instance of a [`Propagator`]. /// @@ -36,60 +32,22 @@ pub trait PropagatorConstructor { /// The propagator that is produced by this constructor. type PropagatorImpl: Propagator + Clone; - /// Add inference checkers to the solver if applicable. - /// - /// If the `check-propagations` feature is turned on, then the inference checker will be used - /// to verify the propagations done by this propagator are correct. - /// - /// See [`InferenceChecker`] for more information. - fn add_inference_checkers(&self, _checkers: InferenceCheckers<'_>) {} - /// Create the propagator instance from `Self`. fn create( self, context: PropagatorConstructorContext, - ) -> (EventRegistration, Self::PropagatorImpl); -} - -/// Interface used to add [`InferenceChecker`]s to the [`State`]. -#[derive(Debug)] -pub struct InferenceCheckers<'state> { - state: &'state mut State, - reification_literal: Option, + ) -> ConstructedPropagator; } -impl<'state> InferenceCheckers<'state> { - #[cfg(feature = "check-propagations")] - pub(crate) fn new(state: &'state mut State) -> Self { - InferenceCheckers { - state, - reification_literal: None, - } - } -} - -impl InferenceCheckers<'_> { - /// Forwards to [`State::add_inference_checker`]. - pub fn add_inference_checker( - &mut self, - inference_code: InferenceCode, - checker: Box>, - ) { - if let Some(reification_literal) = self.reification_literal { - let reification_checker = ReifiedChecker { - inner: checker.into(), - reification_literal, - }; - self.state - .add_inference_checker(inference_code, Box::new(reification_checker)); - } else { - self.state.add_inference_checker(inference_code, checker); - } - } - - pub fn with_reification_literal(&mut self, literal: Literal) { - self.reification_literal = Some(literal) - } +/// The result of [`PropagatorConstructor::create`]. +#[derive(Clone, Debug)] +pub struct ConstructedPropagator

{ + /// The domain events the propagator needs to be be registered for. + pub registration: EventRegistration, + /// Any runtime checkers that verify the propagator's implementation. + pub checkers: RuntimeCheckers, + /// The propagator + pub propagator: P, } /// [`PropagatorConstructorContext`] is used when [`Propagator`]s are initialised after creation. @@ -167,18 +125,6 @@ impl PropagatorConstructorContext<'_> { state: self.state, } } - - /// Add an inference checker for inferences produced by the propagator. - /// - /// If the `check-propagations` feature is not enabled, adding an [`InferenceChecker`] will not - /// do anything. - pub fn add_inference_checker( - &mut self, - inference_code: InferenceCode, - checker: Box>, - ) { - self.state.add_inference_checker(inference_code, checker); - } } mod private { diff --git a/pumpkin-crates/core/src/propagation/mod.rs b/pumpkin-crates/core/src/propagation/mod.rs index c74ee7106..b6631560a 100644 --- a/pumpkin-crates/core/src/propagation/mod.rs +++ b/pumpkin-crates/core/src/propagation/mod.rs @@ -72,12 +72,14 @@ mod domains; mod event_registration; mod local_id; mod propagator; +mod runtime_checkers; pub(crate) mod propagator_id; pub(crate) mod propagator_var_id; pub(crate) mod store; pub use event_registration::*; +pub use runtime_checkers::*; mod reexports { // Re-exports of types not in this module according to the file tree. diff --git a/pumpkin-crates/core/src/propagation/runtime_checkers.rs b/pumpkin-crates/core/src/propagation/runtime_checkers.rs new file mode 100644 index 000000000..0a8b0a08f --- /dev/null +++ b/pumpkin-crates/core/src/propagation/runtime_checkers.rs @@ -0,0 +1,95 @@ +use pumpkin_checking::BoxedChecker; +use pumpkin_checking::InferenceChecker; + +use crate::predicates::Predicate; +use crate::proof::ConstraintTag; +use crate::proof::InferenceCode; +use crate::proof::InferenceLabel; + +/// Holds the runtime checkers that are added by a propagator. +#[derive(Clone, Debug)] +pub struct RuntimeCheckers { + inference_checkers: Vec<(InferenceCode, BoxedChecker)>, +} + +impl RuntimeCheckers { + /// Create a [`RuntimeCheckers`] value which we accept may be empty. + /// + /// This is often not what you want. If it is expected that some checkers should be added, + /// use [`RuntimeCheckers::builder`] instead to communicate that intention. + pub fn empty() -> RuntimeCheckers { + RuntimeCheckers { + inference_checkers: vec![], + } + } + + /// Create a [`RuntimeCheckersBuilder`] to add runtime checkers. + /// + /// The [`RuntimeCheckersBuilder::build`] will panic if no checkers are added. + pub fn builder() -> RuntimeCheckersBuilder { + RuntimeCheckersBuilder { + checkers: RuntimeCheckers { + inference_checkers: vec![], + }, + } + } + + /// Add an [`InferenceChecker`] to verify the soundness of propagations. + pub fn add_inference_checker( + &mut self, + constraint_tag: ConstraintTag, + inference_label: impl InferenceLabel, + checker: impl InferenceChecker + 'static, + ) -> InferenceCode { + let inference_code = InferenceCode::new(constraint_tag, inference_label); + + self.inference_checkers + .push((inference_code.clone(), BoxedChecker::new(Box::new(checker)))); + + inference_code + } +} + +impl IntoIterator for RuntimeCheckers { + type Item = (InferenceCode, BoxedChecker); + + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.inference_checkers.into_iter() + } +} + +/// A builder for the [`RuntimeCheckers`] that ensures at least one checker is added. +#[derive(Clone, Debug)] +pub struct RuntimeCheckersBuilder { + checkers: RuntimeCheckers, +} + +impl RuntimeCheckersBuilder { + /// Add an [`InferenceChecker`] to verify the soundness of propagations. + pub fn add_inference_checker( + &mut self, + constraint_tag: ConstraintTag, + inference_label: impl InferenceLabel, + checker: impl InferenceChecker + 'static, + ) -> InferenceCode { + self.checkers + .add_inference_checker(constraint_tag, inference_label, checker) + } + + /// Finish adding runtime checkers. + /// + /// Panics if runtime verification is enabled and no checkers are added. If it is expected + /// behavior that no checkers are added, use [`RuntimeCheckers::empty`]. + pub fn build(self) -> RuntimeCheckers { + if cfg!(feature = "check-propagations") { + assert!( + !self.checkers.inference_checkers.is_empty(), + "did not register any inference checkers" + ); + } + + self.checkers + } +} diff --git a/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs b/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs index 3b6d87d3a..baca99576 100644 --- a/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs +++ b/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs @@ -6,15 +6,16 @@ use crate::predicates::Predicate; use crate::predicates::PropositionalConjunction; use crate::proof::ConstraintTag; use crate::proof::InferenceCode; +use crate::propagation::ConstructedPropagator; use crate::propagation::DomainEvents; use crate::propagation::EventRegistration; -use crate::propagation::InferenceCheckers; use crate::propagation::LocalId; use crate::propagation::PropagationContext; use crate::propagation::Propagator; use crate::propagation::PropagatorConstructor; use crate::propagation::PropagatorConstructorContext; use crate::propagation::ReadDomains; +use crate::propagation::RuntimeCheckers; use crate::propagators::hypercube_linear::Hypercube; use crate::propagators::hypercube_linear::HypercubeLinearChecker; use crate::propagators::hypercube_linear::LinearInequality; @@ -34,21 +35,10 @@ pub struct HypercubeLinearConstructor { impl PropagatorConstructor for HypercubeLinearConstructor { type PropagatorImpl = HypercubeLinearPropagator; - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.add_inference_checker( - InferenceCode::new(self.constraint_tag, HypercubeLinear), - Box::new(HypercubeLinearChecker { - hypercube: self.hypercube.iter_predicates().collect(), - terms: self.linear.terms().collect(), - bound: self.linear.bound(), - }), - ); - } - fn create( self, mut context: PropagatorConstructorContext, - ) -> (EventRegistration, Self::PropagatorImpl) { + ) -> ConstructedPropagator { let HypercubeLinearConstructor { hypercube, linear, @@ -69,18 +59,33 @@ impl PropagatorConstructor for HypercubeLinearConstructor { ] }; + let mut checkers = RuntimeCheckers::builder(); + let inference_code = checkers.add_inference_checker( + constraint_tag, + HypercubeLinear, + HypercubeLinearChecker { + hypercube: hypercube.iter_predicates().collect(), + terms: linear.terms().collect(), + bound: linear.bound(), + }, + ); + let propagator = HypercubeLinearPropagator { linear, hypercube_predicates, watched_predicates, - inference_code: InferenceCode::new(constraint_tag, HypercubeLinear), + inference_code, }; // TODO: This will be expanded with registration of predicates. let registration = EventRegistration::empty(); - (registration, propagator) + ConstructedPropagator { + registration, + checkers: checkers.build(), + propagator, + } } } diff --git a/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs b/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs index c8b96aeac..7ca0d00ec 100644 --- a/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs +++ b/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs @@ -20,6 +20,7 @@ use crate::engine::reason::Reason; use crate::engine::reason::ReasonStore; use crate::predicate; use crate::proof::InferenceCode; +use crate::propagation::ConstructedPropagator; use crate::propagation::EnqueueDecision; use crate::propagation::EventRegistration; use crate::propagation::ExplanationContext; @@ -31,6 +32,7 @@ use crate::propagation::Propagator; use crate::propagation::PropagatorConstructor; use crate::propagation::PropagatorConstructorContext; use crate::propagation::ReadDomains; +use crate::propagation::RuntimeCheckers; use crate::propagators::nogoods::arena_allocator::ArenaAllocator; use crate::propagators::nogoods::arena_allocator::NogoodIndex; use crate::pumpkin_assert_advanced; @@ -105,7 +107,7 @@ impl PropagatorConstructor for NogoodPropagatorConstructor { fn create( self, context: PropagatorConstructorContext, - ) -> (EventRegistration, Self::PropagatorImpl) { + ) -> ConstructedPropagator { let propagator = NogoodPropagator { handle: PropagatorHandle::new(context.propagator_id), parameters: self.parameters, @@ -121,7 +123,11 @@ impl PropagatorConstructor for NogoodPropagatorConstructor { temp_nogood_reason: Default::default(), }; - (EventRegistration::empty(), propagator) + ConstructedPropagator { + registration: EventRegistration::empty(), + checkers: RuntimeCheckers::empty(), + propagator, + } } } diff --git a/pumpkin-crates/core/src/propagators/reified_propagator.rs b/pumpkin-crates/core/src/propagators/reified_propagator.rs index 51e925e7c..0a4ddfebd 100644 --- a/pumpkin-crates/core/src/propagators/reified_propagator.rs +++ b/pumpkin-crates/core/src/propagators/reified_propagator.rs @@ -6,12 +6,11 @@ use pumpkin_checking::InferenceChecker; use crate::engine::PropagationStatusCP; use crate::engine::notifications::OpaqueDomainEvent; use crate::predicates::Predicate; +use crate::propagation::ConstructedPropagator; use crate::propagation::DomainEvents; use crate::propagation::Domains; use crate::propagation::EnqueueDecision; -use crate::propagation::EventRegistration; use crate::propagation::ExplanationContext; -use crate::propagation::InferenceCheckers; use crate::propagation::LazyExplanation; use crate::propagation::LocalId; use crate::propagation::NotificationContext; @@ -21,6 +20,7 @@ use crate::propagation::Propagator; use crate::propagation::PropagatorConstructor; use crate::propagation::PropagatorConstructorContext; use crate::propagation::ReadDomains; +use crate::propagation::RuntimeCheckers; use crate::pumpkin_assert_simple; use crate::state::Conflict; use crate::variables::Literal; @@ -42,14 +42,20 @@ where fn create( self, mut context: PropagatorConstructorContext, - ) -> (EventRegistration, Self::PropagatorImpl) { + ) -> ConstructedPropagator { let ReifiedPropagatorArgs { propagator, reification_literal, } = self; - let (mut registration, propagator) = propagator.create(context.reborrow()); + let ConstructedPropagator { + mut registration, + propagator, + checkers, + } = propagator.create(context.reborrow()); + // The local ID for the reification literal will be one larger than the largest ID + // registered by the wrapped propagator. let reification_literal_id = registration .iter() .map(|(_, _, lid)| lid) @@ -63,6 +69,18 @@ where reification_literal_id, ); + let mut wrapped_checkers = RuntimeCheckers::empty(); + for (inference_code, checker) in checkers.into_iter() { + let _ = wrapped_checkers.add_inference_checker( + inference_code.tag(), + inference_code.label(), + ReifiedChecker { + inner: checker, + reification_literal, + }, + ); + } + let name = format!("Reified({})", propagator.name()); let propagator = ReifiedPropagator { @@ -73,13 +91,11 @@ where reason_buffer: vec![], }; - (registration, propagator) - } - - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.with_reification_literal(self.reification_literal); - - self.propagator.add_inference_checkers(checkers); + ConstructedPropagator { + registration, + checkers: wrapped_checkers, + propagator, + } } } @@ -286,6 +302,8 @@ mod tests { use crate::predicates::PropositionalConjunction; use crate::proof::ConstraintTag; use crate::proof::InferenceCode; + use crate::proof::Unknown; + use crate::propagation::EventRegistration; use crate::variables::DomainId; #[test] @@ -300,8 +318,8 @@ mod tests { let t1 = triggered_conflict.clone(); let t2 = triggered_conflict.clone(); - let inference_code = InferenceCode::unknown_label(ConstraintTag::create_from_index(0)); - solver.accept_inferences_by(inference_code.clone()); + let inference_code = + solver.accept_inferences_by(ConstraintTag::create_from_index(0), Unknown); let i1 = inference_code.clone(); let i2 = inference_code.clone(); @@ -381,8 +399,8 @@ mod tests { let _ = solver.set_literal(reification_literal, true); let var = solver.new_variable(1, 1); - let inference_code = InferenceCode::unknown_label(ConstraintTag::create_from_index(0)); - solver.accept_inferences_by(inference_code.clone()); + let inference_code = + solver.accept_inferences_by(ConstraintTag::create_from_index(0), Unknown); let inconsistency = solver .new_propagator(ReifiedPropagatorArgs { @@ -423,8 +441,8 @@ mod tests { let reification_literal = solver.new_literal(); let var = solver.new_variable(1, 5); - let inference_code = InferenceCode::unknown_label(ConstraintTag::create_from_index(0)); - solver.accept_inferences_by(inference_code.clone()); + let inference_code = + solver.accept_inferences_by(ConstraintTag::create_from_index(0), Unknown); let propagator = solver .new_propagator(ReifiedPropagatorArgs { @@ -469,14 +487,18 @@ mod tests { fn create( self, _: PropagatorConstructorContext, - ) -> (EventRegistration, Self::PropagatorImpl) { + ) -> ConstructedPropagator { let mut registration = EventRegistration::empty(); for (index, variable) in self.variables_to_register.iter().enumerate() { registration.add(variable, DomainEvents::ANY_INT, LocalId::from(index as u32)); } - (registration, self) + ConstructedPropagator { + registration, + checkers: RuntimeCheckers::empty(), + propagator: self, + } } } From 816773efefbd8ae38c4f7ae5dbee11b0947329bb Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Thu, 28 May 2026 12:16:15 +1000 Subject: [PATCH 08/12] Apply the interface in the rest of the solver --- .../propagators/arithmetic/absolute_value.rs | 34 +++++++----- .../arithmetic/binary/binary_equals.rs | 36 ++++++++----- .../arithmetic/binary/binary_not_equals.rs | 36 ++++++++----- .../arithmetic/integer_division.rs | 33 ++++++------ .../arithmetic/integer_multiplication.rs | 38 +++++++------ .../arithmetic/linear_less_or_equal.rs | 30 ++++++----- .../arithmetic/linear_not_equal.rs | 33 +++++++----- .../src/propagators/arithmetic/maximum.rs | 34 +++++++----- .../time_table_over_interval_incremental.rs | 53 ++++++++++--------- .../time_table_per_point_incremental.rs | 52 +++++++++--------- .../time_table/time_table_over_interval.rs | 52 +++++++++--------- .../time_table/time_table_per_point.rs | 52 +++++++++--------- .../disjunctive/disjunctive_propagator.rs | 47 ++++++++-------- .../propagators/src/propagators/element.rs | 32 +++++------ .../src/deduction_propagator.rs | 10 +++- 15 files changed, 324 insertions(+), 248 deletions(-) diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs index bd824a32b..b66d0851d 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs @@ -7,9 +7,9 @@ use pumpkin_core::declare_inference_label; use pumpkin_core::predicate; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::EventRegistration; -use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; use pumpkin_core::propagation::PropagationContext; @@ -17,6 +17,7 @@ use pumpkin_core::propagation::Propagator; use pumpkin_core::propagation::PropagatorConstructor; use pumpkin_core::propagation::PropagatorConstructorContext; use pumpkin_core::propagation::ReadDomains; +use pumpkin_core::propagation::RuntimeCheckers; use pumpkin_core::state::PropagationStatusCP; use pumpkin_core::variables::IntegerVariable; @@ -36,17 +37,10 @@ where { type PropagatorImpl = AbsoluteValuePropagator; - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.add_inference_checker( - InferenceCode::new(self.constraint_tag, AbsoluteValue), - Box::new(AbsoluteValueChecker { - signed: self.signed.clone(), - absolute: self.absolute.clone(), - }), - ); - } - - fn create(self, _: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { + fn create( + self, + _: PropagatorConstructorContext, + ) -> ConstructedPropagator { let AbsoluteValueArgs { signed, absolute, @@ -58,7 +52,15 @@ where .add(&absolute, DomainEvents::BOUNDS, LocalId::from(1)) .build(); - let inference_code = InferenceCode::new(constraint_tag, AbsoluteValue); + let mut checkers = RuntimeCheckers::builder(); + let inference_code = checkers.add_inference_checker( + constraint_tag, + AbsoluteValue, + AbsoluteValueChecker { + signed: signed.clone(), + absolute: absolute.clone(), + }, + ); let propagator = AbsoluteValuePropagator { signed, @@ -66,7 +68,11 @@ where inference_code, }; - (registration, propagator) + ConstructedPropagator { + registration, + checkers: checkers.build(), + propagator, + } } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs index adc34e36e..107eb31ff 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs @@ -16,13 +16,13 @@ use pumpkin_core::predicates::PredicateConstructor; use pumpkin_core::predicates::PredicateType; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::ExplanationContext; -use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LazyExplanation; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::NotificationContext; @@ -33,6 +33,7 @@ use pumpkin_core::propagation::Propagator; use pumpkin_core::propagation::PropagatorConstructor; use pumpkin_core::propagation::PropagatorConstructorContext; use pumpkin_core::propagation::ReadDomains; +use pumpkin_core::propagation::RuntimeCheckers; use pumpkin_core::state::EmptyDomainConflict; use pumpkin_core::state::PropagationStatusCP; use pumpkin_core::state::PropagatorConflict; @@ -55,17 +56,10 @@ where { type PropagatorImpl = BinaryEqualsPropagator; - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.add_inference_checker( - InferenceCode::new(self.constraint_tag, BinaryEquals), - Box::new(BinaryEqualsChecker { - lhs: self.a.clone(), - rhs: self.b.clone(), - }), - ); - } - - fn create(self, _: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { + fn create( + self, + _: PropagatorConstructorContext, + ) -> ConstructedPropagator { let BinaryEqualsPropagatorArgs { a, b, @@ -77,6 +71,16 @@ where .add(&b, DomainEvents::ANY_INT, LocalId::from(1)) .build(); + let mut checkers = RuntimeCheckers::builder(); + let inference_code = checkers.add_inference_checker( + constraint_tag, + BinaryEquals, + BinaryEqualsChecker { + lhs: a.clone(), + rhs: b.clone(), + }, + ); + let propagator = BinaryEqualsPropagator { a, b, @@ -84,14 +88,18 @@ where a_removed_values: HashSet::default(), b_removed_values: HashSet::default(), - inference_code: InferenceCode::new(constraint_tag, BinaryEquals), + inference_code, has_backtracked: false, first_propagation_loop: true, reason: Predicate::trivially_false(), }; - (registration, propagator) + ConstructedPropagator { + registration, + checkers: checkers.build(), + propagator, + } } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs index 60f7e49e3..dae15494d 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs @@ -6,10 +6,10 @@ use pumpkin_core::declare_inference_label; use pumpkin_core::predicate; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EventRegistration; -use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; use pumpkin_core::propagation::PropagationContext; @@ -17,6 +17,7 @@ use pumpkin_core::propagation::Propagator; use pumpkin_core::propagation::PropagatorConstructor; use pumpkin_core::propagation::PropagatorConstructorContext; use pumpkin_core::propagation::ReadDomains; +use pumpkin_core::propagation::RuntimeCheckers; use pumpkin_core::state::PropagationStatusCP; use pumpkin_core::state::PropagatorConflict; use pumpkin_core::variables::IntegerVariable; @@ -38,17 +39,10 @@ where { type PropagatorImpl = BinaryNotEqualsPropagator; - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.add_inference_checker( - InferenceCode::new(self.constraint_tag, BinaryNotEquals), - Box::new(BinaryNotEqualsChecker { - lhs: self.a.clone(), - rhs: self.b.clone(), - }), - ); - } - - fn create(self, _: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { + fn create( + self, + _: PropagatorConstructorContext, + ) -> ConstructedPropagator { let BinaryNotEqualsPropagatorArgs { a, b, @@ -61,14 +55,28 @@ where .add(&b, DomainEvents::ASSIGN, LocalId::from(1)) .build(); + let mut checkers = RuntimeCheckers::builder(); + let inference_code = checkers.add_inference_checker( + constraint_tag, + BinaryNotEquals, + BinaryNotEqualsChecker { + lhs: a.clone(), + rhs: b.clone(), + }, + ); + let propagator = BinaryNotEqualsPropagator { a, b, - inference_code: InferenceCode::new(constraint_tag, BinaryNotEquals), + inference_code, }; - (registration, propagator) + ConstructedPropagator { + registration, + checkers: checkers.build(), + propagator, + } } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs index b9dccaf4e..552ac0b10 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs @@ -8,9 +8,9 @@ use pumpkin_core::declare_inference_label; use pumpkin_core::predicate; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::EventRegistration; -use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; use pumpkin_core::propagation::PropagationContext; @@ -18,6 +18,7 @@ use pumpkin_core::propagation::Propagator; use pumpkin_core::propagation::PropagatorConstructor; use pumpkin_core::propagation::PropagatorConstructorContext; use pumpkin_core::propagation::ReadDomains; +use pumpkin_core::propagation::RuntimeCheckers; use pumpkin_core::state::PropagationStatusCP; use pumpkin_core::variables::IntegerVariable; @@ -47,7 +48,7 @@ where fn create( self, context: PropagatorConstructorContext, - ) -> (EventRegistration, Self::PropagatorImpl) { + ) -> ConstructedPropagator { let DivisionArgs { numerator, denominator, @@ -66,7 +67,16 @@ where .add(&rhs, DomainEvents::BOUNDS, ID_RHS) .build(); - let inference_code = InferenceCode::new(constraint_tag, Division); + let mut checkers = RuntimeCheckers::builder(); + let inference_code = checkers.add_inference_checker( + constraint_tag, + Division, + IntegerDivisionChecker { + numerator: numerator.clone(), + denominator: denominator.clone(), + rhs: rhs.clone(), + }, + ); let propagator = DivisionPropagator { numerator, @@ -75,18 +85,11 @@ where inference_code, }; - (registration, propagator) - } - - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.add_inference_checker( - InferenceCode::new(self.constraint_tag, Division), - Box::new(IntegerDivisionChecker { - numerator: self.numerator.clone(), - denominator: self.denominator.clone(), - rhs: self.rhs.clone(), - }), - ); + ConstructedPropagator { + registration, + checkers: checkers.build(), + propagator, + } } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs index 65a3519b4..ba580a08a 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs @@ -7,9 +7,9 @@ use pumpkin_core::declare_inference_label; use pumpkin_core::predicate; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::EventRegistration; -use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; use pumpkin_core::propagation::PropagationContext; @@ -17,6 +17,7 @@ use pumpkin_core::propagation::Propagator; use pumpkin_core::propagation::PropagatorConstructor; use pumpkin_core::propagation::PropagatorConstructorContext; use pumpkin_core::propagation::ReadDomains; +use pumpkin_core::propagation::RuntimeCheckers; use pumpkin_core::state::PropagationStatusCP; use pumpkin_core::state::propagator_conflict; use pumpkin_core::variables::IntegerVariable; @@ -40,18 +41,10 @@ where { type PropagatorImpl = IntegerMultiplicationPropagator; - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.add_inference_checker( - InferenceCode::new(self.constraint_tag, IntegerMultiplication), - Box::new(IntegerMultiplicationChecker { - a: self.a.clone(), - b: self.b.clone(), - c: self.c.clone(), - }), - ); - } - - fn create(self, _: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { + fn create( + self, + _: PropagatorConstructorContext, + ) -> ConstructedPropagator { let IntegerMultiplicationArgs { a, b, @@ -65,14 +58,29 @@ where .add(&c, DomainEvents::ANY_INT, ID_C) .build(); + let mut checkers = RuntimeCheckers::builder(); + let inference_code = checkers.add_inference_checker( + constraint_tag, + IntegerMultiplication, + IntegerMultiplicationChecker { + a: a.clone(), + b: b.clone(), + c: c.clone(), + }, + ); + let propagator = IntegerMultiplicationPropagator { a, b, c, - inference_code: InferenceCode::new(constraint_tag, IntegerMultiplication), + inference_code, }; - (registration, propagator) + ConstructedPropagator { + registration, + checkers: checkers.build(), + propagator, + } } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs index ab244ff96..9ed1def32 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs @@ -10,12 +10,12 @@ use pumpkin_core::predicates::Predicate; use pumpkin_core::predicates::PropositionalConjunction; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::ExplanationContext; -use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LazyExplanation; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::NotificationContext; @@ -26,6 +26,7 @@ use pumpkin_core::propagation::Propagator; use pumpkin_core::propagation::PropagatorConstructor; use pumpkin_core::propagation::PropagatorConstructorContext; use pumpkin_core::propagation::ReadDomains; +use pumpkin_core::propagation::RuntimeCheckers; use pumpkin_core::propagation::TrailedInteger; use pumpkin_core::state::PropagationStatusCP; use pumpkin_core::state::PropagatorConflict; @@ -47,20 +48,10 @@ where { type PropagatorImpl = LinearLessOrEqualPropagator; - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.add_inference_checker( - InferenceCode::new(self.constraint_tag, LinearBounds), - Box::new(LinearLessOrEqualInferenceChecker::new( - self.x.clone(), - self.c, - )), - ); - } - fn create( self, mut context: PropagatorConstructorContext, - ) -> (EventRegistration, Self::PropagatorImpl) { + ) -> ConstructedPropagator { let LinearLessOrEqualPropagatorArgs { x, c, @@ -80,16 +71,27 @@ where let lower_bound_left_hand_side = context.new_trailed_integer(lower_bound_left_hand_side); + let mut checkers = RuntimeCheckers::builder(); + let inference_code = checkers.add_inference_checker( + constraint_tag, + LinearBounds, + LinearLessOrEqualInferenceChecker::new(x.clone(), c), + ); + let propagator = LinearLessOrEqualPropagator { x, c, lower_bound_left_hand_side, current_bounds: current_bounds.into(), - inference_code: InferenceCode::new(constraint_tag, LinearBounds), + inference_code, reason_buffer: Vec::default(), }; - (registration.build(), propagator) + ConstructedPropagator { + registration: registration.build(), + checkers: checkers.build(), + propagator, + } } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs index 23fc766a9..cc02997a6 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs @@ -14,12 +14,12 @@ use pumpkin_core::predicate; use pumpkin_core::predicates::PropositionalConjunction; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; use pumpkin_core::propagation::EventRegistration; -use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::NotificationContext; use pumpkin_core::propagation::OpaqueDomainEvent; @@ -29,6 +29,7 @@ use pumpkin_core::propagation::Propagator; use pumpkin_core::propagation::PropagatorConstructor; use pumpkin_core::propagation::PropagatorConstructorContext; use pumpkin_core::propagation::ReadDomains; +use pumpkin_core::propagation::RuntimeCheckers; use pumpkin_core::state::PropagationStatusCP; use pumpkin_core::state::PropagatorConflict; use pumpkin_core::variables::IntegerVariable; @@ -51,20 +52,10 @@ where { type PropagatorImpl = LinearNotEqualPropagator; - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.add_inference_checker( - InferenceCode::new(self.constraint_tag, LinearNotEquals), - Box::new(LinearNotEqualChecker { - terms: self.terms.as_ref().into(), - bound: self.rhs, - }), - ); - } - fn create( self, mut context: PropagatorConstructorContext, - ) -> (EventRegistration, Self::PropagatorImpl) { + ) -> ConstructedPropagator { let LinearNotEqualPropagatorArgs { terms, rhs, @@ -81,6 +72,16 @@ where ); } + let mut checkers = RuntimeCheckers::builder(); + let inference_code = checkers.add_inference_checker( + constraint_tag, + LinearNotEquals, + LinearNotEqualChecker { + terms: terms.as_ref().into(), + bound: rhs, + }, + ); + let mut propagator = LinearNotEqualPropagator { terms, rhs, @@ -88,12 +89,16 @@ where fixed_lhs: 0, unfixed_variable_has_been_updated: false, should_recalculate_lhs: false, - inference_code: InferenceCode::new(constraint_tag, LinearNotEquals), + inference_code, }; propagator.recalculate_fixed_variables(context.domains()); - (registration.build(), propagator) + ConstructedPropagator { + registration: registration.build(), + checkers: checkers.build(), + propagator, + } } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs index 85761c40c..046f4ed31 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs @@ -8,9 +8,9 @@ use pumpkin_core::predicate; use pumpkin_core::predicates::PropositionalConjunction; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::EventRegistration; -use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; use pumpkin_core::propagation::PropagationContext; @@ -18,6 +18,7 @@ use pumpkin_core::propagation::Propagator; use pumpkin_core::propagation::PropagatorConstructor; use pumpkin_core::propagation::PropagatorConstructorContext; use pumpkin_core::propagation::ReadDomains; +use pumpkin_core::propagation::RuntimeCheckers; use pumpkin_core::state::PropagationStatusCP; use pumpkin_core::variables::IntegerVariable; @@ -37,17 +38,10 @@ where { type PropagatorImpl = MaximumPropagator; - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.add_inference_checker( - InferenceCode::new(self.constraint_tag, Maximum), - Box::new(MaximumChecker { - array: self.array.clone(), - rhs: self.rhs.clone(), - }), - ); - } - - fn create(self, _: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { + fn create( + self, + _: PropagatorConstructorContext, + ) -> ConstructedPropagator { let MaximumArgs { array, rhs, @@ -65,7 +59,15 @@ where LocalId::from(array.len() as u32), ); - let inference_code = InferenceCode::new(constraint_tag, Maximum); + let mut checkers = RuntimeCheckers::builder(); + let inference_code = checkers.add_inference_checker( + constraint_tag, + Maximum, + MaximumChecker { + array: array.clone(), + rhs: rhs.clone(), + }, + ); let propagator = MaximumPropagator { array, @@ -73,7 +75,11 @@ where inference_code, }; - (registration.build(), propagator) + ConstructedPropagator { + registration: registration.build(), + checkers: checkers.build(), + propagator, + } } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs index 312bd2c83..cf3e8229f 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs @@ -8,11 +8,11 @@ use pumpkin_core::asserts::pumpkin_assert_simple; use pumpkin_core::conjunction; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; -use pumpkin_core::propagation::EventRegistration; -use pumpkin_core::propagation::InferenceCheckers; + use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::NotificationContext; use pumpkin_core::propagation::OpaqueDomainEvent; @@ -21,6 +21,7 @@ use pumpkin_core::propagation::PropagationContext; use pumpkin_core::propagation::Propagator; use pumpkin_core::propagation::PropagatorConstructor; use pumpkin_core::propagation::PropagatorConstructorContext; +use pumpkin_core::propagation::RuntimeCheckers; use pumpkin_core::state::PropagationStatusCP; use pumpkin_core::state::propagator_conflict; use pumpkin_core::variables::IntegerVariable; @@ -111,29 +112,10 @@ impl PropagatorConstruc { type PropagatorImpl = Self; - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.add_inference_checker( - InferenceCode::new(self.constraint_tag, TimeTable), - Box::new(TimeTableChecker { - tasks: self - .parameters - .tasks - .iter() - .map(|task| CheckerTask { - start_time: task.start_variable.clone(), - processing_time: task.processing_time, - resource_usage: task.resource_usage, - }) - .collect(), - capacity: self.parameters.capacity, - }), - ); - } - fn create( mut self, mut context: PropagatorConstructorContext, - ) -> (EventRegistration, Self::PropagatorImpl) { + ) -> ConstructedPropagator { // We only register for notifications of backtrack events if incremental backtracking is // enabled let registration = register_tasks( @@ -148,9 +130,32 @@ impl PropagatorConstruc self.is_time_table_outdated = true; - self.inference_code = Some(InferenceCode::new(self.constraint_tag, TimeTable)); + let mut checkers = RuntimeCheckers::builder(); + self.inference_code = Some( + checkers.add_inference_checker( + self.constraint_tag, + TimeTable, + TimeTableChecker { + tasks: self + .parameters + .tasks + .iter() + .map(|task| CheckerTask { + start_time: task.start_variable.clone(), + processing_time: task.processing_time, + resource_usage: task.resource_usage, + }) + .collect(), + capacity: self.parameters.capacity, + }, + ), + ); - (registration, self) + ConstructedPropagator { + registration, + checkers: checkers.build(), + propagator: self, + } } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs index 2428cf98f..b3a09fb3c 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs @@ -8,11 +8,10 @@ use pumpkin_core::asserts::pumpkin_assert_extreme; use pumpkin_core::conjunction; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; -use pumpkin_core::propagation::EventRegistration; -use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::NotificationContext; use pumpkin_core::propagation::OpaqueDomainEvent; @@ -21,6 +20,7 @@ use pumpkin_core::propagation::PropagationContext; use pumpkin_core::propagation::Propagator; use pumpkin_core::propagation::PropagatorConstructor; use pumpkin_core::propagation::PropagatorConstructorContext; +use pumpkin_core::propagation::RuntimeCheckers; use pumpkin_core::state::PropagationStatusCP; use pumpkin_core::state::propagator_conflict; use pumpkin_core::variables::IntegerVariable; @@ -108,29 +108,10 @@ impl Propagator { type PropagatorImpl = Self; - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.add_inference_checker( - InferenceCode::new(self.constraint_tag, TimeTable), - Box::new(TimeTableChecker { - tasks: self - .parameters - .tasks - .iter() - .map(|task| CheckerTask { - start_time: task.start_variable.clone(), - processing_time: task.processing_time, - resource_usage: task.resource_usage, - }) - .collect(), - capacity: self.parameters.capacity, - }), - ); - } - fn create( mut self, mut context: PropagatorConstructorContext, - ) -> (EventRegistration, Self::PropagatorImpl) { + ) -> ConstructedPropagator { let registration = register_tasks(&self.parameters.tasks, context.reborrow(), true); self.updatable_structures .reset_all_bounds_and_remove_fixed(context.domains(), &self.parameters); @@ -138,9 +119,32 @@ impl Propagator // Then we do normal propagation self.is_time_table_outdated = true; - self.inference_code = Some(InferenceCode::new(self.constraint_tag, TimeTable)); + let mut checkers = RuntimeCheckers::builder(); + self.inference_code = Some( + checkers.add_inference_checker( + self.constraint_tag, + TimeTable, + TimeTableChecker { + tasks: self + .parameters + .tasks + .iter() + .map(|task| CheckerTask { + start_time: task.start_variable.clone(), + processing_time: task.processing_time, + resource_usage: task.resource_usage, + }) + .collect(), + capacity: self.parameters.capacity, + }, + ), + ); - (registration, self) + ConstructedPropagator { + registration, + checkers: checkers.build(), + propagator: self, + } } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs index cf0792903..602c6b03c 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs @@ -6,11 +6,10 @@ use pumpkin_core::asserts::pumpkin_assert_simple; use pumpkin_core::conjunction; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; -use pumpkin_core::propagation::EventRegistration; -use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::NotificationContext; use pumpkin_core::propagation::OpaqueDomainEvent; @@ -20,6 +19,7 @@ use pumpkin_core::propagation::Propagator; use pumpkin_core::propagation::PropagatorConstructor; use pumpkin_core::propagation::PropagatorConstructorContext; use pumpkin_core::propagation::ReadDomains; +use pumpkin_core::propagation::RuntimeCheckers; use pumpkin_core::state::PropagationStatusCP; use pumpkin_core::state::PropagatorConflict; use pumpkin_core::state::propagator_conflict; @@ -111,36 +111,40 @@ impl PropagatorConstructor { type PropagatorImpl = Self; - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.add_inference_checker( - InferenceCode::new(self.constraint_tag, TimeTable), - Box::new(TimeTableChecker { - tasks: self - .parameters - .tasks - .iter() - .map(|task| CheckerTask { - start_time: task.start_variable.clone(), - processing_time: task.processing_time, - resource_usage: task.resource_usage, - }) - .collect(), - capacity: self.parameters.capacity, - }), - ); - } - fn create( mut self, mut context: PropagatorConstructorContext, - ) -> (EventRegistration, Self::PropagatorImpl) { + ) -> ConstructedPropagator { self.updatable_structures .initialise_bounds_and_remove_fixed(context.domains(), &self.parameters); let registration = register_tasks(&self.parameters.tasks, context.reborrow(), false); - self.inference_code = Some(InferenceCode::new(self.constraint_tag, TimeTable)); + let mut checkers = RuntimeCheckers::builder(); + self.inference_code = Some( + checkers.add_inference_checker( + self.constraint_tag, + TimeTable, + TimeTableChecker { + tasks: self + .parameters + .tasks + .iter() + .map(|task| CheckerTask { + start_time: task.start_variable.clone(), + processing_time: task.processing_time, + resource_usage: task.resource_usage, + }) + .collect(), + capacity: self.parameters.capacity, + }, + ), + ); - (registration, self) + ConstructedPropagator { + registration, + checkers: checkers.build(), + propagator: self, + } } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs index 471f2e26e..16723f457 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs @@ -9,10 +9,9 @@ use pumpkin_core::asserts::pumpkin_assert_extreme; use pumpkin_core::conjunction; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::EnqueueDecision; -use pumpkin_core::propagation::EventRegistration; -use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::NotificationContext; use pumpkin_core::propagation::OpaqueDomainEvent; @@ -22,6 +21,7 @@ use pumpkin_core::propagation::Propagator; use pumpkin_core::propagation::PropagatorConstructor; use pumpkin_core::propagation::PropagatorConstructorContext; use pumpkin_core::propagation::ReadDomains; +use pumpkin_core::propagation::RuntimeCheckers; use pumpkin_core::state::PropagationStatusCP; use pumpkin_core::state::PropagatorConflict; use pumpkin_core::state::propagator_conflict; @@ -101,36 +101,40 @@ impl TimeTablePerPointPropagator { impl PropagatorConstructor for TimeTablePerPointPropagator { type PropagatorImpl = Self; - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.add_inference_checker( - InferenceCode::new(self.constraint_tag, TimeTable), - Box::new(TimeTableChecker { - tasks: self - .parameters - .tasks - .iter() - .map(|task| CheckerTask { - start_time: task.start_variable.clone(), - processing_time: task.processing_time, - resource_usage: task.resource_usage, - }) - .collect(), - capacity: self.parameters.capacity, - }), - ); - } - fn create( mut self, mut context: PropagatorConstructorContext, - ) -> (EventRegistration, Self::PropagatorImpl) { + ) -> ConstructedPropagator { self.updatable_structures .initialise_bounds_and_remove_fixed(context.domains(), &self.parameters); let registration = register_tasks(&self.parameters.tasks, context.reborrow(), false); - self.inference_code = Some(InferenceCode::new(self.constraint_tag, TimeTable)); + let mut checkers = RuntimeCheckers::builder(); + self.inference_code = Some( + checkers.add_inference_checker( + self.constraint_tag, + TimeTable, + TimeTableChecker { + tasks: self + .parameters + .tasks + .iter() + .map(|task| CheckerTask { + start_time: task.start_variable.clone(), + processing_time: task.processing_time, + resource_usage: task.resource_usage, + }) + .collect(), + capacity: self.parameters.capacity, + }, + ), + ); - (registration, self) + ConstructedPropagator { + registration, + checkers: checkers.build(), + propagator: self, + } } } diff --git a/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs b/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs index 4341ab54b..220b67256 100644 --- a/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs +++ b/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs @@ -7,15 +7,16 @@ use pumpkin_core::predicate; use pumpkin_core::predicates::PropositionalConjunction; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::EventRegistration; -use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::PropagationContext; use pumpkin_core::propagation::Propagator; use pumpkin_core::propagation::PropagatorConstructor; use pumpkin_core::propagation::PropagatorConstructorContext; use pumpkin_core::propagation::ReadDomains; +use pumpkin_core::propagation::RuntimeCheckers; use pumpkin_core::state::PropagationStatusCP; use pumpkin_core::state::propagator_conflict; use pumpkin_core::variables::IntegerVariable; @@ -80,7 +81,10 @@ impl DisjunctiveConstructor { impl PropagatorConstructor for DisjunctiveConstructor { type PropagatorImpl = DisjunctivePropagator; - fn create(self, _: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { + fn create( + self, + _: PropagatorConstructorContext, + ) -> ConstructedPropagator { let tasks = self .tasks .into_iter() @@ -93,13 +97,26 @@ impl PropagatorConstructor for DisjunctiveConstr .collect::>(); let theta_lambda_tree = ThetaLambdaTree::new(&tasks); - let inference_code = InferenceCode::new(self.constraint_tag, DisjunctiveEdgeFinding); - let mut registration = EventRegistration::builder(); for task in tasks.iter() { registration = registration.add(&task.start_time, DomainEvents::BOUNDS, task.id); } + let mut checkers = RuntimeCheckers::builder(); + let inference_code = checkers.add_inference_checker( + self.constraint_tag, + DisjunctiveEdgeFinding, + DisjunctiveEdgeFindingChecker { + tasks: tasks + .iter() + .map(|task| ArgDisjunctiveTask { + start_time: task.start_time.clone(), + processing_time: task.processing_time, + }) + .collect(), + }, + ); + let propagator = DisjunctivePropagator { tasks: tasks.clone().into_boxed_slice(), sorted_tasks: tasks, @@ -108,23 +125,11 @@ impl PropagatorConstructor for DisjunctiveConstr inference_code, }; - (registration.build(), propagator) - } - - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.add_inference_checker( - InferenceCode::new(self.constraint_tag, DisjunctiveEdgeFinding), - Box::new(DisjunctiveEdgeFindingChecker { - tasks: self - .tasks - .iter() - .map(|task| ArgDisjunctiveTask { - start_time: task.start_time.clone(), - processing_time: task.processing_time, - }) - .collect(), - }), - ); + ConstructedPropagator { + registration: registration.build(), + checkers: checkers.build(), + propagator, + } } } diff --git a/pumpkin-crates/propagators/src/propagators/element.rs b/pumpkin-crates/propagators/src/propagators/element.rs index 09f1b9271..3da49cbd3 100644 --- a/pumpkin-crates/propagators/src/propagators/element.rs +++ b/pumpkin-crates/propagators/src/propagators/element.rs @@ -16,10 +16,10 @@ use pumpkin_core::predicate; use pumpkin_core::predicates::Predicate; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::ExplanationContext; -use pumpkin_core::propagation::InferenceCheckers; use pumpkin_core::propagation::LazyExplanation; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; @@ -28,6 +28,7 @@ use pumpkin_core::propagation::Propagator; use pumpkin_core::propagation::PropagatorConstructor; use pumpkin_core::propagation::PropagatorConstructorContext; use pumpkin_core::propagation::ReadDomains; +use pumpkin_core::propagation::RuntimeCheckers; use pumpkin_core::state::PropagationStatusCP; use pumpkin_core::variables::IntegerVariable; use pumpkin_core::variables::Reason; @@ -50,18 +51,10 @@ where { type PropagatorImpl = ElementPropagator; - fn add_inference_checkers(&self, mut checkers: InferenceCheckers<'_>) { - checkers.add_inference_checker( - InferenceCode::new(self.constraint_tag, Element), - Box::new(ElementChecker::new( - self.array.clone(), - self.index.clone(), - self.rhs.clone(), - )), - ); - } - - fn create(self, _: PropagatorConstructorContext) -> (EventRegistration, Self::PropagatorImpl) { + fn create( + self, + _: PropagatorConstructorContext, + ) -> ConstructedPropagator { let ElementArgs { array, index, @@ -81,7 +74,12 @@ where registration = registration.add(&index, DomainEvents::ANY_INT, ID_INDEX); registration = registration.add(&rhs, DomainEvents::ANY_INT, ID_RHS); - let inference_code = InferenceCode::new(constraint_tag, Element); + let mut checkers = RuntimeCheckers::builder(); + let inference_code = checkers.add_inference_checker( + constraint_tag, + Element, + ElementChecker::new(array.clone(), index.clone(), rhs.clone()), + ); let propagator = ElementPropagator { array, @@ -91,7 +89,11 @@ where rhs_reason_buffer: vec![], }; - (registration.build(), propagator) + ConstructedPropagator { + registration: registration.build(), + checkers: checkers.build(), + propagator, + } } } diff --git a/pumpkin-proof-processor/src/deduction_propagator.rs b/pumpkin-proof-processor/src/deduction_propagator.rs index 80c5bc33c..577651176 100644 --- a/pumpkin-proof-processor/src/deduction_propagator.rs +++ b/pumpkin-proof-processor/src/deduction_propagator.rs @@ -2,6 +2,7 @@ use pumpkin_core::declare_inference_label; use pumpkin_core::predicates::PropositionalConjunction; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; +use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::EventRegistration; use pumpkin_core::propagation::PredicateId; use pumpkin_core::propagation::PropagationContext; @@ -9,6 +10,7 @@ use pumpkin_core::propagation::Propagator; use pumpkin_core::propagation::PropagatorConstructor; use pumpkin_core::propagation::PropagatorConstructorContext; use pumpkin_core::propagation::ReadDomains; +use pumpkin_core::propagation::RuntimeCheckers; use pumpkin_core::state::Conflict; use pumpkin_core::state::PropagationStatusCP; use pumpkin_core::state::PropagatorConflict; @@ -28,7 +30,7 @@ impl PropagatorConstructor for DeductionPropagatorConstructor { fn create( self, mut context: PropagatorConstructorContext, - ) -> (EventRegistration, Self::PropagatorImpl) { + ) -> ConstructedPropagator { declare_inference_label!(Nogood); let DeductionPropagatorConstructor { @@ -48,7 +50,11 @@ impl PropagatorConstructor for DeductionPropagatorConstructor { active: true, }; - (EventRegistration::empty(), propagator) + ConstructedPropagator { + registration: EventRegistration::empty(), + checkers: RuntimeCheckers::empty(), + propagator, + } } } From b56571525499833b7efa815a4cbb7e61357228a8 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Thu, 28 May 2026 20:20:10 +1000 Subject: [PATCH 09/12] Squashed commit of the following: commit 9a7d21bdd3b744f9f6e8a731f9f436e7dd6b745c Author: Maarten Flippo Date: Thu May 28 19:57:48 2026 +1000 Clarify documentation commit 6839f510eec9b6255653d9c424cd2ab22dc89b5e Author: Maarten Flippo Date: Thu May 28 19:36:36 2026 +1000 Rename 'EventRegistration' to 'EventsToRegister' --- .../core/src/propagation/constructor.rs | 6 +- .../src/propagation/event_registration.rs | 86 +++++++++++++------ .../hypercube_linear/propagator.rs | 4 +- .../propagators/nogoods/nogood_propagator.rs | 4 +- .../src/propagators/reified_propagator.rs | 4 +- .../propagators/arithmetic/absolute_value.rs | 5 +- .../arithmetic/binary/binary_equals.rs | 4 +- .../arithmetic/binary/binary_not_equals.rs | 5 +- .../arithmetic/integer_division.rs | 5 +- .../arithmetic/integer_multiplication.rs | 5 +- .../arithmetic/linear_less_or_equal.rs | 4 +- .../arithmetic/linear_not_equal.rs | 5 +- .../src/propagators/arithmetic/maximum.rs | 5 +- .../time_table_over_interval_incremental.rs | 1 - .../src/propagators/cumulative/utils/util.rs | 6 +- .../disjunctive/disjunctive_propagator.rs | 5 +- .../propagators/src/propagators/element.rs | 4 +- .../src/deduction_propagator.rs | 4 +- 18 files changed, 99 insertions(+), 63 deletions(-) diff --git a/pumpkin-crates/core/src/propagation/constructor.rs b/pumpkin-crates/core/src/propagation/constructor.rs index bc85e72ce..78fb9592b 100644 --- a/pumpkin-crates/core/src/propagation/constructor.rs +++ b/pumpkin-crates/core/src/propagation/constructor.rs @@ -18,8 +18,8 @@ use crate::predicates::Predicate; #[cfg(doc)] use crate::propagation::DomainEvent; use crate::propagation::DomainEvents; -use crate::propagation::EventRegistration; -use crate::propagation::runtime_checkers::RuntimeCheckers; +use crate::propagation::EventsToRegister; +use crate::propagation::RuntimeCheckers; use crate::variables::IntegerVariable; /// A propagator constructor creates a fully initialized instance of a [`Propagator`]. @@ -43,7 +43,7 @@ pub trait PropagatorConstructor { #[derive(Clone, Debug)] pub struct ConstructedPropagator

{ /// The domain events the propagator needs to be be registered for. - pub registration: EventRegistration, + pub registration: EventsToRegister, /// Any runtime checkers that verify the propagator's implementation. pub checkers: RuntimeCheckers, /// The propagator diff --git a/pumpkin-crates/core/src/propagation/event_registration.rs b/pumpkin-crates/core/src/propagation/event_registration.rs index 665b92ed5..143446bbc 100644 --- a/pumpkin-crates/core/src/propagation/event_registration.rs +++ b/pumpkin-crates/core/src/propagation/event_registration.rs @@ -16,51 +16,65 @@ pub trait EventTarget { ); } +/// The interface to a component that needs to know which variables care about which events. pub trait EventDispatcher { + // This is a separate trait to isolate the event registration from the rest of the solver. + // That isolation is beneficial when writing tests, as individual components can easily be + // mocked without needing to set up an entire state/solver. + /// Register the [`DomainId`] with the given [`LocalId`] on the given [`DomainEvents`]. + /// + /// It is possible to register the same [`DomainId`] with different [`LocalId`]s. This may + /// happen when the propagator uses multiple local IDs for different events for the same + /// variable. Or when different views over the same domain are used in a propagator. fn register(&mut self, domain_id: DomainId, events: EnumSet, local_id: LocalId); } /// Contains all the events and domains that a propagator needs to be enqueued for. #[derive(Clone, Debug)] -pub struct EventRegistration(Vec<(DomainId, EnumSet, LocalId)>); +pub struct EventsToRegister(Vec<(DomainId, EnumSet, LocalId)>); -impl EventRegistration { - /// Create an [`EventRegistration`] without any variables. +impl EventsToRegister { + /// Create an [`EventsToRegister`] without any variables. /// /// This is the uncommon case. Without registering for variable events, a propagator will never - /// be enqueued. - pub fn empty() -> EventRegistration { - EventRegistration(vec![]) + /// be enqueued automatically. However, certain propagators like, e.g., compound propagators, + /// may not be able to register during construction in which case they will be enqueued + /// explicitly when a constraint is added to them. The nogood propagator is an example of such a + /// propagator. + pub fn empty() -> EventsToRegister { + EventsToRegister(vec![]) } - /// Create a new [`EventRegistrationBuilder`]. + /// Create a new [`EventsToRegisterBuilder`]. /// - /// If no event registrations will be made, use [`EventRegistration::empty`] instead. - /// Calling [`EventRegistrationBuilder::build`] without any registrations will cause a panic. + /// If no event registrations will be made, use [`EventsToRegister::empty`] instead. + /// Calling [`EventsToRegisterBuilder::build`] without any registrations will cause a panic. + pub fn builder() -> EventsToRegisterBuilder { + EventsToRegisterBuilder { + registrations: EventsToRegister(vec![]), + } + } + + /// Add a new event registration to an existing instance of self. /// /// # Example /// /// ``` /// use pumpkin_core::propagation::DomainEvents; - /// use pumpkin_core::propagation::EventRegistration; + /// use pumpkin_core::propagation::EventsToRegister; /// use pumpkin_core::propagation::LocalId; /// use pumpkin_core::variables::DomainId; /// /// let v1 = DomainId::new(0); /// let v2 = DomainId::new(0); - /// let registration = EventRegistration::builder() + /// let mut registration = EventsToRegister::builder() /// .add(&v1, DomainEvents::ANY_INT, LocalId::from(0)) - /// .add(&v2, DomainEvents::ANY_INT, LocalId::from(1)) /// .build(); + /// + /// // Extend the events to register with another variable. + /// registration.add(&v2, DomainEvents::ANY_INT, LocalId::from(1)); /// ``` - pub fn builder() -> EventRegistrationBuilder { - EventRegistrationBuilder { - registrations: EventRegistration(vec![]), - } - } - - /// Add a new event registration. pub fn add( &mut self, target: &impl EventTarget, @@ -76,22 +90,38 @@ impl EventRegistration { } } -impl EventDispatcher for EventRegistration { +impl EventDispatcher for EventsToRegister { fn register(&mut self, domain_id: DomainId, events: EnumSet, local_id: LocalId) { self.0.push((domain_id, events, local_id)); } } -/// Used to construct an [`EventRegistration`] for heterogeneous [`EventTarget`] implementations. +/// Used to construct an [`EventsToRegister`] for heterogeneous [`EventTarget`] implementations. /// -/// See [`EventRegistration::builder`] for a usage example. +/// See [`EventsToRegister::builder`] for a usage example. #[derive(Clone, Debug)] -pub struct EventRegistrationBuilder { - registrations: EventRegistration, +pub struct EventsToRegisterBuilder { + registrations: EventsToRegister, } -impl EventRegistrationBuilder { +impl EventsToRegisterBuilder { /// Add a new event registration. + /// + /// # Example + /// + /// ``` + /// use pumpkin_core::propagation::DomainEvents; + /// use pumpkin_core::propagation::EventsToRegister; + /// use pumpkin_core::propagation::LocalId; + /// use pumpkin_core::variables::DomainId; + /// + /// let v1 = DomainId::new(0); + /// let v2 = DomainId::new(0); + /// let registration = EventsToRegister::builder() + /// .add(&v1, DomainEvents::ANY_INT, LocalId::from(0)) + /// .add(&v2, DomainEvents::ANY_INT, LocalId::from(1)) + /// .build(); + /// ``` pub fn add( mut self, target: &impl EventTarget, @@ -102,11 +132,11 @@ impl EventRegistrationBuilder { self } - /// Finish constructing the [`EventRegistration`]. + /// Finish constructing the [`EventsToRegister`]. /// /// If no variables are registered, then this panics. If no variables can be registered during - /// construction, use [`EventRegistration::empty`]. - pub fn build(self) -> EventRegistration { + /// construction, use [`EventsToRegister::empty`]. + pub fn build(self) -> EventsToRegister { assert!( !self.registrations.0.is_empty(), "did not register for any events" diff --git a/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs b/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs index baca99576..1e4d31cd8 100644 --- a/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs +++ b/pumpkin-crates/core/src/propagators/hypercube_linear/propagator.rs @@ -8,7 +8,7 @@ use crate::proof::ConstraintTag; use crate::proof::InferenceCode; use crate::propagation::ConstructedPropagator; use crate::propagation::DomainEvents; -use crate::propagation::EventRegistration; +use crate::propagation::EventsToRegister; use crate::propagation::LocalId; use crate::propagation::PropagationContext; use crate::propagation::Propagator; @@ -79,7 +79,7 @@ impl PropagatorConstructor for HypercubeLinearConstructor { }; // TODO: This will be expanded with registration of predicates. - let registration = EventRegistration::empty(); + let registration = EventsToRegister::empty(); ConstructedPropagator { registration, diff --git a/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs b/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs index 7ca0d00ec..3dc312631 100644 --- a/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs +++ b/pumpkin-crates/core/src/propagators/nogoods/nogood_propagator.rs @@ -22,7 +22,7 @@ use crate::predicate; use crate::proof::InferenceCode; use crate::propagation::ConstructedPropagator; use crate::propagation::EnqueueDecision; -use crate::propagation::EventRegistration; +use crate::propagation::EventsToRegister; use crate::propagation::ExplanationContext; use crate::propagation::LazyExplanation; use crate::propagation::NotificationContext; @@ -124,7 +124,7 @@ impl PropagatorConstructor for NogoodPropagatorConstructor { }; ConstructedPropagator { - registration: EventRegistration::empty(), + registration: EventsToRegister::empty(), checkers: RuntimeCheckers::empty(), propagator, } diff --git a/pumpkin-crates/core/src/propagators/reified_propagator.rs b/pumpkin-crates/core/src/propagators/reified_propagator.rs index 0a4ddfebd..001e4e138 100644 --- a/pumpkin-crates/core/src/propagators/reified_propagator.rs +++ b/pumpkin-crates/core/src/propagators/reified_propagator.rs @@ -303,7 +303,7 @@ mod tests { use crate::proof::ConstraintTag; use crate::proof::InferenceCode; use crate::proof::Unknown; - use crate::propagation::EventRegistration; + use crate::propagation::EventsToRegister; use crate::variables::DomainId; #[test] @@ -488,7 +488,7 @@ mod tests { self, _: PropagatorConstructorContext, ) -> ConstructedPropagator { - let mut registration = EventRegistration::empty(); + let mut registration = EventsToRegister::empty(); for (index, variable) in self.variables_to_register.iter().enumerate() { registration.add(variable, DomainEvents::ANY_INT, LocalId::from(index as u32)); diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs index b66d0851d..6b38e560b 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs @@ -9,7 +9,8 @@ use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; -use pumpkin_core::propagation::EventRegistration; +use pumpkin_core::propagation::EventsToRegister; + use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; use pumpkin_core::propagation::PropagationContext; @@ -47,7 +48,7 @@ where constraint_tag, } = self; - let registration = EventRegistration::builder() + let registration = EventsToRegister::builder() .add(&signed, DomainEvents::BOUNDS, LocalId::from(0)) .add(&absolute, DomainEvents::BOUNDS, LocalId::from(1)) .build(); diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs index 107eb31ff..2c080940f 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs @@ -21,7 +21,7 @@ use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; -use pumpkin_core::propagation::EventRegistration; +use pumpkin_core::propagation::EventsToRegister; use pumpkin_core::propagation::ExplanationContext; use pumpkin_core::propagation::LazyExplanation; use pumpkin_core::propagation::LocalId; @@ -66,7 +66,7 @@ where constraint_tag, } = self; - let registration = EventRegistration::builder() + let registration = EventsToRegister::builder() .add(&a, DomainEvents::ANY_INT, LocalId::from(0)) .add(&b, DomainEvents::ANY_INT, LocalId::from(1)) .build(); diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs index dae15494d..849e128f1 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs @@ -9,7 +9,8 @@ use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; -use pumpkin_core::propagation::EventRegistration; +use pumpkin_core::propagation::EventsToRegister; + use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; use pumpkin_core::propagation::PropagationContext; @@ -50,7 +51,7 @@ where } = self; // We only care about the case where one of the two is assigned - let registration = EventRegistration::builder() + let registration = EventsToRegister::builder() .add(&a, DomainEvents::ASSIGN, LocalId::from(0)) .add(&b, DomainEvents::ASSIGN, LocalId::from(1)) .build(); diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs index 552ac0b10..42755dd1d 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs @@ -10,7 +10,8 @@ use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; -use pumpkin_core::propagation::EventRegistration; +use pumpkin_core::propagation::EventsToRegister; + use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; use pumpkin_core::propagation::PropagationContext; @@ -61,7 +62,7 @@ where "Denominator cannot contain 0" ); - let registration = EventRegistration::builder() + let registration = EventsToRegister::builder() .add(&numerator, DomainEvents::BOUNDS, ID_NUMERATOR) .add(&denominator, DomainEvents::BOUNDS, ID_DENOMINATOR) .add(&rhs, DomainEvents::BOUNDS, ID_RHS) diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs index ba580a08a..30ff646f3 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs @@ -9,7 +9,8 @@ use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; -use pumpkin_core::propagation::EventRegistration; +use pumpkin_core::propagation::EventsToRegister; + use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; use pumpkin_core::propagation::PropagationContext; @@ -52,7 +53,7 @@ where constraint_tag, } = self; - let registration = EventRegistration::builder() + let registration = EventsToRegister::builder() .add(&a, DomainEvents::ANY_INT, ID_A) .add(&b, DomainEvents::ANY_INT, ID_B) .add(&c, DomainEvents::ANY_INT, ID_C) diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs index 9ed1def32..806848492 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs @@ -14,7 +14,7 @@ use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; -use pumpkin_core::propagation::EventRegistration; +use pumpkin_core::propagation::EventsToRegister; use pumpkin_core::propagation::ExplanationContext; use pumpkin_core::propagation::LazyExplanation; use pumpkin_core::propagation::LocalId; @@ -61,7 +61,7 @@ where let mut lower_bound_left_hand_side = 0_i64; let mut current_bounds = vec![]; - let mut registration = EventRegistration::builder(); + let mut registration = EventsToRegister::builder(); for (i, x_i) in x.iter().enumerate() { registration = registration.add(x_i, DomainEvents::LOWER_BOUND, LocalId::from(i as u32)); diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs index cc02997a6..7f847e56e 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs @@ -19,7 +19,8 @@ use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; -use pumpkin_core::propagation::EventRegistration; +use pumpkin_core::propagation::EventsToRegister; + use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::NotificationContext; use pumpkin_core::propagation::OpaqueDomainEvent; @@ -62,7 +63,7 @@ where constraint_tag, } = self; - let mut registration = EventRegistration::builder(); + let mut registration = EventsToRegister::builder(); for (i, x_i) in terms.iter().enumerate() { registration = registration.add(x_i, DomainEvents::ASSIGN, LocalId::from(i as u32)); context.register_backtrack( diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs index 046f4ed31..f9195010e 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs @@ -10,7 +10,8 @@ use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; -use pumpkin_core::propagation::EventRegistration; +use pumpkin_core::propagation::EventsToRegister; + use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; use pumpkin_core::propagation::PropagationContext; @@ -48,7 +49,7 @@ where constraint_tag, } = self; - let mut registration = EventRegistration::builder(); + let mut registration = EventsToRegister::builder(); for (idx, var) in array.iter().enumerate() { registration = registration.add(var, DomainEvents::BOUNDS, LocalId::from(idx as u32)); } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs index cf3e8229f..bb0df4aac 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs @@ -12,7 +12,6 @@ use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; - use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::NotificationContext; use pumpkin_core::propagation::OpaqueDomainEvent; diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/utils/util.rs b/pumpkin-crates/propagators/src/propagators/cumulative/utils/util.rs index 063042abf..bd0f1c031 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/utils/util.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/utils/util.rs @@ -7,7 +7,7 @@ use enumset::enum_set; use pumpkin_core::propagation::DomainEvent; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; -use pumpkin_core::propagation::EventRegistration; +use pumpkin_core::propagation::EventsToRegister; use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::PropagatorConstructorContext; use pumpkin_core::propagation::ReadDomains; @@ -53,8 +53,8 @@ pub(crate) fn register_tasks( tasks: &[Rc>], mut context: PropagatorConstructorContext<'_>, register_backtrack: bool, -) -> EventRegistration { - let mut registration = EventRegistration::builder(); +) -> EventsToRegister { + let mut registration = EventsToRegister::builder(); for task in tasks.iter() { registration = registration.add( diff --git a/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs b/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs index 220b67256..bc08c9fcd 100644 --- a/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs +++ b/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs @@ -9,7 +9,8 @@ use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; -use pumpkin_core::propagation::EventRegistration; +use pumpkin_core::propagation::EventsToRegister; + use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::PropagationContext; use pumpkin_core::propagation::Propagator; @@ -97,7 +98,7 @@ impl PropagatorConstructor for DisjunctiveConstr .collect::>(); let theta_lambda_tree = ThetaLambdaTree::new(&tasks); - let mut registration = EventRegistration::builder(); + let mut registration = EventsToRegister::builder(); for task in tasks.iter() { registration = registration.add(&task.start_time, DomainEvents::BOUNDS, task.id); } diff --git a/pumpkin-crates/propagators/src/propagators/element.rs b/pumpkin-crates/propagators/src/propagators/element.rs index 3da49cbd3..684a079dc 100644 --- a/pumpkin-crates/propagators/src/propagators/element.rs +++ b/pumpkin-crates/propagators/src/propagators/element.rs @@ -18,7 +18,7 @@ use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; -use pumpkin_core::propagation::EventRegistration; +use pumpkin_core::propagation::EventsToRegister; use pumpkin_core::propagation::ExplanationContext; use pumpkin_core::propagation::LazyExplanation; use pumpkin_core::propagation::LocalId; @@ -62,7 +62,7 @@ where constraint_tag, } = self; - let mut registration = EventRegistration::builder(); + let mut registration = EventsToRegister::builder(); for (i, x_i) in array.iter().enumerate() { registration = registration.add( x_i, diff --git a/pumpkin-proof-processor/src/deduction_propagator.rs b/pumpkin-proof-processor/src/deduction_propagator.rs index 577651176..46767d886 100644 --- a/pumpkin-proof-processor/src/deduction_propagator.rs +++ b/pumpkin-proof-processor/src/deduction_propagator.rs @@ -3,7 +3,7 @@ use pumpkin_core::predicates::PropositionalConjunction; use pumpkin_core::proof::ConstraintTag; use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::ConstructedPropagator; -use pumpkin_core::propagation::EventRegistration; +use pumpkin_core::propagation::EventsToRegister; use pumpkin_core::propagation::PredicateId; use pumpkin_core::propagation::PropagationContext; use pumpkin_core::propagation::Propagator; @@ -51,7 +51,7 @@ impl PropagatorConstructor for DeductionPropagatorConstructor { }; ConstructedPropagator { - registration: EventRegistration::empty(), + registration: EventsToRegister::empty(), checkers: RuntimeCheckers::empty(), propagator, } From 25ec3218ab0e150820776132f80f75a23fe25941 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Thu, 28 May 2026 20:24:19 +1000 Subject: [PATCH 10/12] Apply feedback --- pumpkin-crates/core/src/checkers/store.rs | 20 ++++++++------------ pumpkin-crates/core/src/engine/state.rs | 1 - 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/pumpkin-crates/core/src/checkers/store.rs b/pumpkin-crates/core/src/checkers/store.rs index c514d4643..f90c4911e 100644 --- a/pumpkin-crates/core/src/checkers/store.rs +++ b/pumpkin-crates/core/src/checkers/store.rs @@ -1,24 +1,22 @@ +//! This module facilitates runtime verification in Pumpkin. It defines common types as well as the +//! [`CheckerStore`] that owns the checkers that are active in the solver. + use pumpkin_checking::BoxedChecker; +#[cfg(doc)] +use pumpkin_checking::InferenceChecker; use crate::containers::HashMap; use crate::predicates::Predicate; use crate::proof::InferenceCode; -/// Owns the runtime checkers that are active. -pub trait StoresCheckers { - fn add_inference_checker( - &mut self, - inference_code: InferenceCode, - checker: BoxedChecker, - ); -} - +/// Owns the runtime checkers present in the solver. #[derive(Clone, Debug, Default)] pub struct CheckerStore { inference_codes: HashMap>>, } impl CheckerStore { + /// Get the [`InferenceChecker`]s for the given inference code. pub fn for_inference_code( &self, inference_code: &InferenceCode, @@ -28,10 +26,8 @@ impl CheckerStore { .map(|checkers| itertools::Either::Left(checkers.iter())) .unwrap_or(itertools::Either::Right(std::iter::empty())) } -} -impl StoresCheckers for CheckerStore { - fn add_inference_checker( + pub fn add_inference_checker( &mut self, inference_code: InferenceCode, checker: BoxedChecker, diff --git a/pumpkin-crates/core/src/engine/state.rs b/pumpkin-crates/core/src/engine/state.rs index db56275bb..1efcf1e9a 100644 --- a/pumpkin-crates/core/src/engine/state.rs +++ b/pumpkin-crates/core/src/engine/state.rs @@ -6,7 +6,6 @@ use pumpkin_checking::InferenceChecker; use pumpkin_checking::VariableState; use crate::checkers::CheckerStore; -use crate::checkers::StoresCheckers; use crate::containers::KeyGenerator; use crate::create_statistics_struct; use crate::engine::Assignments; From 529ab9bd1c2061c6bfb71fe93b68e8de77747845 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Thu, 28 May 2026 20:25:45 +1000 Subject: [PATCH 11/12] Add docs to NotificationEngineWatchers --- .../core/src/propagation/contexts/propagation_context.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pumpkin-crates/core/src/propagation/contexts/propagation_context.rs b/pumpkin-crates/core/src/propagation/contexts/propagation_context.rs index 48d21cb67..c5a632def 100644 --- a/pumpkin-crates/core/src/propagation/contexts/propagation_context.rs +++ b/pumpkin-crates/core/src/propagation/contexts/propagation_context.rs @@ -297,6 +297,9 @@ pub(crate) fn build_reason( } } +/// Wrapper around the [`NotificationEngine`] that is tied to a specific propagator. +/// +/// Implements [`EventDispatcher`] to handle registration of domain events. struct NotificationEngineWatchers<'a> { propagator_id: PropagatorId, notificaton_engine: &'a mut NotificationEngine, From aafb97cbcb78149af62bab67058bc7891805a81c Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Thu, 28 May 2026 20:29:34 +1000 Subject: [PATCH 12/12] Fix formatting --- .../propagators/src/propagators/arithmetic/absolute_value.rs | 1 - .../src/propagators/arithmetic/binary/binary_not_equals.rs | 1 - .../propagators/src/propagators/arithmetic/integer_division.rs | 1 - .../src/propagators/arithmetic/integer_multiplication.rs | 1 - .../propagators/src/propagators/arithmetic/linear_not_equal.rs | 1 - pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs | 1 - .../src/propagators/disjunctive/disjunctive_propagator.rs | 1 - 7 files changed, 7 deletions(-) diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs index 6b38e560b..face0b7c9 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs @@ -10,7 +10,6 @@ use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::EventsToRegister; - use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; use pumpkin_core::propagation::PropagationContext; diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs index 849e128f1..ce7f8864d 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs @@ -10,7 +10,6 @@ use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EventsToRegister; - use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; use pumpkin_core::propagation::PropagationContext; diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs index 42755dd1d..10aba7a22 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs @@ -11,7 +11,6 @@ use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::EventsToRegister; - use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; use pumpkin_core::propagation::PropagationContext; diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs index 30ff646f3..dbfa263de 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs @@ -10,7 +10,6 @@ use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::EventsToRegister; - use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; use pumpkin_core::propagation::PropagationContext; diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs index 7f847e56e..bf78d8700 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs @@ -20,7 +20,6 @@ use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::Domains; use pumpkin_core::propagation::EnqueueDecision; use pumpkin_core::propagation::EventsToRegister; - use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::NotificationContext; use pumpkin_core::propagation::OpaqueDomainEvent; diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs index f9195010e..80e094a63 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs @@ -11,7 +11,6 @@ use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::EventsToRegister; - use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::Priority; use pumpkin_core::propagation::PropagationContext; diff --git a/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs b/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs index bc08c9fcd..0b7c39e2d 100644 --- a/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs +++ b/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs @@ -10,7 +10,6 @@ use pumpkin_core::proof::InferenceCode; use pumpkin_core::propagation::ConstructedPropagator; use pumpkin_core::propagation::DomainEvents; use pumpkin_core::propagation::EventsToRegister; - use pumpkin_core::propagation::LocalId; use pumpkin_core::propagation::PropagationContext; use pumpkin_core::propagation::Propagator;