Skip to main content

pumpkin_core/constraints/
mod.rs

1//! Defines the main building blocks of constraints.
2use crate::Solver;
3use crate::propagation::PropagatorConstructor;
4use crate::propagators::reified_propagator::ReifiedPropagatorArgs;
5use crate::variables::Literal;
6
7mod constraint_poster;
8pub use constraint_poster::ConstraintPoster;
9
10/// A [`Constraint`] is a relation over variables. It disqualifies certain partial assignments of
11/// making it into a solution of the problem.
12///
13/// For example, the constraint `a = b` over two variables `a` and `b` only allows assignments to
14/// `a` and `b` of the same value, and rejects any assignment where `a` and `b` differ.
15pub trait Constraint {
16    /// Add the [`Constraint`] to the [`Solver`].
17    ///
18    /// The `tag` allows inferences to be traced to the constraint that implies them. They will
19    /// show up in the proof log.
20    fn post(self, solver: &mut Solver);
21
22    /// Add the half-reified version of the [`Constraint`] to the [`Solver`]; i.e. post the
23    /// constraint `r -> constraint` where `r` is a reification literal.
24    ///
25    /// The `tag` allows inferences to be traced to the constraint that implies them. They will
26    /// show up in the proof log.
27    fn implied_by(self, solver: &mut Solver, reification_literal: Literal);
28}
29
30impl<ConcretePropagator> Constraint for ConcretePropagator
31where
32    ConcretePropagator: PropagatorConstructor + 'static,
33{
34    fn post(self, solver: &mut Solver) {
35        let _ = solver.add_propagator(self);
36    }
37
38    fn implied_by(self, solver: &mut Solver, reification_literal: Literal) {
39        let _ = solver.add_propagator(ReifiedPropagatorArgs {
40            propagator: self,
41            reification_literal,
42        });
43    }
44}
45
46impl<C: Constraint> Constraint for Vec<C> {
47    fn post(self, solver: &mut Solver) {
48        self.into_iter().for_each(|c| c.post(solver))
49    }
50
51    fn implied_by(self, solver: &mut Solver, reification_literal: Literal) {
52        self.into_iter()
53            .for_each(|c| c.implied_by(solver, reification_literal))
54    }
55}
56
57/// A [`Constraint`] which has a well-defined negation.
58///
59/// Having a negation means the [`Constraint`] can be fully reified; i.e., a constraint `C` can be
60/// turned into `r <-> C` where `r` is a reification literal.
61///
62/// For example, the negation of the [`Constraint`] `a = b` is (well-)defined as `a != b`.
63pub trait NegatableConstraint: Constraint {
64    type NegatedConstraint: NegatableConstraint + 'static;
65
66    fn negation(&self) -> Self::NegatedConstraint;
67
68    /// Add the reified version of the [`Constraint`] to the [`Solver`]; i.e. post the constraint
69    /// `r <-> constraint` where `r` is a reification literal.
70    ///
71    /// The `tag` allows inferences to be traced to the constraint that implies them. They will
72    /// show up in the proof log.
73    fn reify(self, solver: &mut Solver, reification_literal: Literal)
74    where
75        Self: Sized,
76    {
77        let negation = self.negation();
78
79        self.implied_by(solver, reification_literal);
80        negation.implied_by(solver, !reification_literal)
81    }
82}