pumpkin_core/api/
solver.rs

1use super::outputs::Satisfiable;
2use super::outputs::SolutionReference;
3use super::results::OptimisationResult;
4use super::results::SatisfactionResult;
5use super::results::SatisfactionResultUnderAssumptions;
6use crate::basic_types::CSPSolverExecutionFlag;
7use crate::basic_types::ConstraintOperationError;
8use crate::basic_types::Solution;
9use crate::branching::branchers::autonomous_search::AutonomousSearch;
10use crate::branching::branchers::independent_variable_value_brancher::IndependentVariableValueBrancher;
11use crate::branching::value_selection::RandomSplitter;
12#[cfg(doc)]
13use crate::branching::value_selection::ValueSelector;
14use crate::branching::variable_selection::RandomSelector;
15#[cfg(doc)]
16use crate::branching::variable_selection::VariableSelector;
17use crate::branching::Brancher;
18use crate::constraints::ConstraintPoster;
19use crate::containers::HashSet;
20use crate::engine::predicates::predicate::Predicate;
21use crate::engine::propagation::constructor::PropagatorConstructor;
22use crate::engine::termination::TerminationCondition;
23use crate::engine::variables::DomainId;
24use crate::engine::variables::IntegerVariable;
25use crate::engine::variables::Literal;
26use crate::engine::ConstraintSatisfactionSolver;
27#[cfg(doc)]
28use crate::optimisation::linear_sat_unsat::LinearSatUnsat;
29#[cfg(doc)]
30use crate::optimisation::linear_unsat_sat::LinearUnsatSat;
31use crate::optimisation::solution_callback::SolutionCallback;
32use crate::optimisation::OptimisationProcedure;
33use crate::options::SolverOptions;
34#[cfg(doc)]
35use crate::predicates;
36use crate::proof::ConstraintTag;
37use crate::results::solution_iterator::SolutionIterator;
38use crate::results::unsatisfiable::UnsatisfiableUnderAssumptions;
39use crate::statistics::log_statistic;
40use crate::statistics::log_statistic_postfix;
41
42/// The main interaction point which allows the creation of variables, the addition of constraints,
43/// and solving problems.
44///
45///
46/// # Creating Variables
47/// As stated in [`crate::variables`], we can create two types of variables: propositional variables
48/// and integer variables.
49///
50/// ```rust
51/// # use pumpkin_solver::Solver;
52/// # use crate::pumpkin_solver::variables::TransformableVariable;
53/// let mut solver = Solver::default();
54///
55/// // Integer Variables
56///
57/// // We can create an integer variable with a domain in the range [0, 10]
58/// let integer_between_bounds = solver.new_bounded_integer(0, 10);
59///
60/// // We can also create such a variable with a name
61/// let named_integer_between_bounds = solver.new_named_bounded_integer(0, 10, "x");
62///
63/// // We can also create an integer variable with a non-continuous domain in the follow way
64/// let mut sparse_integer = solver.new_sparse_integer(vec![0, 3, 5]);
65///
66/// // We can also create such a variable with a name
67/// let named_sparse_integer = solver.new_named_sparse_integer(vec![0, 3, 5], "y");
68///
69/// // Additionally, we can also create an affine view over a variable with both a scale and an offset (or either)
70/// let view_over_integer = integer_between_bounds.scaled(-1).offset(15);
71///
72///
73/// // Propositional Variable
74///
75/// // We can create a literal
76/// let literal = solver.new_literal();
77///
78/// // We can also create such a variable with a name
79/// let named_literal = solver.new_named_literal("z");
80///
81/// // We can also get the predicate from the literal
82/// let true_predicate = literal.get_true_predicate();
83///
84/// // We can also create an iterator of new literals and get a number of them at once
85/// let list_of_5_literals = solver.new_literals().take(5).collect::<Vec<_>>();
86/// assert_eq!(list_of_5_literals.len(), 5);
87/// ```
88///
89/// # Using the Solver
90/// For examples on how to use the solver, see the [root-level crate documentation](crate) or [one of these examples](https://github.com/ConSol-Lab/Pumpkin/tree/master/pumpkin-lib/examples).
91#[derive(Debug)]
92pub struct Solver {
93    /// The internal [`ConstraintSatisfactionSolver`] which is used to solve the problems.
94    pub(crate) satisfaction_solver: ConstraintSatisfactionSolver,
95    true_literal: Literal,
96}
97
98impl Default for Solver {
99    fn default() -> Self {
100        let satisfaction_solver = ConstraintSatisfactionSolver::default();
101        let true_literal = Literal::new(Predicate::trivially_true().get_domain());
102        Self {
103            satisfaction_solver,
104            true_literal,
105        }
106    }
107}
108
109impl Solver {
110    /// Creates a solver with the provided [`SolverOptions`].
111    pub fn with_options(solver_options: SolverOptions) -> Self {
112        let satisfaction_solver = ConstraintSatisfactionSolver::new(solver_options);
113        let true_literal = Literal::new(Predicate::trivially_true().get_domain());
114        Self {
115            satisfaction_solver,
116            true_literal,
117        }
118    }
119
120    /// Logs the statistics currently present in the solver with the provided objective value.
121    pub fn log_statistics_with_objective(&self, objective_value: i64) {
122        log_statistic("objective", objective_value);
123        self.log_statistics();
124    }
125
126    /// Logs the statistics currently present in the solver.
127    pub fn log_statistics(&self) {
128        self.satisfaction_solver.log_statistics();
129        log_statistic_postfix();
130    }
131
132    pub fn get_solution_reference(&self) -> SolutionReference<'_> {
133        self.satisfaction_solver.get_solution_reference()
134    }
135}
136
137/// Methods to retrieve information about variables
138impl Solver {
139    /// Get the value of the given [`Literal`] at the root level (after propagation), which could be
140    /// unassigned.
141    pub fn get_literal_value(&self, literal: Literal) -> Option<bool> {
142        self.satisfaction_solver.get_literal_value(literal)
143    }
144
145    /// Get the lower-bound of the given [`IntegerVariable`] at the root level (after propagation).
146    pub fn lower_bound(&self, variable: &impl IntegerVariable) -> i32 {
147        self.satisfaction_solver.get_lower_bound(variable)
148    }
149
150    /// Get the upper-bound of the given [`IntegerVariable`] at the root level (after propagation).
151    pub fn upper_bound(&self, variable: &impl IntegerVariable) -> i32 {
152        self.satisfaction_solver.get_upper_bound(variable)
153    }
154}
155
156/// Functions to create and retrieve integer and propositional variables.
157impl Solver {
158    /// Returns an infinite iterator of positive literals of new variables. The new variables will
159    /// be unnamed.
160    ///
161    /// # Example
162    /// ```
163    /// # use pumpkin_solver::Solver;
164    /// # use pumpkin_solver::variables::Literal;
165    /// let mut solver = Solver::default();
166    /// let literals: Vec<Literal> = solver.new_literals().take(5).collect();
167    ///
168    /// // `literals` contains 5 positive literals of newly created propositional variables.
169    /// assert_eq!(literals.len(), 5);
170    /// ```
171    ///
172    /// Note that this method captures the lifetime of the immutable reference to `self`.
173    pub fn new_literals(&mut self) -> impl Iterator<Item = Literal> + '_ {
174        std::iter::from_fn(|| Some(self.new_literal()))
175    }
176
177    /// Create a fresh propositional variable and return the literal with positive polarity.
178    ///
179    /// # Example
180    /// ```rust
181    /// # use pumpkin_solver::Solver;
182    /// let mut solver = Solver::default();
183    ///
184    /// // We can create a literal
185    /// let literal = solver.new_literal();
186    /// ```
187    pub fn new_literal(&mut self) -> Literal {
188        self.satisfaction_solver.create_new_literal(None)
189    }
190
191    pub fn new_literal_for_predicate(
192        &mut self,
193        predicate: Predicate,
194        constraint_tag: ConstraintTag,
195    ) -> Literal {
196        self.satisfaction_solver
197            .create_new_literal_for_predicate(predicate, None, constraint_tag)
198    }
199
200    /// Create a fresh propositional variable with a given name and return the literal with positive
201    /// polarity.
202    ///
203    /// # Example
204    /// ```rust
205    /// # use pumpkin_solver::Solver;
206    /// let mut solver = Solver::default();
207    ///
208    /// // We can also create such a variable with a name
209    /// let named_literal = solver.new_named_literal("z");
210    /// ```
211    pub fn new_named_literal(&mut self, name: impl Into<String>) -> Literal {
212        self.satisfaction_solver
213            .create_new_literal(Some(name.into()))
214    }
215
216    /// Get a literal which is always true.
217    pub fn get_true_literal(&self) -> Literal {
218        self.true_literal
219    }
220
221    /// Get a literal which is always false.
222    pub fn get_false_literal(&self) -> Literal {
223        !self.true_literal
224    }
225
226    /// Create a new integer variable with the given bounds.
227    ///
228    /// # Example
229    /// ```rust
230    /// # use pumpkin_solver::Solver;
231    /// let mut solver = Solver::default();
232    ///
233    /// // We can create an integer variable with a domain in the range [0, 10]
234    /// let integer_between_bounds = solver.new_bounded_integer(0, 10);
235    /// ```
236    pub fn new_bounded_integer(&mut self, lower_bound: i32, upper_bound: i32) -> DomainId {
237        self.satisfaction_solver
238            .create_new_integer_variable(lower_bound, upper_bound, None)
239    }
240
241    /// Create a new named integer variable with the given bounds.
242    ///
243    /// # Example
244    /// ```rust
245    /// # use pumpkin_solver::Solver;
246    /// let mut solver = Solver::default();
247    ///
248    /// // We can also create such a variable with a name
249    /// let named_integer_between_bounds = solver.new_named_bounded_integer(0, 10, "x");
250    /// ```
251    pub fn new_named_bounded_integer(
252        &mut self,
253        lower_bound: i32,
254        upper_bound: i32,
255        name: impl Into<String>,
256    ) -> DomainId {
257        self.satisfaction_solver.create_new_integer_variable(
258            lower_bound,
259            upper_bound,
260            Some(name.into()),
261        )
262    }
263
264    /// Create a new integer variable which has a domain of predefined values. We remove duplicates
265    /// by converting to a hash set
266    ///
267    /// # Example
268    /// ```rust
269    /// # use pumpkin_solver::Solver;
270    /// let mut solver = Solver::default();
271    ///
272    /// // We can also create an integer variable with a non-continuous domain in the follow way
273    /// let mut sparse_integer = solver.new_sparse_integer(vec![0, 3, 5]);
274    /// ```
275    pub fn new_sparse_integer(&mut self, values: impl Into<Vec<i32>>) -> DomainId {
276        let values: HashSet<i32> = values.into().into_iter().collect();
277
278        self.satisfaction_solver
279            .create_new_integer_variable_sparse(values.into_iter().collect(), None)
280    }
281
282    /// Create a new named integer variable which has a domain of predefined values.
283    ///
284    /// # Example
285    /// ```rust
286    /// # use pumpkin_solver::Solver;
287    /// let mut solver = Solver::default();
288    ///
289    /// // We can also create such a variable with a name
290    /// let named_sparse_integer = solver.new_named_sparse_integer(vec![0, 3, 5], "y");
291    /// ```
292    pub fn new_named_sparse_integer(
293        &mut self,
294        values: impl Into<Vec<i32>>,
295        name: impl Into<String>,
296    ) -> DomainId {
297        self.satisfaction_solver
298            .create_new_integer_variable_sparse(values.into(), Some(name.into()))
299    }
300}
301
302/// Functions for solving with the constraints that have been added to the [`Solver`].
303impl Solver {
304    /// Solves the current model in the [`Solver`] until it finds a solution (or is indicated to
305    /// terminate by the provided [`TerminationCondition`]) and returns a [`SatisfactionResult`]
306    /// which can be used to obtain the found solution or find other solutions.
307    pub fn satisfy<'this, 'brancher, B: Brancher, T: TerminationCondition>(
308        &'this mut self,
309        brancher: &'brancher mut B,
310        termination: &mut T,
311    ) -> SatisfactionResult<'this, 'brancher, B> {
312        match self.satisfaction_solver.solve(termination, brancher) {
313            CSPSolverExecutionFlag::Feasible => {
314                brancher.on_solution(self.satisfaction_solver.get_solution_reference());
315
316                SatisfactionResult::Satisfiable(Satisfiable::new(self, brancher))
317            }
318            CSPSolverExecutionFlag::Infeasible => {
319                // Reset the state whenever we return a result
320                self.satisfaction_solver.restore_state_at_root(brancher);
321                let _ = self.satisfaction_solver.conclude_proof_unsat();
322
323                SatisfactionResult::Unsatisfiable(self)
324            }
325            CSPSolverExecutionFlag::Timeout => {
326                // Reset the state whenever we return a result
327                self.satisfaction_solver.restore_state_at_root(brancher);
328                SatisfactionResult::Unknown(self)
329            }
330        }
331    }
332
333    pub fn get_solution_iterator<
334        'this,
335        'brancher,
336        'termination,
337        B: Brancher,
338        T: TerminationCondition,
339    >(
340        &'this mut self,
341        brancher: &'brancher mut B,
342        termination: &'termination mut T,
343    ) -> SolutionIterator<'this, 'brancher, 'termination, B, T> {
344        SolutionIterator::new(self, brancher, termination)
345    }
346
347    /// Solves the current model in the [`Solver`] until it finds a solution (or is indicated to
348    /// terminate by the provided [`TerminationCondition`]) and returns a [`SatisfactionResult`]
349    /// which can be used to obtain the found solution or find other solutions.
350    ///
351    /// This method takes as input a list of [`Predicate`]s which represent so-called assumptions
352    /// (see \[1\] for a more detailed explanation). See the [`predicates`] documentation for how
353    /// to construct these predicates.
354    ///
355    /// # Bibliography
356    /// \[1\] N. Eén and N. Sörensson, ‘Temporal induction by incremental SAT solving’, Electronic
357    /// Notes in Theoretical Computer Science, vol. 89, no. 4, pp. 543–560, 2003.
358    pub fn satisfy_under_assumptions<'this, 'brancher, B: Brancher, T: TerminationCondition>(
359        &'this mut self,
360        brancher: &'brancher mut B,
361        termination: &mut T,
362        assumptions: &[Predicate],
363    ) -> SatisfactionResultUnderAssumptions<'this, 'brancher, B> {
364        match self
365            .satisfaction_solver
366            .solve_under_assumptions(assumptions, termination, brancher)
367        {
368            CSPSolverExecutionFlag::Feasible => {
369                let solution: Solution = self.satisfaction_solver.get_solution_reference().into();
370                // Reset the state whenever we return a result
371                brancher.on_solution(solution.as_reference());
372                SatisfactionResultUnderAssumptions::Satisfiable(Satisfiable::new(self, brancher))
373            }
374            CSPSolverExecutionFlag::Infeasible => {
375                if self
376                    .satisfaction_solver
377                    .state
378                    .is_infeasible_under_assumptions()
379                {
380                    // The state is automatically reset when we return this result
381                    SatisfactionResultUnderAssumptions::UnsatisfiableUnderAssumptions(
382                        UnsatisfiableUnderAssumptions::new(&mut self.satisfaction_solver, brancher),
383                    )
384                } else {
385                    // Reset the state whenever we return a result
386                    self.satisfaction_solver.restore_state_at_root(brancher);
387                    SatisfactionResultUnderAssumptions::Unsatisfiable(self)
388                }
389            }
390            CSPSolverExecutionFlag::Timeout => {
391                // Reset the state whenever we return a result
392                self.satisfaction_solver.restore_state_at_root(brancher);
393                SatisfactionResultUnderAssumptions::Unknown(self)
394            }
395        }
396    }
397
398    /// Solves the model currently in the [`Solver`] to optimality where the provided
399    /// `objective_variable` is optimised as indicated by the `direction` (or is indicated to
400    /// terminate by the provided [`TerminationCondition`]). Uses a search strategy based on the
401    /// provided [`OptimisationProcedure`], currently [`LinearSatUnsat`] and
402    /// [`LinearUnsatSat`] are supported.
403    ///
404    /// It returns an [`OptimisationResult`] which can be used to retrieve the optimal solution if
405    /// it exists.
406    pub fn optimise<B, Callback>(
407        &mut self,
408        brancher: &mut B,
409        termination: &mut impl TerminationCondition,
410        mut optimisation_procedure: impl OptimisationProcedure<B, Callback>,
411    ) -> OptimisationResult
412    where
413        B: Brancher,
414        Callback: SolutionCallback<B>,
415    {
416        optimisation_procedure.optimise(brancher, termination, self)
417    }
418}
419
420/// Functions for adding new constraints to the solver.
421impl Solver {
422    /// Creates a new [`ConstraintTag`] that can be used to add constraints to the solver.
423    ///
424    /// See the [`ConstraintTag`] documentation for information on how the tags are used.
425    pub fn new_constraint_tag(&mut self) -> ConstraintTag {
426        self.satisfaction_solver.new_constraint_tag()
427    }
428
429    /// Add a constraint to the solver. This returns a [`ConstraintPoster`] which enables control
430    /// on whether to add the constraint as-is, or whether to (half) reify it.
431    ///
432    /// All constraints require a [`ConstraintTag`] to be supplied. See its documentation for more
433    /// information.
434    ///
435    /// If none of the methods on [`ConstraintPoster`] are used, the constraint _is not_ actually
436    /// added to the solver. In this case, a warning is emitted.
437    ///
438    /// # Example
439    /// ```
440    /// # use pumpkin_solver::constraints;
441    /// # use pumpkin_solver::Solver;
442    /// let mut solver = Solver::default();
443    ///
444    /// let a = solver.new_bounded_integer(0, 3);
445    /// let b = solver.new_bounded_integer(0, 3);
446    ///
447    /// let constraint_tag = solver.new_constraint_tag();
448    ///
449    /// solver
450    ///     .add_constraint(constraints::equals([a, b], 0, constraint_tag))
451    ///     .post();
452    /// ```
453    pub fn add_constraint<Constraint>(
454        &mut self,
455        constraint: Constraint,
456    ) -> ConstraintPoster<'_, Constraint> {
457        ConstraintPoster::new(self, constraint)
458    }
459
460    /// Creates a clause from `literals` and adds it to the current formula.
461    ///
462    /// If the formula becomes trivially unsatisfiable, a [`ConstraintOperationError`] will be
463    /// returned. Subsequent calls to this method will always return an error, and no
464    /// modification of the solver will take place.
465    pub fn add_clause(
466        &mut self,
467        clause: impl IntoIterator<Item = Predicate>,
468        constraint_tag: ConstraintTag,
469    ) -> Result<(), ConstraintOperationError> {
470        self.satisfaction_solver.add_clause(clause, constraint_tag)
471    }
472
473    /// Post a new propagator to the solver. If unsatisfiability can be immediately determined
474    /// through propagation, this will return a [`ConstraintOperationError`].
475    ///
476    /// A propagator is provided through an implementation of [`PropagatorConstructor`]. The
477    /// propagator that will be added is [`PropagatorConstructor::PropagatorImpl`].
478    ///
479    /// If the solver is already in a conflicting state, i.e. a previous call to this method
480    /// already returned `false`, calling this again will not alter the solver in any way, and
481    /// `false` will be returned again.
482    pub(crate) fn add_propagator<Constructor>(
483        &mut self,
484        constructor: Constructor,
485    ) -> Result<(), ConstraintOperationError>
486    where
487        Constructor: PropagatorConstructor,
488        Constructor::PropagatorImpl: 'static,
489    {
490        self.satisfaction_solver.add_propagator(constructor)
491    }
492}
493
494/// Default brancher implementation
495impl Solver {
496    /// Creates an instance of the [`DefaultBrancher`].
497    pub fn default_brancher(&self) -> DefaultBrancher {
498        DefaultBrancher::default_over_all_variables(&self.satisfaction_solver.assignments)
499    }
500}
501
502/// Proof logging methods
503impl Solver {
504    #[doc(hidden)]
505    /// Conclude the proof with the unsatisfiable claim.
506    ///
507    /// This method will finish the proof. Any new operation will not be logged to the proof.
508    pub fn conclude_proof_unsat(&mut self) {
509        let _ = self.satisfaction_solver.conclude_proof_unsat();
510    }
511
512    /// Conclude the proof with the optimality claim.
513    ///
514    /// This method will finish the proof. Any new operation will not be logged to the proof.
515    pub fn conclude_proof_dual_bound(&mut self, bound: Predicate) {
516        let _ = self.satisfaction_solver.conclude_proof_optimal(bound);
517    }
518}
519
520/// A brancher which makes use of VSIDS \[1\] and solution-based phase saving (both adapted for CP).
521///
522/// If VSIDS does not contain any (unfixed) predicates then it will default to the
523/// [`IndependentVariableValueBrancher`] using [`RandomSelector`] for variable selection
524/// (over the variables in the order in which they were defined) and [`RandomSplitter`] for
525/// value selection.
526///
527/// # Bibliography
528/// \[1\] M. W. Moskewicz, C. F. Madigan, Y. Zhao, L. Zhang, and S. Malik, ‘Chaff: Engineering an
529/// efficient SAT solver’, in Proceedings of the 38th annual Design Automation Conference, 2001.
530///
531/// \[2\] E. Demirović, G. Chu, and P. J. Stuckey, ‘Solution-based phase saving for CP: A
532/// value-selection heuristic to simulate local search behavior in complete solvers’, in the
533/// proceedings of the Principles and Practice of Constraint Programming (CP 2018).
534pub type DefaultBrancher =
535    AutonomousSearch<IndependentVariableValueBrancher<DomainId, RandomSelector, RandomSplitter>>;