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