Skip to content

Commit 0f63882

Browse files
fix(pumpkin-core): Allow propagators to register for predicates becoming true (#331)
Predicate notification only happened for the `NogoodPropagator`. Now that we are moving toward a more flexible API, we need arbitrary propagators to be able to register for predicates becoming true. This patch does _not_ remove the special status of the `NogoodPropagator`, it merely enables other propagators to register for specific events.
1 parent 301551a commit 0f63882

File tree

6 files changed

+59
-56
lines changed

6 files changed

+59
-56
lines changed

pumpkin-crates/core/src/containers/keyed_vec.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ impl<Key, Value> KeyedVec<Key, Value> {
4242
}
4343

4444
impl<Key: StorageKey, Value> KeyedVec<Key, Value> {
45+
pub(crate) fn get(&self, key: Key) -> Option<&Value> {
46+
self.elements.get(key.index())
47+
}
48+
4549
pub(crate) fn len(&self) -> usize {
4650
self.elements.len()
4751
}
@@ -117,6 +121,12 @@ impl<Key: StorageKey, Value> KeyedVec<Key, Value> {
117121
}
118122

119123
impl<Key: StorageKey, Value: Clone> KeyedVec<Key, Value> {
124+
pub(crate) fn accomodate(&mut self, key: Key, default_value: Value) {
125+
if key.index() >= self.elements.len() {
126+
self.elements.resize(key.index() + 1, default_value)
127+
}
128+
}
129+
120130
pub(crate) fn resize(&mut self, new_len: usize, value: Value) {
121131
self.elements.resize(new_len, value)
122132
}

pumpkin-crates/core/src/engine/cp/propagation/constructor.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ use super::PropagationContext;
66
use super::Propagator;
77
use super::PropagatorId;
88
use super::PropagatorVarId;
9+
use crate::basic_types::PredicateId;
910
use crate::engine::Assignments;
1011
use crate::engine::DomainEvents;
1112
use crate::engine::State;
1213
use crate::engine::TrailedValues;
1314
use crate::engine::notifications::Watchers;
15+
use crate::predicates::Predicate;
1416
use crate::proof::ConstraintTag;
1517
use crate::proof::InferenceCode;
1618
use crate::proof::InferenceLabel;
@@ -91,6 +93,15 @@ impl PropagatorConstructorContext<'_> {
9193
var.watch_all(&mut watchers, domain_events.get_int_events());
9294
}
9395

96+
/// Register the propagator to be enqueued when the given [`Predicate`] becomes true.
97+
/// Returns the [`PredicateId`] used by the solver to track the predicate.
98+
#[allow(unused, reason = "will become public API")]
99+
pub(crate) fn register_predicate(&mut self, predicate: Predicate) -> PredicateId {
100+
self.state
101+
.notification_engine
102+
.watch_predicate(predicate, self.propagator_id)
103+
}
104+
94105
/// Subscribes the propagator to the given [`DomainEvents`] when they are undone during
95106
/// backtracking. This method is complementary to [`PropagatorConstructorContext::register`],
96107
/// the [`LocalId`]s provided to both of these method should be the same for the same variable.

pumpkin-crates/core/src/engine/cp/propagation/propagator.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,6 @@ pub(crate) trait Propagator: Downcast + DynClone {
128128
/// By default, the propagator does nothing when this method is called.
129129
fn notify_predicate_id_satisfied(&mut self, _predicate_id: PredicateId) {}
130130

131-
/// Called when a [`PredicateId`] has been falsified.
132-
///
133-
/// By default, the propagator does nothing when this method is called.
134-
#[allow(dead_code, reason = "Will be part of the public API")]
135-
fn notify_predicate_id_falsified(&mut self, _predicate_id: PredicateId) {}
136-
137131
/// Called each time the [`ConstraintSatisfactionSolver`] backtracks, the propagator can then
138132
/// update its internal data structures given the new variable domains.
139133
///

pumpkin-crates/core/src/engine/notifications/mod.rs

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ pub(crate) use predicate_notification::PredicateNotifier;
1414
use super::propagation::PropagationContext;
1515
use super::propagation::PropagatorVarId;
1616
use crate::basic_types::PredicateId;
17+
use crate::containers::KeyedVec;
1718
use crate::engine::Assignments;
1819
use crate::engine::PropagatorQueue;
1920
use crate::engine::TrailedValues;
2021
use crate::engine::propagation::EnqueueDecision;
2122
use crate::engine::propagation::LocalId;
22-
use crate::engine::propagation::Propagator;
2323
use crate::engine::propagation::PropagatorId;
2424
use crate::engine::propagation::contexts::PropagationContextWithTrailedValues;
2525
use crate::engine::propagation::store::PropagatorStore;
@@ -39,6 +39,8 @@ pub(crate) struct NotificationEngine {
3939
/// Contains information on which propagator to notify upon
4040
/// integer events, e.g., lower or upper bound change of a variable.
4141
watch_list_domain_events: WatchListDomainEvents,
42+
/// The watch list from predicates to propagators.
43+
watch_list_predicate_id: KeyedVec<PredicateId, Vec<PropagatorId>>,
4244
/// Events which have occurred since the last round of notifications have taken place
4345
events: EventSink,
4446
/// Backtrack events which have occurred since the last of backtrack notifications have taken
@@ -51,6 +53,7 @@ impl Default for NotificationEngine {
5153
fn default() -> Self {
5254
let mut result = Self {
5355
watch_list_domain_events: Default::default(),
56+
watch_list_predicate_id: Default::default(),
5457
predicate_notifier: Default::default(),
5558
last_notified_trail_index: 0,
5659
events: Default::default(),
@@ -73,6 +76,7 @@ impl Default for NotificationEngine {
7376

7477
let mut result = Self {
7578
watch_list_domain_events,
79+
watch_list_predicate_id: Default::default(),
7680
predicate_notifier: Default::default(),
7781
last_notified_trail_index: usize::MAX,
7882
events: Default::default(),
@@ -134,6 +138,20 @@ impl NotificationEngine {
134138
}
135139
}
136140

141+
pub(crate) fn watch_predicate(
142+
&mut self,
143+
predicate: Predicate,
144+
propagator_id: PropagatorId,
145+
) -> PredicateId {
146+
let predicate_id = self.get_id(predicate);
147+
148+
self.watch_list_predicate_id
149+
.accomodate(predicate_id, vec![]);
150+
self.watch_list_predicate_id[predicate_id].push(propagator_id);
151+
152+
predicate_id
153+
}
154+
137155
pub(crate) fn watch_all_backtrack(
138156
&mut self,
139157
domain: DomainId,
@@ -292,14 +310,7 @@ impl NotificationEngine {
292310
self.events = events;
293311

294312
// Then we notify the propagators that a predicate has been satisfied.
295-
//
296-
// Currently, only the nogood propagator is notified.
297-
let nogood_propagator = propagators
298-
.get_propagator_mut(nogood_propagator_handle)
299-
.expect("nogood propagator handle refers to a nogood propagator");
300-
self.notify_predicate_id_satisfied(nogood_propagator);
301-
// At the moment this does nothing yet, but we call it to drain predicates.
302-
self.notify_predicate_id_falsified();
313+
self.notify_predicate_id_satisfied(nogood_propagator_handle, propagators);
303314

304315
self.last_notified_trail_index = assignments.num_trail_entries();
305316
}
@@ -334,19 +345,23 @@ impl NotificationEngine {
334345
true
335346
}
336347

337-
/// Notifies propagators that certain [`Predicate`]s have been falsified.
338-
///
339-
/// Currently, no propagators are informed of this information.
340-
fn notify_predicate_id_falsified(&mut self) {
341-
for _predicate_id in self.predicate_notifier.drain_falsified_predicates() {
342-
// At the moment this does nothing
343-
}
344-
}
345-
346348
/// Notifies the propagator that certain [`Predicate`]s have been satisfied.
347-
fn notify_predicate_id_satisfied(&mut self, propagator: &mut dyn Propagator) {
349+
fn notify_predicate_id_satisfied(
350+
&mut self,
351+
nogood_propagator_handle: PropagatorHandle<NogoodPropagator>,
352+
propagators: &mut PropagatorStore,
353+
) {
348354
for predicate_id in self.predicate_notifier.drain_satisfied_predicates() {
349-
propagator.notify_predicate_id_satisfied(predicate_id);
355+
if let Some(watch_list) = self.watch_list_predicate_id.get(predicate_id) {
356+
let propagators_to_notify = watch_list.iter().copied();
357+
358+
for propagator_id in propagators_to_notify {
359+
propagators[propagator_id].notify_predicate_id_satisfied(predicate_id);
360+
}
361+
}
362+
363+
propagators[nogood_propagator_handle.propagator_id()]
364+
.notify_predicate_id_satisfied(predicate_id);
350365
}
351366
}
352367

@@ -480,14 +495,7 @@ impl NotificationEngine {
480495

481496
if let Some(handle) = nogood_propagator_handle {
482497
// Then we notify the propagators that a predicate has been satisfied.
483-
//
484-
// Currently, only the nogood propagator is notified.
485-
let nogood_propagator = propagators
486-
.get_propagator_mut(handle)
487-
.expect("nogood propagator handle refers to a nogood propagator");
488-
self.notify_predicate_id_satisfied(nogood_propagator);
489-
// At the moment this does nothing yet, but we call it to drain predicates.
490-
self.notify_predicate_id_falsified();
498+
self.notify_predicate_id_satisfied(handle, propagators);
491499
}
492500

493501
self.last_notified_trail_index = assignments.num_trail_entries();

pumpkin-crates/core/src/engine/notifications/predicate_notification/predicate_assignments.rs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,6 @@ pub(crate) struct PredicateIdAssignments {
2424
trail: Trail<PredicateId>,
2525
/// The known value for each [`Predicate`] (represented by a [`PredicateId`]).
2626
predicate_values: KeyedVec<PredicateId, PredicateValue>,
27-
/// The [`Predicate`]s which are currently known to be falsified used for notification.
28-
///
29-
/// Note that this structure does not contain _all_ of the falsified [`Predicate`]s but rather
30-
/// the one which have been falsified since the last round of notifications.
31-
falsified_predicates: Vec<PredicateId>,
3227
/// The [`Predicate`]s which are currently known to be satisfied used for notification.
3328
///
3429
/// Note that this structure does not contain _all_ of the satisfied [`Predicate`]s but rather
@@ -68,12 +63,6 @@ impl PredicateIdAssignments {
6863
self.predicate_values.keys()
6964
}
7065

71-
/// Returns the falsified predicates; note that this structure will be cleared once it is
72-
/// dropped.
73-
pub(crate) fn drain_falsified_predicates(&mut self) -> impl Iterator<Item = PredicateId> + '_ {
74-
self.falsified_predicates.drain(..)
75-
}
76-
7766
/// Returns the satisfied predicates; note that this structure will be cleared once it is
7867
/// dropped.
7968
pub(crate) fn drain_satisfied_predicates(&mut self) -> impl Iterator<Item = PredicateId> + '_ {
@@ -104,10 +93,8 @@ impl PredicateIdAssignments {
10493
if self.predicate_values[predicate_id] != value {
10594
// If it is not the same as what is already in the cache then we need to store it and
10695
// (potentially) update the predicates which should be notified
107-
match value {
108-
PredicateValue::AssignedTrue => self.satisfied_predicates.push(predicate_id),
109-
PredicateValue::AssignedFalse => self.falsified_predicates.push(predicate_id),
110-
_ => {}
96+
if value == PredicateValue::AssignedTrue {
97+
self.satisfied_predicates.push(predicate_id)
11198
}
11299
self.predicate_values[predicate_id] = value;
113100
self.trail.push(predicate_id)
@@ -179,7 +166,6 @@ impl PredicateIdAssignments {
179166
// We also need to clear the stored updated predicates; if this is not done, then it can be
180167
// the case that a predicate is erroneously said to be satisfied/falsified while it is not
181168
self.satisfied_predicates.clear();
182-
self.falsified_predicates.clear();
183169

184170
self.trail
185171
.synchronise(new_checkpoint)

pumpkin-crates/core/src/engine/notifications/predicate_notification/predicate_notifier.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,6 @@ impl PredicateNotifier {
4646
.debug_create_from_assignments(assignments, &mut self.predicate_to_id);
4747
}
4848

49-
/// Returns the falsified predicates; note that this structure will be cleared once it is
50-
/// dropped.
51-
pub(crate) fn drain_falsified_predicates(&mut self) -> impl Iterator<Item = PredicateId> + '_ {
52-
self.predicate_id_assignments.drain_falsified_predicates()
53-
}
54-
5549
/// Returns the satisfied predicates; note that this structure will be cleared once it is
5650
/// dropped.
5751
pub(crate) fn drain_satisfied_predicates(&mut self) -> impl Iterator<Item = PredicateId> + '_ {

0 commit comments

Comments
 (0)