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