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