pumpkin_core/engine/predicates/
predicate.rs1use enumset::EnumSetType;
2use pumpkin_checking::AtomicConstraint;
3
4use crate::engine::Assignments;
5use crate::engine::variables::DomainId;
6use crate::predicate;
7use crate::propagation::DomainEvent;
8
9#[derive(Clone, PartialEq, Eq, Copy, Hash)]
24pub struct Predicate {
25 id: u32,
28 value: i32,
29}
30
31const LOWER_BOUND_CODE: u8 = PredicateType::LowerBound as u8;
32const UPPER_BOUND_CODE: u8 = PredicateType::UpperBound as u8;
33const NOT_EQUAL_CODE: u8 = PredicateType::NotEqual as u8;
34const EQUAL_CODE: u8 = PredicateType::Equal as u8;
35
36impl Predicate {
37 pub fn new(id: DomainId, predicate_type: PredicateType, value: i32) -> Self {
40 let code = predicate_type as u8;
41 let id = id.id() | (code as u32) << 30;
42 Self { id, value }
43 }
44
45 pub fn implies(&self, other: Predicate) -> bool {
58 if self.get_domain() != other.get_domain() {
59 return false;
61 }
62
63 match self.get_predicate_type() {
64 PredicateType::LowerBound => match other.get_predicate_type() {
65 PredicateType::LowerBound => {
66 self.get_right_hand_side() >= other.get_right_hand_side()
67 }
68 PredicateType::NotEqual => self.get_right_hand_side() > other.get_right_hand_side(),
69 PredicateType::UpperBound | PredicateType::Equal => false,
70 },
71 PredicateType::UpperBound => match other.get_predicate_type() {
72 PredicateType::UpperBound => {
73 self.get_right_hand_side() <= other.get_right_hand_side()
74 }
75 PredicateType::NotEqual => self.get_right_hand_side() < other.get_right_hand_side(),
76 PredicateType::LowerBound | PredicateType::Equal => false,
77 },
78 PredicateType::NotEqual => {
79 other.get_predicate_type() == PredicateType::NotEqual
80 && self.get_right_hand_side() == other.get_right_hand_side()
81 }
82 PredicateType::Equal => match other.get_predicate_type() {
83 PredicateType::LowerBound => {
84 self.get_right_hand_side() >= other.get_right_hand_side()
85 }
86 PredicateType::UpperBound => {
87 self.get_right_hand_side() <= other.get_right_hand_side()
88 }
89 PredicateType::NotEqual => {
90 self.get_right_hand_side() != other.get_right_hand_side()
91 }
92 PredicateType::Equal => self.get_right_hand_side() == other.get_right_hand_side(),
93 },
94 }
95 }
96
97 fn get_type_code(&self) -> u8 {
98 (self.id >> 30) as u8
99 }
100
101 pub fn get_predicate_type(&self) -> PredicateType {
102 (*self).into()
103 }
104
105 fn is_bound_predicate(&self) -> bool {
106 self.is_upper_bound_predicate() || self.is_lower_bound_predicate()
107 }
108}
109
110impl PartialOrd for Predicate {
111 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
112 Some(self.cmp(other))
113 }
114}
115
116impl Ord for Predicate {
117 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
119 match self.get_domain().cmp(&other.get_domain()) {
120 std::cmp::Ordering::Equal => {
121 if self.is_bound_predicate() || other.is_bound_predicate() {
122 match self.get_type_code().cmp(&other.get_type_code()) {
123 std::cmp::Ordering::Equal => {
124 self.get_right_hand_side().cmp(&other.get_right_hand_side())
125 }
126 ordering @ (std::cmp::Ordering::Less | std::cmp::Ordering::Greater) => {
127 ordering
128 }
129 }
130 } else {
131 self.get_right_hand_side().cmp(&other.get_right_hand_side())
132 }
133 }
134
135 ordering @ (std::cmp::Ordering::Less | std::cmp::Ordering::Greater) => ordering,
136 }
137 }
138}
139
140#[derive(Debug, Hash, EnumSetType)]
141#[repr(u8)]
142#[enumset(repr = "u8")]
143pub enum PredicateType {
144 LowerBound = 0,
147 NotEqual = 1,
148 Equal = 2,
149 UpperBound = 3,
150}
151
152impl From<DomainEvent> for PredicateType {
153 fn from(value: DomainEvent) -> Self {
154 match value {
155 DomainEvent::Assign => PredicateType::Equal,
156 DomainEvent::LowerBound => PredicateType::LowerBound,
157 DomainEvent::UpperBound => PredicateType::UpperBound,
158 DomainEvent::Removal => PredicateType::NotEqual,
159 }
160 }
161}
162
163impl PredicateType {
164 pub fn is_lower_bound(&self) -> bool {
165 matches!(self, PredicateType::LowerBound)
166 }
167
168 pub fn is_upper_bound(&self) -> bool {
169 matches!(self, PredicateType::UpperBound)
170 }
171
172 pub fn is_disequality(&self) -> bool {
173 matches!(self, PredicateType::NotEqual)
174 }
175
176 pub(crate) fn into_predicate(
177 self,
178 domain: DomainId,
179 assignments: &Assignments,
180 removed_value: Option<i32>,
181 ) -> Predicate {
182 match self {
183 PredicateType::LowerBound => {
184 predicate!(domain >= assignments.get_lower_bound(domain))
185 }
186 PredicateType::UpperBound => predicate!(domain <= assignments.get_upper_bound(domain)),
187 PredicateType::NotEqual => predicate!(
188 domain
189 != removed_value
190 .expect("For a `NotEqual`, the removed value should be provided")
191 ),
192 PredicateType::Equal => predicate!(
193 domain
194 == assignments.get_assigned_value(&domain).expect(
195 "Expected domain to be assigned when creating an `Equal` predicate"
196 )
197 ),
198 }
199 }
200}
201
202impl From<Predicate> for PredicateType {
203 fn from(value: Predicate) -> Self {
204 match value.get_type_code() {
205 LOWER_BOUND_CODE => Self::LowerBound,
206 UPPER_BOUND_CODE => Self::UpperBound,
207 EQUAL_CODE => Self::Equal,
208 NOT_EQUAL_CODE => Self::NotEqual,
209 code => panic!("Unknown type code {code}"),
210 }
211 }
212}
213
214impl PredicateType {
215 pub const fn into_bits(self) -> u8 {
216 self as _
217 }
218
219 pub const fn from_bits(value: u8) -> PredicateType {
220 match value {
221 LOWER_BOUND_CODE => PredicateType::LowerBound,
222 UPPER_BOUND_CODE => PredicateType::UpperBound,
223 EQUAL_CODE => PredicateType::Equal,
224 NOT_EQUAL_CODE => PredicateType::NotEqual,
225 _ => panic!("Unknown code"),
226 }
227 }
228}
229
230impl Predicate {
231 pub(crate) fn is_mutually_exclusive_with(self, other: Predicate) -> bool {
232 let domain_id = self.get_domain();
233 let rhs = self.get_right_hand_side();
234
235 let domain_id_other = other.get_domain();
236 let rhs_other = other.get_right_hand_side();
237
238 if domain_id != domain_id_other {
239 return false;
241 }
242
243 match (self.get_predicate_type(), other.get_predicate_type()) {
244 (PredicateType::LowerBound, PredicateType::LowerBound)
245 | (PredicateType::LowerBound, PredicateType::NotEqual)
246 | (PredicateType::UpperBound, PredicateType::UpperBound)
247 | (PredicateType::UpperBound, PredicateType::NotEqual)
248 | (PredicateType::NotEqual, PredicateType::LowerBound)
249 | (PredicateType::NotEqual, PredicateType::UpperBound)
250 | (PredicateType::NotEqual, PredicateType::NotEqual) => false,
251 (PredicateType::LowerBound, PredicateType::UpperBound) => rhs > rhs_other,
252 (PredicateType::UpperBound, PredicateType::LowerBound) => rhs_other > rhs,
253 (PredicateType::LowerBound, PredicateType::Equal) => rhs > rhs_other,
254 (PredicateType::Equal, PredicateType::LowerBound) => rhs_other > rhs,
255 (PredicateType::UpperBound, PredicateType::Equal) => rhs < rhs_other,
256 (PredicateType::Equal, PredicateType::UpperBound) => rhs_other < rhs,
257 (PredicateType::NotEqual, PredicateType::Equal)
258 | (PredicateType::Equal, PredicateType::NotEqual) => rhs == rhs_other,
259 (PredicateType::Equal, PredicateType::Equal) => rhs != rhs_other,
260 }
261 }
262 pub fn is_equality_predicate(&self) -> bool {
263 self.get_type_code() == EQUAL_CODE
264 }
265
266 pub fn is_lower_bound_predicate(&self) -> bool {
267 self.get_type_code() == LOWER_BOUND_CODE
268 }
269
270 pub fn is_upper_bound_predicate(&self) -> bool {
271 self.get_type_code() == UPPER_BOUND_CODE
272 }
273
274 pub fn is_not_equal_predicate(&self) -> bool {
275 self.get_type_code() == NOT_EQUAL_CODE
276 }
277
278 pub fn get_domain(&self) -> DomainId {
280 DomainId::new(0b00111111_11111111_11111111_11111111 & self.id)
281 }
282
283 pub fn get_right_hand_side(&self) -> i32 {
284 self.value
285 }
286
287 pub fn trivially_true() -> Predicate {
288 let domain_id = DomainId::new(0);
291 predicate!(domain_id == 1)
292 }
293
294 pub fn trivially_false() -> Predicate {
295 let domain_id = DomainId::new(0);
298 predicate!(domain_id != 1)
299 }
300}
301
302impl std::ops::Not for Predicate {
303 type Output = Predicate;
304
305 fn not(self) -> Self::Output {
306 let domain_id = self.get_domain();
307 let value = self.get_right_hand_side();
308
309 match self.get_predicate_type() {
310 PredicateType::LowerBound => predicate!(domain_id <= value - 1),
311 PredicateType::UpperBound => predicate!(domain_id >= value + 1),
312 PredicateType::NotEqual => predicate!(domain_id == value),
313 PredicateType::Equal => predicate!(domain_id != value),
314 }
315 }
316}
317
318impl std::fmt::Display for Predicate {
319 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320 if *self == Predicate::trivially_true() {
321 write!(f, "[True]")
322 } else if *self == Predicate::trivially_false() {
323 write!(f, "[False]")
324 } else {
325 let domain_id = self.get_domain();
326 let rhs = self.get_right_hand_side();
327
328 match self.get_predicate_type() {
329 PredicateType::LowerBound => write!(f, "[{domain_id} >= {rhs}]"),
330 PredicateType::UpperBound => write!(f, "[{domain_id} <= {rhs}]"),
331 PredicateType::NotEqual => write!(f, "[{domain_id} != {rhs}]"),
332 PredicateType::Equal => write!(f, "[{domain_id} == {rhs}]"),
333 }
334 }
335 }
336}
337
338impl std::fmt::Debug for Predicate {
339 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
340 write!(f, "{self}")
341 }
342}
343
344impl AtomicConstraint for Predicate {
345 type Identifier = DomainId;
346
347 fn identifier(&self) -> Self::Identifier {
348 self.get_domain()
349 }
350
351 fn comparison(&self) -> pumpkin_checking::Comparison {
352 match self.get_predicate_type() {
353 PredicateType::LowerBound => pumpkin_checking::Comparison::GreaterEqual,
354 PredicateType::UpperBound => pumpkin_checking::Comparison::LessEqual,
355 PredicateType::NotEqual => pumpkin_checking::Comparison::NotEqual,
356 PredicateType::Equal => pumpkin_checking::Comparison::Equal,
357 }
358 }
359
360 fn value(&self) -> i32 {
361 self.get_right_hand_side()
362 }
363
364 fn negate(&self) -> Self {
365 !*self
366 }
367}
368
369#[cfg(test)]
370mod test {
371 use super::Predicate;
372 use crate::predicate;
373 use crate::variables::DomainId;
374
375 #[test]
376 fn are_mutually_exclusive() {
377 let domain_id = DomainId::new(0);
378
379 assert!(!predicate!(domain_id >= 5).is_mutually_exclusive_with(predicate!(domain_id >= 7)));
380 assert!(!predicate!(domain_id >= 5).is_mutually_exclusive_with(predicate!(domain_id != 2)));
381 assert!(!predicate!(domain_id <= 5).is_mutually_exclusive_with(predicate!(domain_id <= 8)));
382 assert!(!predicate!(domain_id <= 5).is_mutually_exclusive_with(predicate!(domain_id != 8)));
383 assert!(!predicate!(domain_id != 9).is_mutually_exclusive_with(predicate!(domain_id >= 8)));
384 assert!(!predicate!(domain_id != 9).is_mutually_exclusive_with(predicate!(domain_id <= 8)));
385 assert!(!predicate!(domain_id != 9).is_mutually_exclusive_with(predicate!(domain_id != 8)));
386
387 assert!(predicate!(domain_id <= 7).is_mutually_exclusive_with(predicate!(domain_id >= 8)));
388 assert!(predicate!(domain_id >= 8).is_mutually_exclusive_with(predicate!(domain_id <= 7)));
389
390 assert!(predicate!(domain_id >= 8).is_mutually_exclusive_with(predicate!(domain_id == 7)));
391 assert!(predicate!(domain_id == 7).is_mutually_exclusive_with(predicate!(domain_id >= 8)));
392
393 assert!(predicate!(domain_id == 7).is_mutually_exclusive_with(predicate!(domain_id <= 6)));
394 assert!(predicate!(domain_id <= 6).is_mutually_exclusive_with(predicate!(domain_id == 7)));
395
396 assert!(predicate!(domain_id != 8).is_mutually_exclusive_with(predicate!(domain_id == 8)));
397 assert!(predicate!(domain_id == 8).is_mutually_exclusive_with(predicate!(domain_id != 8)));
398
399 assert!(predicate!(domain_id == 7).is_mutually_exclusive_with(predicate!(domain_id == 8)));
400 }
401
402 #[test]
403 fn negating_trivially_true_predicate() {
404 let trivially_true = Predicate::trivially_true();
405 let trivially_false = Predicate::trivially_false();
406 assert!(!trivially_true == trivially_false);
407 }
408
409 #[test]
410 fn negating_trivially_false_predicate() {
411 let trivially_true = Predicate::trivially_true();
412 let trivially_false = Predicate::trivially_false();
413 assert!(!trivially_false == trivially_true);
414 }
415
416 #[test]
417 fn predicates_over_same_domain_are_ordered_by_increasing_lower_bound() {
418 let x = DomainId::new(0);
419 let p1 = predicate![x >= 4];
420 let p2 = predicate![x >= 6];
421 assert!(p1 < p2);
422 }
423
424 #[test]
425 fn not_equal_predicates_are_bigger_than_lower_bounds() {
426 let x = DomainId::new(0);
427 let p1 = predicate![x >= 4];
428 let p2 = predicate![x != 6];
429 let p3 = predicate![x != 2];
430
431 assert!(p1 < p2);
432 assert!(p1 < p3);
433 }
434
435 #[test]
436 fn not_equal_predicates_are_ordered_by_rhs() {
437 let x = DomainId::new(0);
438 let p1 = predicate![x != 6];
439 let p2 = predicate![x != 2];
440
441 assert!(p1 > p2);
442 }
443
444 #[test]
445 fn equal_predicates_are_ordered_by_rhs() {
446 let x = DomainId::new(0);
447 let p1 = predicate![x == 6];
448 let p2 = predicate![x == 2];
449
450 assert!(p1 > p2);
451 }
452
453 #[test]
454 fn equal_predicates_bigger_than_lower_bounds() {
455 let x = DomainId::new(0);
456 let p1 = predicate![x == 6];
457 let p2 = predicate![x >= 2];
458
459 assert!(p1 > p2);
460 }
461
462 #[test]
463 fn equal_predicates_smaller_than_upper_bounds() {
464 let x = DomainId::new(0);
465 let p1 = predicate![x == 6];
466 let p2 = predicate![x <= 2];
467
468 assert!(p1 < p2);
469 }
470
471 #[test]
472 fn tighter_upper_bound_is_smaller() {
473 let x = DomainId::new(0);
474 let p1 = predicate![x <= 6];
475 let p2 = predicate![x <= 2];
476
477 assert!(p1 > p2);
478 }
479
480 #[test]
481 fn implies_over_different_domains_is_false() {
482 let x = DomainId::new(0);
483 let y = DomainId::new(1);
484
485 assert!(!predicate![x >= 5].implies(predicate![y >= 4]));
486 }
487
488 #[test]
489 fn lower_bound_implies() {
490 let x = DomainId::new(0);
491
492 assert!(predicate![x >= 5].implies(predicate![x >= 5]));
494 assert!(predicate![x >= 5].implies(predicate![x >= 4]));
495
496 assert!(predicate![x >= 5].implies(predicate![x != 4]));
498 assert!(predicate![x >= 5].implies(predicate![x != 3]));
499
500 assert!(!predicate![x >= 5].implies(predicate![x >= 6]));
502
503 assert!(!predicate![x >= 5].implies(predicate![x != 6]));
505 assert!(!predicate![x >= 5].implies(predicate![x != 5]));
506
507 assert!(!predicate![x >= 5].implies(predicate![x == 6]));
509 assert!(!predicate![x >= 5].implies(predicate![x == 5]));
510 assert!(!predicate![x >= 5].implies(predicate![x == 4]));
511 }
512
513 #[test]
514 fn upper_bound_implies() {
515 let x = DomainId::new(0);
516
517 assert!(predicate![x <= 5].implies(predicate![x <= 5]));
519 assert!(predicate![x <= 5].implies(predicate![x <= 6]));
520
521 assert!(predicate![x <= 5].implies(predicate![x != 6]));
523 assert!(predicate![x <= 5].implies(predicate![x != 7]));
524
525 assert!(!predicate![x <= 5].implies(predicate![x <= 4]));
527
528 assert!(!predicate![x <= 5].implies(predicate![x != 4]));
530 assert!(!predicate![x <= 5].implies(predicate![x != 5]));
531
532 assert!(!predicate![x <= 5].implies(predicate![x == 6]));
534 assert!(!predicate![x <= 5].implies(predicate![x == 5]));
535 assert!(!predicate![x <= 5].implies(predicate![x == 4]));
536 }
537
538 #[test]
539 fn equals_implies() {
540 let x = DomainId::new(0);
541
542 assert!(predicate![x == 5].implies(predicate![x >= 5]));
544 assert!(predicate![x == 5].implies(predicate![x >= 4]));
545
546 assert!(predicate![x == 5].implies(predicate![x <= 5]));
548 assert!(predicate![x == 5].implies(predicate![x <= 6]));
549
550 assert!(predicate![x == 5].implies(predicate![x != 4]));
552 assert!(predicate![x == 5].implies(predicate![x != 6]));
553
554 assert!(!predicate![x == 5].implies(predicate![x != 5]));
556
557 assert!(!predicate![x == 5].implies(predicate![x >= 6]));
559
560 assert!(!predicate![x == 5].implies(predicate![x <= 4]));
562 }
563
564 #[test]
565 fn not_equals_implies_nothing() {
566 let x = DomainId::new(0);
567
568 assert!(!predicate![x != 5].implies(predicate![x <= 4]));
569 assert!(!predicate![x != 5].implies(predicate![x <= 5]));
570 assert!(!predicate![x != 5].implies(predicate![x <= 6]));
571
572 assert!(!predicate![x != 5].implies(predicate![x >= 4]));
573 assert!(!predicate![x != 5].implies(predicate![x >= 5]));
574 assert!(!predicate![x != 5].implies(predicate![x >= 6]));
575
576 assert!(!predicate![x != 5].implies(predicate![x == 4]));
577 assert!(!predicate![x != 5].implies(predicate![x == 5]));
578 assert!(!predicate![x != 5].implies(predicate![x == 6]));
579
580 assert!(!predicate![x != 5].implies(predicate![x != 4]));
581 assert!(!predicate![x != 5].implies(predicate![x != 6]));
582
583 assert!(predicate![x != 5].implies(predicate![x != 5]));
584 }
585}