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