Skip to main content

pumpkin_core/propagation/contexts/
propagation_context.rs

1use enumset::EnumSet;
2
3use crate::basic_types::PredicateId;
4use crate::engine::Assignments;
5use crate::engine::EmptyDomain;
6use crate::engine::EmptyDomainConflict;
7use crate::engine::TrailedValues;
8use crate::engine::notifications::NotificationEngine;
9use crate::engine::notifications::Watchers;
10use crate::engine::predicates::predicate::Predicate;
11use crate::engine::reason::Reason;
12use crate::engine::reason::ReasonStore;
13use crate::engine::reason::StoredReason;
14use crate::engine::variables::Literal;
15use crate::propagation::DomainEvent;
16use crate::propagation::DomainEvents;
17use crate::propagation::Domains;
18use crate::propagation::EventDispatcher;
19use crate::propagation::HasAssignments;
20use crate::propagation::LocalId;
21#[cfg(doc)]
22use crate::propagation::Propagator;
23#[cfg(doc)]
24use crate::propagation::PropagatorConstructorContext;
25use crate::propagation::PropagatorId;
26use crate::propagation::PropagatorVarId;
27#[cfg(doc)]
28use crate::propagation::ReadDomains;
29use crate::pumpkin_assert_simple;
30use crate::variables::DomainId;
31use crate::variables::IntegerVariable;
32
33/// Provided to the propagator when it is notified of a domain event.
34///
35/// Domains can be read through the implementation of [`ReadDomains`].
36///
37/// The difference with [`PropagationContext`] is that it is not possible to perform a propagation
38/// in the notify callback.
39#[derive(Debug)]
40pub struct NotificationContext<'a> {
41    pub(crate) trailed_values: &'a mut TrailedValues,
42    pub(crate) assignments: &'a Assignments,
43}
44
45impl<'a> NotificationContext<'a> {
46    pub(crate) fn new(trailed_values: &'a mut TrailedValues, assignments: &'a Assignments) -> Self {
47        Self {
48            trailed_values,
49            assignments,
50        }
51    }
52
53    /// Get the current domains.
54    pub fn domains(&mut self) -> Domains<'_> {
55        Domains::new(self.assignments, self.trailed_values)
56    }
57
58    pub fn reborrow(&mut self) -> NotificationContext<'_> {
59        NotificationContext {
60            trailed_values: self.trailed_values,
61            assignments: self.assignments,
62        }
63    }
64}
65
66impl<'a> HasAssignments for NotificationContext<'a> {
67    fn assignments(&self) -> &Assignments {
68        self.assignments
69    }
70
71    fn trailed_values(&self) -> &TrailedValues {
72        self.trailed_values
73    }
74
75    fn trailed_values_mut(&mut self) -> &mut TrailedValues {
76        self.trailed_values
77    }
78}
79
80/// Provides information about the state of the solver to a propagator.
81///
82/// Domains can be read through the implementation of [`ReadDomains`], and changes to the state can
83/// be made via [`Self::post`].
84#[derive(Debug)]
85pub struct PropagationContext<'a> {
86    pub(crate) trailed_values: &'a mut TrailedValues,
87    pub(crate) assignments: &'a mut Assignments,
88    pub(crate) reason_store: &'a mut ReasonStore,
89    pub(crate) propagator_id: PropagatorId,
90    pub(crate) notification_engine: &'a mut NotificationEngine,
91    reification_literal: Option<Literal>,
92}
93
94impl<'a> HasAssignments for PropagationContext<'a> {
95    fn assignments(&self) -> &Assignments {
96        self.assignments
97    }
98
99    fn trailed_values(&self) -> &TrailedValues {
100        self.trailed_values
101    }
102
103    fn trailed_values_mut(&mut self) -> &mut TrailedValues {
104        self.trailed_values
105    }
106}
107
108impl<'a> PropagationContext<'a> {
109    pub(crate) fn new(
110        trailed_values: &'a mut TrailedValues,
111        assignments: &'a mut Assignments,
112        reason_store: &'a mut ReasonStore,
113        notification_engine: &'a mut NotificationEngine,
114        propagator_id: PropagatorId,
115    ) -> Self {
116        PropagationContext {
117            trailed_values,
118            assignments,
119            reason_store,
120            propagator_id,
121            notification_engine,
122            reification_literal: None,
123        }
124    }
125
126    /// Register the propagator to be enqueued when the provided [`Predicate`] becomes true.
127    ///
128    /// Returns the [`PredicateId`] assigned to the provided predicate, which will be provided
129    /// to [`Propagator::notify_predicate_id_satisfied`].
130    pub fn register_predicate(&mut self, predicate: Predicate) -> PredicateId {
131        self.notification_engine.watch_predicate(
132            predicate,
133            self.propagator_id,
134            self.trailed_values,
135            self.assignments,
136        )
137    }
138
139    /// Stop being enqueued for the given predicate.
140    pub fn unregister_predicate(&mut self, predicate_id: PredicateId) {
141        self.notification_engine
142            .unwatch_predicate(predicate_id, self.propagator_id);
143    }
144
145    /// Subscribes the propagator to the given [`DomainEvents`].
146    pub fn register_domain_event(
147        &mut self,
148        var: impl IntegerVariable,
149        domain_events: DomainEvents,
150        local_id: LocalId,
151    ) {
152        var.register(
153            &mut NotificationEngineWatchers {
154                notificaton_engine: self.notification_engine,
155                propagator_id: self.propagator_id,
156            },
157            domain_events.events(),
158            local_id,
159        );
160    }
161
162    /// Stop being enqueued for events on the given integer variable.
163    pub fn unregister_domain_event(&mut self, var: impl IntegerVariable, local_id: LocalId) {
164        let propagator_var = PropagatorVarId {
165            propagator: self.propagator_id,
166            variable: local_id,
167        };
168
169        let mut watchers = Watchers::new(propagator_var, self.notification_engine);
170        var.unwatch_all(&mut watchers);
171    }
172
173    /// Get the [`Predicate`] for a given [`PredicateId`].
174    pub fn get_predicate(&mut self, predicate_id: PredicateId) -> Predicate {
175        self.notification_engine.get_predicate(predicate_id)
176    }
177
178    /// Get a [`PredicateId`] for the given [`Predicate`].
179    ///
180    /// If no ID exists, one will be created.
181    pub fn get_id(&mut self, predicate: Predicate) -> PredicateId {
182        self.notification_engine.get_id(predicate)
183    }
184
185    /// Apply a reification literal to all the explanations that are passed to the context.
186    pub(crate) fn with_reification(&mut self, reification_literal: Literal) {
187        pumpkin_assert_simple!(
188            self.reification_literal.is_none(),
189            "cannot reify an already reified propagation context"
190        );
191
192        self.reification_literal = Some(reification_literal);
193    }
194
195    /// Get the current domain information.
196    pub fn domains(&mut self) -> Domains<'_> {
197        Domains::new(self.assignments, self.trailed_values)
198    }
199
200    pub(crate) fn get_checkpoint(&self) -> usize {
201        self.assignments.get_checkpoint()
202    }
203
204    /// Returns whether the [`Predicate`] corresponding to the provided [`PredicateId`] is
205    /// satisfied.
206    pub(crate) fn is_predicate_id_falsified(&mut self, predicate_id: PredicateId) -> bool {
207        self.notification_engine
208            .is_predicate_id_falsified(predicate_id, self.assignments)
209    }
210
211    /// Returns whether the [`Predicate`] corresponding to the provided [`PredicateId`] is
212    /// satisfied.
213    pub(crate) fn is_predicate_id_satisfied(&mut self, predicate_id: PredicateId) -> bool {
214        self.notification_engine
215            .is_predicate_id_satisfied(predicate_id, self.assignments)
216    }
217
218    pub(crate) fn evaluate_predicate_id(&mut self, predicate_id: PredicateId) -> Option<bool> {
219        self.notification_engine
220            .evaluate_predicate_id(predicate_id, self.assignments)
221    }
222
223    /// Returns the number of [`PredicateId`]s.
224    pub(crate) fn num_predicate_ids(&self) -> usize {
225        self.notification_engine.num_predicate_ids()
226    }
227
228    pub fn reborrow(&mut self) -> PropagationContext<'_> {
229        PropagationContext {
230            trailed_values: self.trailed_values,
231            assignments: self.assignments,
232            reason_store: self.reason_store,
233            propagator_id: self.propagator_id,
234            notification_engine: self.notification_engine,
235            reification_literal: self.reification_literal,
236        }
237    }
238}
239
240impl PropagationContext<'_> {
241    /// Assign the truth-value of the given [`Predicate`] to `true` in the current partial
242    /// assignment.
243    ///
244    /// If the truth-value is already `true`, then this is a no-op. Alternatively, if the
245    /// truth-value is `false`, then a conflict is triggered and the [`EmptyDomain`] error is
246    /// returned. At that point, no-more propagation should happen.
247    pub fn post(
248        &mut self,
249        predicate: Predicate,
250        reason: impl Into<Reason>,
251    ) -> Result<(), EmptyDomainConflict> {
252        let slot = self.reason_store.new_slot();
253
254        let modification_result = self.assignments.post_predicate(
255            predicate,
256            Some(slot.reason_ref()),
257            self.notification_engine,
258        );
259
260        match modification_result {
261            Ok(false) => Ok(()),
262            Ok(true) => {
263                let _ = slot.populate(
264                    self.propagator_id,
265                    build_reason(reason, self.reification_literal),
266                );
267                Ok(())
268            }
269            Err(EmptyDomain) => {
270                let _ = slot.populate(
271                    self.propagator_id,
272                    build_reason(reason, self.reification_literal),
273                );
274                let (trigger_predicate, trigger_reason) =
275                    self.assignments.remove_last_trail_element();
276
277                Err(EmptyDomainConflict {
278                    trigger_predicate,
279                    trigger_reason: Some(trigger_reason),
280                })
281            }
282        }
283    }
284}
285
286pub(crate) fn build_reason(
287    reason: impl Into<Reason>,
288    reification_literal: Option<Literal>,
289) -> StoredReason {
290    match reason.into() {
291        Reason::Eager(mut conjunction, inference_code) => {
292            conjunction.extend(
293                reification_literal
294                    .iter()
295                    .map(|lit| lit.get_true_predicate()),
296            );
297            StoredReason::Eager(conjunction, inference_code)
298        }
299        Reason::DynamicLazy(code) => StoredReason::DynamicLazy(code),
300    }
301}
302
303/// Wrapper around the [`NotificationEngine`] that is tied to a specific propagator.
304///
305/// Implements [`EventDispatcher`] to handle registration of domain events.
306struct NotificationEngineWatchers<'a> {
307    propagator_id: PropagatorId,
308    notificaton_engine: &'a mut NotificationEngine,
309}
310
311impl EventDispatcher for NotificationEngineWatchers<'_> {
312    fn register(&mut self, domain_id: DomainId, events: EnumSet<DomainEvent>, local_id: LocalId) {
313        self.notificaton_engine.register(
314            domain_id,
315            events,
316            PropagatorVarId {
317                propagator: self.propagator_id,
318                variable: local_id,
319            },
320        );
321    }
322}