1use std::fmt::Debug;
4
5use pumpkin_checking::InferenceChecker;
6
7use super::PropagatorQueue;
8use crate::containers::KeyGenerator;
9use crate::engine::EmptyDomain;
10use crate::engine::State;
11use crate::engine::predicates::predicate::Predicate;
12use crate::engine::variables::DomainId;
13use crate::engine::variables::IntegerVariable;
14use crate::engine::variables::Literal;
15use crate::options::LearningOptions;
16use crate::predicate;
17use crate::predicates::PropositionalConjunction;
18use crate::proof::ConstraintTag;
19use crate::proof::InferenceCode;
20use crate::propagation::EnqueueDecision;
21use crate::propagation::ExplanationContext;
22use crate::propagation::NotificationContext;
23use crate::propagation::PropagationContext;
24use crate::propagation::PropagatorConstructor;
25use crate::propagation::PropagatorId;
26use crate::propagators::nogoods::NogoodPropagator;
27use crate::propagators::nogoods::NogoodPropagatorConstructor;
28use crate::state::Conflict;
29use crate::state::EmptyDomainConflict;
30use crate::state::PropagatorHandle;
31
32#[derive(Debug)]
34pub struct TestSolver {
35 pub state: State,
36 constraint_tags: KeyGenerator<ConstraintTag>,
37 pub nogood_handle: PropagatorHandle<NogoodPropagator>,
38}
39
40impl Default for TestSolver {
41 fn default() -> Self {
42 let mut state = State::default();
43 let handle = state.add_propagator(NogoodPropagatorConstructor::new(
44 0,
45 LearningOptions::default(),
46 ));
47 let mut solver = Self {
48 state,
49 constraint_tags: Default::default(),
50 nogood_handle: handle,
51 };
52 solver.state.notification_engine.grow();
54 solver
55 }
56}
57
58#[deprecated = "Will be replaced by the state API"]
59impl TestSolver {
60 pub fn accept_inferences_by(&mut self, inference_code: InferenceCode) {
61 #[derive(Debug, Clone, Copy)]
62 struct Checker;
63
64 impl InferenceChecker<Predicate> for Checker {
65 fn check(
66 &self,
67 _: pumpkin_checking::VariableState<Predicate>,
68 _: &[Predicate],
69 _: Option<&Predicate>,
70 ) -> bool {
71 true
72 }
73 }
74
75 self.state
76 .add_inference_checker(inference_code, Box::new(Checker));
77 }
78
79 pub fn new_variable(&mut self, lb: i32, ub: i32) -> DomainId {
80 self.state.new_interval_variable(lb, ub, None)
81 }
82
83 pub fn new_sparse_variable(&mut self, values: Vec<i32>) -> DomainId {
84 self.state.new_sparse_variable(values, None)
85 }
86
87 pub fn new_literal(&mut self) -> Literal {
88 let domain_id = self.new_variable(0, 1);
89 Literal::new(domain_id)
90 }
91
92 pub fn new_propagator<Constructor>(
93 &mut self,
94 constructor: Constructor,
95 ) -> Result<PropagatorId, Conflict>
96 where
97 Constructor: PropagatorConstructor,
98 Constructor::PropagatorImpl: 'static,
99 {
100 let handle = self.state.add_propagator(constructor);
101 self.state
102 .propagate_to_fixed_point()
103 .map(|_| handle.propagator_id())
104 }
105
106 pub fn contains<Var: IntegerVariable>(&self, var: Var, value: i32) -> bool {
107 var.contains(&self.state.assignments, value)
108 }
109
110 pub fn lower_bound(&self, var: DomainId) -> i32 {
111 self.state.assignments.get_lower_bound(var)
112 }
113
114 pub fn remove_and_notify(
115 &mut self,
116 propagator: PropagatorId,
117 var: DomainId,
118 value: i32,
119 ) -> EnqueueDecision {
120 let result = self.state.post(predicate!(var != value));
121 assert!(
122 result.is_ok(),
123 "The provided value to `increase_lower_bound` caused an empty domain, generally the propagator should not be notified of this change!"
124 );
125 let mut propagator_queue = PropagatorQueue::new(4);
126 #[allow(deprecated, reason = "Will be refactored in the future")]
127 self.state
128 .notification_engine
129 .notify_propagators_about_domain_events_test(
130 &mut self.state.assignments,
131 &mut self.state.trailed_values,
132 &mut self.state.propagators,
133 &mut propagator_queue,
134 );
135 if propagator_queue.is_propagator_enqueued(propagator) {
136 EnqueueDecision::Enqueue
137 } else {
138 EnqueueDecision::Skip
139 }
140 }
141
142 pub fn increase_lower_bound_and_notify(
143 &mut self,
144 propagator: PropagatorId,
145 _local_id: u32,
146 var: DomainId,
147 value: i32,
148 ) -> EnqueueDecision {
149 let result = self.state.post(predicate!(var >= value));
150 assert!(
151 result.is_ok(),
152 "The provided value to `increase_lower_bound` caused an empty domain, generally the propagator should not be notified of this change!"
153 );
154 let mut propagator_queue = PropagatorQueue::new(4);
155 #[allow(deprecated, reason = "Will be refactored in the future")]
156 self.state
157 .notification_engine
158 .notify_propagators_about_domain_events_test(
159 &mut self.state.assignments,
160 &mut self.state.trailed_values,
161 &mut self.state.propagators,
162 &mut propagator_queue,
163 );
164 if propagator_queue.is_propagator_enqueued(propagator) {
165 EnqueueDecision::Enqueue
166 } else {
167 EnqueueDecision::Skip
168 }
169 }
170
171 pub fn decrease_upper_bound_and_notify(
172 &mut self,
173 propagator: PropagatorId,
174 _local_id: u32,
175 var: DomainId,
176 value: i32,
177 ) -> EnqueueDecision {
178 let result = self.state.post(predicate!(var <= value));
179 assert!(
180 result.is_ok(),
181 "The provided value to `increase_lower_bound` caused an empty domain, generally the propagator should not be notified of this change!"
182 );
183 let mut propagator_queue = PropagatorQueue::new(4);
184 #[allow(deprecated, reason = "Will be refactored in the future")]
185 self.state
186 .notification_engine
187 .notify_propagators_about_domain_events_test(
188 &mut self.state.assignments,
189 &mut self.state.trailed_values,
190 &mut self.state.propagators,
191 &mut propagator_queue,
192 );
193 if propagator_queue.is_propagator_enqueued(propagator) {
194 EnqueueDecision::Enqueue
195 } else {
196 EnqueueDecision::Skip
197 }
198 }
199
200 pub fn is_literal_false(&self, literal: Literal) -> bool {
201 self.state
202 .assignments
203 .evaluate_predicate(literal.get_true_predicate())
204 .is_some_and(|truth_value| !truth_value)
205 }
206
207 pub fn upper_bound(&self, var: DomainId) -> i32 {
208 self.state.assignments.get_upper_bound(var)
209 }
210
211 pub fn remove(&mut self, var: DomainId, value: i32) -> Result<(), EmptyDomainConflict> {
212 let _ = self.state.post(predicate!(var != value))?;
213
214 Ok(())
215 }
216
217 pub fn set_literal(&mut self, literal: Literal, truth_value: bool) -> Result<(), EmptyDomain> {
218 let _ = match truth_value {
219 true => self.state.assignments.post_predicate(
220 literal.get_true_predicate(),
221 None,
222 &mut self.state.notification_engine,
223 )?,
224 false => self.state.assignments.post_predicate(
225 (!literal).get_true_predicate(),
226 None,
227 &mut self.state.notification_engine,
228 )?,
229 };
230
231 Ok(())
232 }
233
234 pub fn propagate(&mut self, propagator: PropagatorId) -> Result<(), Conflict> {
235 let context = PropagationContext::new(
236 &mut self.state.trailed_values,
237 &mut self.state.assignments,
238 &mut self.state.reason_store,
239 &mut self.state.notification_engine,
240 propagator,
241 );
242 self.state.propagators[propagator].propagate(context)
243 }
244
245 pub fn propagate_until_fixed_point(
246 &mut self,
247 propagator: PropagatorId,
248 ) -> Result<(), Conflict> {
249 let mut num_trail_entries = self.state.assignments.num_trail_entries();
250 self.notify_propagator(propagator);
251 loop {
252 {
253 let context = PropagationContext::new(
255 &mut self.state.trailed_values,
256 &mut self.state.assignments,
257 &mut self.state.reason_store,
258 &mut self.state.notification_engine,
259 propagator,
260 );
261 self.state.propagators[propagator].propagate(context)?;
262 self.notify_propagator(propagator);
263 }
264 if self.state.assignments.num_trail_entries() == num_trail_entries {
265 break;
266 }
267 num_trail_entries = self.state.assignments.num_trail_entries();
268 }
269 Ok(())
270 }
271
272 pub fn notify_propagator(&mut self, _propagator: PropagatorId) {
273 #[allow(deprecated, reason = "Will be refactored in the future")]
274 self.state
275 .notification_engine
276 .notify_propagators_about_domain_events_test(
277 &mut self.state.assignments,
278 &mut self.state.trailed_values,
279 &mut self.state.propagators,
280 &mut PropagatorQueue::new(4),
281 );
282 }
283
284 pub fn get_reason_int(&mut self, predicate: Predicate) -> PropositionalConjunction {
285 #[allow(deprecated, reason = "Will be refactored in the future")]
286 let reason_ref = self
287 .state
288 .assignments
289 .get_reason_for_predicate_brute_force(predicate);
290 let mut predicates = vec![];
291 let _ = self.state.reason_store.get_or_compute(
292 reason_ref,
293 ExplanationContext::without_working_nogood(
294 &self.state.assignments,
295 self.state
296 .assignments
297 .get_trail_position(&predicate)
298 .unwrap(),
299 &mut self.state.notification_engine,
300 ),
301 &mut self.state.propagators,
302 &mut predicates,
303 );
304
305 PropositionalConjunction::from(predicates)
306 }
307
308 pub fn get_reason_bool(
309 &mut self,
310 literal: Literal,
311 truth_value: bool,
312 ) -> PropositionalConjunction {
313 let predicate = match truth_value {
314 true => literal.get_true_predicate(),
315 false => (!literal).get_true_predicate(),
316 };
317 self.get_reason_int(predicate)
318 }
319
320 pub fn assert_bounds(&self, var: DomainId, lb: i32, ub: i32) {
321 let actual_lb = self.lower_bound(var);
322 let actual_ub = self.upper_bound(var);
323
324 assert_eq!(
325 (lb, ub),
326 (actual_lb, actual_ub),
327 "The expected bounds [{lb}..{ub}] did not match the actual bounds [{actual_lb}..{actual_ub}]"
328 );
329 }
330
331 pub fn new_constraint_tag(&mut self) -> ConstraintTag {
332 self.constraint_tags.next_key()
333 }
334
335 pub fn new_checkpoint(&mut self) {
336 self.state.new_checkpoint();
337 }
338
339 pub fn synchronise(&mut self, level: usize) {
340 let _ = self
341 .state
342 .assignments
343 .synchronise(level, &mut self.state.notification_engine);
344 self.state.notification_engine.synchronise(
345 level,
346 &self.state.assignments,
347 &mut self.state.trailed_values,
348 );
349 self.state.trailed_values.synchronise(level);
350
351 for propagator in self.state.propagators.iter_propagators_mut() {
352 let mut context =
353 NotificationContext::new(&mut self.state.trailed_values, &self.state.assignments);
354
355 propagator.synchronise(context.reborrow());
356 }
357 }
358}