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