pumpkin_core/constraints/mod.rs
1//! Defines the constraints that Pumpkin provides out of the box which can be added to the
2//! [`Solver`].
3//!
4//! A constraint is a relation over variables. In the solver, constraints are enforced through
5//! propagators, and therefore constraints can be viewed as a collection of propagators.
6//!
7//! # Example
8//! ```
9//! # use pumpkin_core::constraints;
10//! # use pumpkin_core::Solver;
11//! let mut solver = Solver::default();
12//!
13//! let a = solver.new_bounded_integer(0, 3);
14//! let b = solver.new_bounded_integer(0, 3);
15//!
16//! // All constraints require a constraint tag.
17//! let constraint_tag = solver.new_constraint_tag();
18//!
19//! solver
20//! .add_constraint(constraints::equals([a, b], 0, constraint_tag))
21//! .post();
22//! ```
23//!
24//! # Note
25//! At the moment, the API for posting propagators is not yet publicly accessible as it is
26//! unstable. Consumers of the Pumpkin library can therefore only define constraints by
27//! decomposing them into the constraints that are predefined in the library. Once the
28//! propagator API is stabilized, it will become part of the public API.
29
30mod all_different;
31mod arithmetic;
32mod boolean;
33mod clause;
34mod constraint_poster;
35mod cumulative;
36mod disjunctive_strict;
37mod element;
38mod table;
39
40pub use all_different::*;
41pub use arithmetic::*;
42pub use boolean::*;
43pub use clause::*;
44pub use constraint_poster::*;
45pub use cumulative::*;
46pub use disjunctive_strict::*;
47pub use element::*;
48pub use table::*;
49
50use crate::engine::propagation::constructor::PropagatorConstructor;
51use crate::propagators::ReifiedPropagatorArgs;
52use crate::variables::Literal;
53use crate::ConstraintOperationError;
54use crate::Solver;
55
56/// A [`Constraint`] is a relation over variables. It disqualifies certain partial assignments of
57/// making it into a solution of the problem.
58///
59/// For example, the constraint `a = b` over two variables `a` and `b` only allows assignments to
60/// `a` and `b` of the same value, and rejects any assignment where `a` and `b` differ.
61pub trait Constraint {
62 /// Add the [`Constraint`] to the [`Solver`].
63 ///
64 /// This method returns a [`ConstraintOperationError`] if the addition of the [`Constraint`] led
65 /// to a root-level conflict.
66 ///
67 /// The `tag` allows inferences to be traced to the constraint that implies them. They will
68 /// show up in the proof log.
69 fn post(self, solver: &mut Solver) -> Result<(), ConstraintOperationError>;
70
71 /// Add the half-reified version of the [`Constraint`] to the [`Solver`]; i.e. post the
72 /// constraint `r -> constraint` where `r` is a reification literal.
73 ///
74 /// This method returns a [`ConstraintOperationError`] if the addition of the [`Constraint`] led
75 /// to a root-level conflict.
76 ///
77 /// The `tag` allows inferences to be traced to the constraint that implies them. They will
78 /// show up in the proof log.
79 fn implied_by(
80 self,
81 solver: &mut Solver,
82 reification_literal: Literal,
83 ) -> Result<(), ConstraintOperationError>;
84}
85
86impl<ConcretePropagator> Constraint for ConcretePropagator
87where
88 ConcretePropagator: PropagatorConstructor + 'static,
89{
90 fn post(self, solver: &mut Solver) -> Result<(), ConstraintOperationError> {
91 let _ = solver.add_propagator(self)?;
92 Ok(())
93 }
94
95 fn implied_by(
96 self,
97 solver: &mut Solver,
98 reification_literal: Literal,
99 ) -> Result<(), ConstraintOperationError> {
100 let _ = solver.add_propagator(ReifiedPropagatorArgs {
101 propagator: self,
102 reification_literal,
103 })?;
104 Ok(())
105 }
106}
107
108impl<C: Constraint> Constraint for Vec<C> {
109 fn post(self, solver: &mut Solver) -> Result<(), ConstraintOperationError> {
110 self.into_iter().try_for_each(|c| c.post(solver))
111 }
112
113 fn implied_by(
114 self,
115 solver: &mut Solver,
116 reification_literal: Literal,
117 ) -> Result<(), ConstraintOperationError> {
118 self.into_iter()
119 .try_for_each(|c| c.implied_by(solver, reification_literal))
120 }
121}
122
123/// A [`Constraint`] which has a well-defined negation.
124///
125/// Having a negation means the [`Constraint`] can be fully reified; i.e., a constraint `C` can be
126/// turned into `r <-> C` where `r` is a reification literal.
127///
128/// For example, the negation of the [`Constraint`] `a = b` is (well-)defined as `a != b`.
129pub trait NegatableConstraint: Constraint {
130 type NegatedConstraint: NegatableConstraint + 'static;
131
132 fn negation(&self) -> Self::NegatedConstraint;
133
134 /// Add the reified version of the [`Constraint`] to the [`Solver`]; i.e. post the constraint
135 /// `r <-> constraint` where `r` is a reification literal.
136 ///
137 /// This method returns a [`ConstraintOperationError`] if the addition of the [`Constraint`] led
138 /// to a root-level conflict.
139 ///
140 /// The `tag` allows inferences to be traced to the constraint that implies them. They will
141 /// show up in the proof log.
142 fn reify(
143 self,
144 solver: &mut Solver,
145 reification_literal: Literal,
146 ) -> Result<(), ConstraintOperationError>
147 where
148 Self: Sized,
149 {
150 let negation = self.negation();
151
152 self.implied_by(solver, reification_literal)?;
153 negation.implied_by(solver, !reification_literal)
154 }
155}