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::propagation::ExplanationContext;
9use crate::propagation::PropagatorId;
10use crate::propagation::store::PropagatorStore;
11use crate::pumpkin_assert_simple;
12
13#[derive(Default, Debug, Clone)]
15pub(crate) struct ReasonStore {
16 trail: Trail<(PropagatorId, StoredReason)>,
17}
18
19impl ReasonStore {
20 pub(crate) fn push(&mut self, propagator: PropagatorId, reason: StoredReason) -> ReasonRef {
21 let index = self.trail.len();
22 self.trail.push((propagator, reason));
23 pumpkin_assert_simple!(
24 index < (1 << 30),
25 "ReasonRef in reason store should fit in ContraintReference, \
26 which has 30 bits available at most"
27 );
28 ReasonRef(index as u32)
29 }
30
31 pub(crate) fn new_slot(&mut self) -> Slot<'_> {
33 Slot { store: self }
34 }
35
36 pub(crate) fn get_or_compute(
39 &self,
40 reference: ReasonRef,
41 context: ExplanationContext<'_>,
42 propagators: &mut PropagatorStore,
43 destination_buffer: &mut impl Extend<Predicate>,
44 ) -> bool {
45 let Some(reason) = self.trail.get(reference.0 as usize) else {
46 return false;
47 };
48
49 reason
50 .1
51 .compute(context, reason.0, propagators, destination_buffer);
52
53 true
54 }
55
56 pub(crate) fn get_lazy_code(&self, reference: ReasonRef) -> Option<&u64> {
57 match self.trail.get(reference.0 as usize) {
58 Some(reason) => match &reason.1 {
59 StoredReason::Eager(_) => None,
60 StoredReason::DynamicLazy(code) => Some(code),
61 },
62 None => None,
63 }
64 }
65
66 pub(crate) fn new_checkpoint(&mut self) {
67 self.trail.new_checkpoint()
68 }
69
70 pub(crate) fn synchronise(&mut self, level: usize) {
71 let _ = self.trail.synchronise(level);
72 }
73
74 #[cfg(test)]
75 pub(crate) fn len(&self) -> usize {
76 self.trail.len()
77 }
78
79 pub(crate) fn get_propagator(&self, reason_ref: ReasonRef) -> PropagatorId {
81 self.trail.get(reason_ref.0 as usize).unwrap().0
82 }
83}
84
85#[derive(Default, Debug, Clone, Copy, Hash, Eq, PartialEq)]
87pub(crate) struct ReasonRef(pub(crate) u32);
88
89#[derive(Debug)]
91pub enum Reason {
92 Eager(PropositionalConjunction),
95 DynamicLazy(u64),
102}
103
104#[derive(Debug, Clone)]
106pub(crate) enum StoredReason {
107 Eager(PropositionalConjunction),
110 DynamicLazy(u64),
117}
118
119impl StoredReason {
120 pub(crate) fn compute(
122 &self,
123 context: ExplanationContext<'_>,
124 propagator_id: PropagatorId,
125 propagators: &mut PropagatorStore,
126 destination_buffer: &mut impl Extend<Predicate>,
127 ) {
128 match self {
129 StoredReason::DynamicLazy(code) => destination_buffer.extend(
133 propagators[propagator_id]
134 .lazy_explanation(*code, context)
135 .iter()
136 .copied(),
137 ),
138 StoredReason::Eager(result) => destination_buffer.extend(result.iter().copied()),
139 }
140 }
141}
142
143impl From<PropositionalConjunction> for Reason {
144 fn from(value: PropositionalConjunction) -> Self {
145 Reason::Eager(value)
146 }
147}
148
149impl From<u64> for Reason {
150 fn from(value: u64) -> Self {
151 Reason::DynamicLazy(value)
152 }
153}
154
155impl From<usize> for Reason {
156 fn from(value: usize) -> Self {
157 Reason::DynamicLazy(value as u64)
158 }
159}
160
161#[derive(Debug)]
163pub(crate) struct Slot<'a> {
164 store: &'a mut ReasonStore,
165}
166
167impl Slot<'_> {
168 pub(crate) fn reason_ref(&self) -> ReasonRef {
170 ReasonRef(self.store.trail.len() as u32)
171 }
172
173 pub(crate) fn populate(self, propagator: PropagatorId, reason: StoredReason) -> ReasonRef {
175 self.store.push(propagator, reason)
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use crate::conjunction;
183 use crate::engine::Assignments;
184 use crate::engine::notifications::NotificationEngine;
185 use crate::engine::variables::DomainId;
186
187 #[test]
188 fn computing_an_eager_reason_returns_a_reference_to_the_conjunction() {
189 let integers = Assignments::default();
190 let mut notification_engine = NotificationEngine::default();
191
192 let x = DomainId::new(0);
193 let y = DomainId::new(1);
194
195 let conjunction = conjunction!([x == 1] & [y == 2]);
196 let reason = StoredReason::Eager(conjunction.clone());
197
198 let mut out_reason = vec![];
199 reason.compute(
200 ExplanationContext::test_new(&integers, &mut notification_engine),
201 PropagatorId(0),
202 &mut PropagatorStore::default(),
203 &mut out_reason,
204 );
205
206 assert_eq!(conjunction.as_slice(), &out_reason);
207 }
208
209 #[test]
210 fn pushing_a_reason_gives_a_reason_ref_that_can_be_computed() {
211 let mut reason_store = ReasonStore::default();
212 let integers = Assignments::default();
213 let mut notification_engine = NotificationEngine::default();
214
215 let x = DomainId::new(0);
216 let y = DomainId::new(1);
217
218 let conjunction = conjunction!([x == 1] & [y == 2]);
219 let reason_ref =
220 reason_store.push(PropagatorId(0), StoredReason::Eager(conjunction.clone()));
221
222 assert_eq!(ReasonRef(0), reason_ref);
223
224 let mut out_reason = vec![];
225 let _ = reason_store.get_or_compute(
226 reason_ref,
227 ExplanationContext::test_new(&integers, &mut notification_engine),
228 &mut PropagatorStore::default(),
229 &mut out_reason,
230 );
231
232 assert_eq!(conjunction.as_slice(), &out_reason);
233 }
234}