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>>;