Skip to main content

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