use super::outputs::Satisfiable;
use super::outputs::SolutionReference;
use super::results::OptimisationResult;
use super::results::SatisfactionResult;
use super::results::SatisfactionResultUnderAssumptions;
use crate::basic_types::CSPSolverExecutionFlag;
use crate::basic_types::ConstraintOperationError;
use crate::branching::Brancher;
use crate::branching::branchers::autonomous_search::AutonomousSearch;
use crate::branching::branchers::independent_variable_value_brancher::IndependentVariableValueBrancher;
use crate::branching::value_selection::RandomSplitter;
#[cfg(doc)]
use crate::branching::value_selection::ValueSelector;
use crate::branching::variable_selection::RandomSelector;
#[cfg(doc)]
use crate::branching::variable_selection::VariableSelector;
use crate::conflict_resolving::ConflictAnalysisContext;
use crate::conflict_resolving::ConflictResolver;
use crate::constraints::ConstraintPoster;
use crate::containers::HashSet;
use crate::engine::ConstraintSatisfactionSolver;
use crate::engine::predicates::predicate::Predicate;
use crate::engine::termination::TerminationCondition;
use crate::engine::variables::DomainId;
use crate::engine::variables::IntegerVariable;
use crate::engine::variables::Literal;
use crate::optimisation::OptimisationProcedure;
#[cfg(doc)]
use crate::optimisation::linear_sat_unsat::LinearSatUnsat;
#[cfg(doc)]
use crate::optimisation::linear_unsat_sat::LinearUnsatSat;
use crate::optimisation::solution_callback::SolutionCallback;
use crate::options::SolverOptions;
#[cfg(doc)]
use crate::predicates;
use crate::proof::ConstraintTag;
use crate::propagation::PropagatorConstructor;
pub use crate::propagation::store::PropagatorHandle;
use crate::results::solution_iterator::SolutionIterator;
use crate::results::unsatisfiable::UnsatisfiableUnderAssumptions;
use crate::statistics::StatisticLogger;
use crate::statistics::log_statistic;
use crate::statistics::log_statistic_postfix;
#[derive(Debug)]
pub struct Solver {
pub(crate) satisfaction_solver: ConstraintSatisfactionSolver,
true_literal: Literal,
}
impl Default for Solver {
fn default() -> Self {
let satisfaction_solver = ConstraintSatisfactionSolver::default();
let true_literal = Literal::new(Predicate::trivially_true().get_domain());
Self {
satisfaction_solver,
true_literal,
}
}
}
impl Solver {
pub fn with_options(solver_options: SolverOptions) -> Self {
let satisfaction_solver = ConstraintSatisfactionSolver::new(solver_options);
let true_literal = Literal::new(Predicate::trivially_true().get_domain());
Self {
satisfaction_solver,
true_literal,
}
}
pub fn log_statistics_with_objective(
&self,
brancher: &impl Brancher,
resolver: &impl ConflictResolver,
objective_value: i64,
verbose: bool,
) {
log_statistic("objective", objective_value);
self.log_statistics(brancher, resolver, verbose);
}
pub fn log_statistics(
&self,
brancher: &impl Brancher,
resolver: &impl ConflictResolver,
verbose: bool,
) {
self.satisfaction_solver.log_statistics(verbose);
resolver.log_statistics(StatisticLogger::default());
if verbose {
brancher.log_statistics(StatisticLogger::default());
}
log_statistic_postfix();
}
pub fn get_solution_reference(&self) -> SolutionReference<'_> {
self.satisfaction_solver.get_solution_reference()
}
pub fn is_logging_proof(&self) -> bool {
self.satisfaction_solver.is_logging_proof()
}
}
impl Solver {
pub fn get_literal_value(&self, literal: Literal) -> Option<bool> {
self.satisfaction_solver.get_literal_value(literal)
}
pub fn lower_bound(&self, variable: &impl IntegerVariable) -> i32 {
self.satisfaction_solver.get_lower_bound(variable)
}
pub fn upper_bound(&self, variable: &impl IntegerVariable) -> i32 {
self.satisfaction_solver.get_upper_bound(variable)
}
pub fn is_inconsistent(&self) -> bool {
self.satisfaction_solver.get_state().is_inconsistent()
}
}
impl Solver {
pub fn new_literals(&mut self) -> impl Iterator<Item = Literal> + '_ {
std::iter::from_fn(|| Some(self.new_literal()))
}
pub fn new_literal(&mut self) -> Literal {
self.satisfaction_solver.create_new_literal(None)
}
pub fn new_literal_for_predicate(
&mut self,
predicate: Predicate,
constraint_tag: ConstraintTag,
) -> Literal {
self.satisfaction_solver
.create_new_literal_for_predicate(predicate, None, constraint_tag)
}
pub fn new_named_literal_for_predicate(
&mut self,
predicate: Predicate,
constraint_tag: ConstraintTag,
name: impl Into<String>,
) -> Literal {
self.satisfaction_solver.create_new_literal_for_predicate(
predicate,
Some(name.into().into()),
constraint_tag,
)
}
pub fn new_named_literal(&mut self, name: impl Into<String>) -> Literal {
let name = name.into();
self.satisfaction_solver
.create_new_literal(Some(name.into()))
}
pub fn get_true_literal(&self) -> Literal {
self.true_literal
}
pub fn get_false_literal(&self) -> Literal {
!self.true_literal
}
pub fn new_bounded_integer(&mut self, lower_bound: i32, upper_bound: i32) -> DomainId {
self.satisfaction_solver
.create_new_integer_variable(lower_bound, upper_bound, None)
}
pub fn new_named_bounded_integer(
&mut self,
lower_bound: i32,
upper_bound: i32,
name: impl Into<String>,
) -> DomainId {
let name = name.into();
self.satisfaction_solver.create_new_integer_variable(
lower_bound,
upper_bound,
Some(name.into()),
)
}
pub fn new_sparse_integer(&mut self, values: impl Into<Vec<i32>>) -> DomainId {
let values: HashSet<i32> = values.into().into_iter().collect();
self.satisfaction_solver
.create_new_integer_variable_sparse(values.into_iter().collect(), None)
}
pub fn new_named_sparse_integer(
&mut self,
values: impl Into<Vec<i32>>,
name: impl Into<String>,
) -> DomainId {
self.satisfaction_solver
.create_new_integer_variable_sparse(values.into(), Some(name.into()))
}
}
impl Solver {
pub fn satisfy<
'this,
'brancher,
'resolver,
B: Brancher,
T: TerminationCondition,
R: ConflictResolver,
>(
&'this mut self,
brancher: &'brancher mut B,
termination: &mut T,
resolver: &'resolver mut R,
) -> SatisfactionResult<'this, 'brancher, 'resolver, B, R> {
match self
.satisfaction_solver
.solve(termination, brancher, resolver)
{
CSPSolverExecutionFlag::Feasible => {
brancher.on_solution(self.satisfaction_solver.get_solution_reference());
SatisfactionResult::Satisfiable(Satisfiable::new(self, brancher, resolver))
}
CSPSolverExecutionFlag::Infeasible => {
self.satisfaction_solver.restore_state_at_root(brancher);
let _ = self.satisfaction_solver.conclude_proof_unsat();
SatisfactionResult::Unsatisfiable(self, brancher, resolver)
}
CSPSolverExecutionFlag::Timeout => {
self.satisfaction_solver.restore_state_at_root(brancher);
SatisfactionResult::Unknown(self, brancher, resolver)
}
}
}
pub fn get_solution_iterator<
'this,
'brancher,
'termination,
'resolver,
B: Brancher,
T: TerminationCondition,
R: ConflictResolver,
>(
&'this mut self,
brancher: &'brancher mut B,
termination: &'termination mut T,
resolver: &'resolver mut R,
) -> SolutionIterator<'this, 'brancher, 'termination, 'resolver, B, T, R> {
SolutionIterator::new(self, brancher, termination, resolver)
}
pub fn satisfy_under_assumptions<
'this,
'brancher,
'resolver,
B: Brancher,
R: ConflictResolver,
>(
&'this mut self,
brancher: &'brancher mut B,
termination: &mut impl TerminationCondition,
resolver: &'resolver mut R,
assumptions: &[Predicate],
) -> SatisfactionResultUnderAssumptions<'this, 'brancher, 'resolver, B, R> {
match self.satisfaction_solver.solve_under_assumptions(
assumptions,
termination,
brancher,
resolver,
) {
CSPSolverExecutionFlag::Feasible => {
brancher.on_solution(self.satisfaction_solver.get_solution_reference());
SatisfactionResultUnderAssumptions::Satisfiable(Satisfiable::new(
self, brancher, resolver,
))
}
CSPSolverExecutionFlag::Infeasible => {
if self
.satisfaction_solver
.solver_state
.is_infeasible_under_assumptions()
{
SatisfactionResultUnderAssumptions::UnsatisfiableUnderAssumptions(
UnsatisfiableUnderAssumptions::new(&mut self.satisfaction_solver, brancher),
)
} else {
self.satisfaction_solver.restore_state_at_root(brancher);
SatisfactionResultUnderAssumptions::Unsatisfiable(self)
}
}
CSPSolverExecutionFlag::Timeout => {
self.satisfaction_solver.restore_state_at_root(brancher);
SatisfactionResultUnderAssumptions::Unknown(self)
}
}
}
pub fn optimise<B, R, Callback>(
&mut self,
brancher: &mut B,
termination: &mut impl TerminationCondition,
resolver: &mut R,
mut optimisation_procedure: impl OptimisationProcedure<B, R, Callback>,
) -> OptimisationResult<Callback::Stop>
where
B: Brancher,
R: ConflictResolver,
Callback: SolutionCallback<B, R>,
{
optimisation_procedure.optimise(brancher, termination, resolver, self)
}
}
impl Solver {
pub fn new_constraint_tag(&mut self) -> ConstraintTag {
self.satisfaction_solver.new_constraint_tag()
}
pub fn add_constraint<Constraint>(
&mut self,
constraint: Constraint,
) -> ConstraintPoster<'_, Constraint> {
ConstraintPoster::new(self, constraint)
}
pub fn add_clause(
&mut self,
clause: impl IntoIterator<Item = Predicate>,
constraint_tag: ConstraintTag,
) -> Result<(), ConstraintOperationError> {
self.satisfaction_solver.add_clause(clause, constraint_tag)
}
pub fn add_propagator<Constructor>(
&mut self,
constructor: Constructor,
) -> Result<PropagatorHandle<Constructor::PropagatorImpl>, ConstraintOperationError>
where
Constructor: PropagatorConstructor,
Constructor::PropagatorImpl: 'static,
{
self.satisfaction_solver.add_propagator(constructor)
}
}
impl Solver {
pub fn default_brancher(&self) -> DefaultBrancher {
DefaultBrancher::default_over_all_variables(self.satisfaction_solver.assignments())
}
}
impl Solver {
#[doc(hidden)]
pub fn conclude_proof_unsat(&mut self) {
let _ = self.satisfaction_solver.conclude_proof_unsat();
}
pub fn conclude_proof_dual_bound(&mut self, bound: Predicate) {
let _ = self.satisfaction_solver.conclude_proof_optimal(bound);
}
}
impl Solver {
#[deprecated(note = "Should only be used for testing")]
pub fn conflict_analysis_context<'a>(
&'a mut self,
brancher: &'a mut impl Brancher,
) -> ConflictAnalysisContext<'a> {
ConflictAnalysisContext {
solver_state: &mut self.satisfaction_solver.solver_state,
brancher,
proof_log: &mut self.satisfaction_solver.internal_parameters.proof_log,
unit_nogood_inference_codes: &mut self.satisfaction_solver.unit_nogood_inference_codes,
restart_strategy: &mut self.satisfaction_solver.restart_strategy,
state: &mut self.satisfaction_solver.state,
nogood_propagator_handle: self.satisfaction_solver.nogood_propagator_handle,
rng: &mut self
.satisfaction_solver
.internal_parameters
.random_generator,
}
}
}
pub type DefaultBrancher =
AutonomousSearch<IndependentVariableValueBrancher<DomainId, RandomSelector, RandomSplitter>>;