Skip to main content

pumpkin_core/propagation/contexts/
propagation_context.rs

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