pumpkin_core/engine/cp/
reason.rs1use std::fmt::Debug;
2
3use crate::basic_types::PropositionalConjunction;
4use crate::basic_types::Trail;
5#[cfg(doc)]
6use crate::containers::KeyedVec;
7use crate::predicates::Predicate;
8use crate::proof::InferenceCode;
9use crate::propagation::ExplanationContext;
10use crate::propagation::PropagatorId;
11use crate::propagation::store::PropagatorStore;
12use crate::pumpkin_assert_simple;
13
14#[derive(Default, Debug, Clone)]
16pub(crate) struct ReasonStore {
17 trail: Trail<(PropagatorId, StoredReason)>,
18}
19
20impl ReasonStore {
21 pub(crate) fn push(&mut self, propagator: PropagatorId, reason: StoredReason) -> ReasonRef {
22 let index = self.trail.len();
23 self.trail.push((propagator, reason));
24 pumpkin_assert_simple!(
25 index < (1 << 30),
26 "ReasonRef in reason store should fit in ContraintReference, \
27 which has 30 bits available at most"
28 );
29 ReasonRef(index as u32)
30 }
31
32 pub(crate) fn new_slot(&mut self) -> Slot<'_> {
34 Slot { store: self }
35 }
36
37 pub(crate) fn get_or_compute(
43 &self,
44 reference: ReasonRef,
45 context: ExplanationContext<'_>,
46 propagators: &mut PropagatorStore,
47 destination_buffer: &mut impl Extend<Predicate>,
48 ) -> InferenceCode {
49 let reason = self
50 .trail
51 .get(reference.0 as usize)
52 .expect("reason reference should not be stale");
53
54 reason
55 .1
56 .compute(context, reason.0, propagators, destination_buffer)
57 }
58
59 pub(crate) fn get_lazy_code(&self, reference: ReasonRef) -> Option<&u64> {
60 match self.trail.get(reference.0 as usize) {
61 Some(reason) => match &reason.1 {
62 StoredReason::Eager(_, _) => None,
63 StoredReason::DynamicLazy(code) => Some(code),
64 },
65 None => None,
66 }
67 }
68
69 pub(crate) fn new_checkpoint(&mut self) {
70 self.trail.new_checkpoint()
71 }
72
73 pub(crate) fn synchronise(&mut self, level: usize) {
74 let _ = self.trail.synchronise(level);
75 }
76
77 #[cfg(test)]
78 pub(crate) fn len(&self) -> usize {
79 self.trail.len()
80 }
81
82 pub(crate) fn get_propagator(&self, reason_ref: ReasonRef) -> PropagatorId {
84 self.trail.get(reason_ref.0 as usize).unwrap().0
85 }
86}
87
88#[derive(Default, Debug, Clone, Copy, Hash, Eq, PartialEq)]
90pub(crate) struct ReasonRef(pub(crate) u32);
91
92#[derive(Debug)]
94pub enum Reason {
95 Eager(PropositionalConjunction, InferenceCode),
98 DynamicLazy(u64),
106}
107
108#[derive(Debug, Clone)]
110pub(crate) enum StoredReason {
111 Eager(PropositionalConjunction, InferenceCode),
114 DynamicLazy(u64),
122}
123
124impl StoredReason {
125 pub(crate) fn compute(
128 &self,
129 context: ExplanationContext<'_>,
130 propagator_id: PropagatorId,
131 propagators: &mut PropagatorStore,
132 destination_buffer: &mut impl Extend<Predicate>,
133 ) -> InferenceCode {
134 match self {
135 StoredReason::DynamicLazy(code) => {
139 let expl = propagators[propagator_id].lazy_explanation(*code, context);
140 destination_buffer.extend(expl.predicates.iter().copied());
141 expl.inference_code
142 }
143 StoredReason::Eager(result, inference_code) => {
144 destination_buffer.extend(result.iter().copied());
145 inference_code.clone()
146 }
147 }
148 }
149}
150
151impl From<(PropositionalConjunction, &InferenceCode)> for Reason {
152 fn from((conj, code): (PropositionalConjunction, &InferenceCode)) -> Self {
153 Reason::Eager(conj, code.clone())
154 }
155}
156
157impl From<u64> for Reason {
158 fn from(value: u64) -> Self {
159 Reason::DynamicLazy(value)
160 }
161}
162
163impl From<usize> for Reason {
164 fn from(value: usize) -> Self {
165 Reason::DynamicLazy(value as u64)
166 }
167}
168
169#[derive(Debug)]
171pub(crate) struct Slot<'a> {
172 store: &'a mut ReasonStore,
173}
174
175impl Slot<'_> {
176 pub(crate) fn reason_ref(&self) -> ReasonRef {
178 ReasonRef(self.store.trail.len() as u32)
179 }
180
181 pub(crate) fn populate(self, propagator: PropagatorId, reason: StoredReason) -> ReasonRef {
183 self.store.push(propagator, reason)
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use std::num::NonZero;
190
191 use super::*;
192 use crate::conjunction;
193 use crate::engine::Assignments;
194 use crate::engine::notifications::NotificationEngine;
195 use crate::engine::variables::DomainId;
196 use crate::proof::ConstraintTag;
197
198 fn dummy_inference_code() -> InferenceCode {
199 InferenceCode::unknown_label(ConstraintTag::from_non_zero(NonZero::new(1).unwrap()))
200 }
201
202 #[test]
203 fn computing_an_eager_reason_returns_a_reference_to_the_conjunction() {
204 let integers = Assignments::default();
205 let mut notification_engine = NotificationEngine::default();
206
207 let x = DomainId::new(0);
208 let y = DomainId::new(1);
209
210 let conjunction = conjunction!([x == 1] & [y == 2]);
211 let reason = StoredReason::Eager(conjunction.clone(), dummy_inference_code());
212
213 let mut out_reason = vec![];
214 let _ = reason.compute(
215 ExplanationContext::test_new(&integers, &mut notification_engine),
216 PropagatorId(0),
217 &mut PropagatorStore::default(),
218 &mut out_reason,
219 );
220
221 assert_eq!(conjunction.as_slice(), &out_reason);
222 }
223
224 #[test]
225 fn pushing_a_reason_gives_a_reason_ref_that_can_be_computed() {
226 let mut reason_store = ReasonStore::default();
227 let integers = Assignments::default();
228 let mut notification_engine = NotificationEngine::default();
229
230 let x = DomainId::new(0);
231 let y = DomainId::new(1);
232
233 let conjunction = conjunction!([x == 1] & [y == 2]);
234 let reason_ref = reason_store.push(
235 PropagatorId(0),
236 StoredReason::Eager(conjunction.clone(), dummy_inference_code()),
237 );
238
239 assert_eq!(ReasonRef(0), reason_ref);
240
241 let mut out_reason = vec![];
242 let _ = reason_store.get_or_compute(
243 reason_ref,
244 ExplanationContext::test_new(&integers, &mut notification_engine),
245 &mut PropagatorStore::default(),
246 &mut out_reason,
247 );
248
249 assert_eq!(conjunction.as_slice(), &out_reason);
250 }
251}