use crate::{
clause,
encodings::CollectClauses,
instances::Cnf,
lit,
types::{Assignment, Cl, Clause, Lit, TernaryVal, Var},
};
use core::time::Duration;
use std::fmt;
use thiserror::Error;
pub mod external;
pub use external::Solver as ExternalSolver;
pub mod simulators;
pub trait Solve: Extend<Clause> + for<'a> Extend<&'a Clause> {
#[must_use]
fn signature(&self) -> &'static str;
fn reserve(&mut self, _max_var: Var) -> anyhow::Result<()> {
Ok(())
}
fn solve(&mut self) -> anyhow::Result<SolverResult>;
fn solution(&self, high_var: Var) -> anyhow::Result<Assignment> {
let mut assignment = Vec::new();
let len = high_var.idx32() + 1;
assignment.reserve(len as usize);
for idx in 0..len {
let lit = Lit::positive(idx);
assignment.push(self.lit_val(lit)?);
}
Ok(Assignment::from(assignment))
}
fn full_solution(&self) -> anyhow::Result<Assignment>
where
Self: SolveStats,
{
if let Some(high_var) = self.max_var() {
self.solution(high_var)
} else {
self.lit_val(lit![0])?;
Ok(Assignment::default())
}
}
fn var_val(&self, var: Var) -> anyhow::Result<TernaryVal> {
self.lit_val(var.pos_lit())
}
fn lit_val(&self, lit: Lit) -> anyhow::Result<TernaryVal>;
fn add_clause(&mut self, clause: Clause) -> anyhow::Result<()> {
self.add_clause_ref(&clause)
}
fn add_clause_ref<C>(&mut self, clause: &C) -> anyhow::Result<()>
where
C: AsRef<Cl> + ?Sized;
fn add_unit(&mut self, lit: Lit) -> anyhow::Result<()> {
self.add_clause(clause![lit])
}
fn add_binary(&mut self, lit1: Lit, lit2: Lit) -> anyhow::Result<()> {
self.add_clause(clause![lit1, lit2])
}
fn add_ternary(&mut self, lit1: Lit, lit2: Lit, lit3: Lit) -> anyhow::Result<()> {
self.add_clause(clause![lit1, lit2, lit3])
}
fn add_cnf(&mut self, cnf: Cnf) -> anyhow::Result<()> {
cnf.into_iter().try_for_each(|cl| self.add_clause(cl))
}
fn add_cnf_ref(&mut self, cnf: &Cnf) -> anyhow::Result<()> {
cnf.iter().try_for_each(|cl| self.add_clause_ref(cl))
}
}
pub trait SolveIncremental: Solve {
fn solve_assumps(&mut self, assumps: &[Lit]) -> anyhow::Result<SolverResult>;
fn core(&mut self) -> anyhow::Result<Vec<Lit>>;
}
pub trait Terminate<'term> {
fn attach_terminator<CB>(&mut self, cb: CB)
where
CB: FnMut() -> ControlSignal + 'term;
fn detach_terminator(&mut self);
}
pub trait Learn<'learn> {
fn attach_learner<CB>(&mut self, cb: CB, max_len: usize)
where
CB: FnMut(Clause) + 'learn;
fn detach_learner(&mut self);
}
pub trait Interrupt {
type Interrupter: InterruptSolver + Send + 'static;
#[must_use]
fn interrupter(&mut self) -> Self::Interrupter;
}
pub trait InterruptSolver: Sync {
fn interrupt(&self);
}
pub trait PhaseLit {
fn phase_lit(&mut self, lit: Lit) -> anyhow::Result<()>;
fn unphase_var(&mut self, var: Var) -> anyhow::Result<()>;
fn unphase_lit(&mut self, lit: Lit) -> anyhow::Result<()> {
self.unphase_var(lit.var())
}
}
pub trait FreezeVar {
fn freeze_var(&mut self, var: Var) -> anyhow::Result<()>;
fn melt_var(&mut self, var: Var) -> anyhow::Result<()>;
fn is_frozen(&mut self, var: Var) -> anyhow::Result<bool>;
}
pub trait FlipLit {
fn flip_lit(&mut self, lit: Lit) -> anyhow::Result<bool>;
fn is_flippable(&mut self, lit: Lit) -> anyhow::Result<bool>;
}
pub trait LimitConflicts {
fn limit_conflicts(&mut self, limit: Option<u32>) -> anyhow::Result<()>;
}
pub trait LimitDecisions {
fn limit_decisions(&mut self, limit: Option<u32>) -> anyhow::Result<()>;
}
pub trait LimitPropagations {
fn limit_propagations(&mut self, limit: Option<u32>) -> anyhow::Result<()>;
}
pub trait GetInternalStats {
#[must_use]
fn propagations(&self) -> usize;
#[must_use]
fn decisions(&self) -> usize;
#[must_use]
fn conflicts(&self) -> usize;
}
pub trait Propagate {
fn propagate(&mut self, assumps: &[Lit], phase_saving: bool)
-> anyhow::Result<PropagateResult>;
}
#[derive(Debug)]
#[must_use]
pub struct PropagateResult {
pub propagated: Vec<Lit>,
pub conflict: bool,
}
#[allow(dead_code)]
type TermCallbackPtr<'a> = Box<dyn FnMut() -> ControlSignal + 'a>;
#[allow(dead_code)]
type LearnCallbackPtr<'a> = Box<dyn FnMut(Clause) + 'a>;
#[allow(dead_code)]
type OptTermCallbackStore<'a> = Option<Box<TermCallbackPtr<'a>>>;
#[allow(dead_code)]
type OptLearnCallbackStore<'a> = Option<Box<LearnCallbackPtr<'a>>>;
#[derive(Clone, PartialEq, Default, Debug)]
pub struct SolverStats {
pub n_sat: usize,
pub n_unsat: usize,
pub n_terminated: usize,
pub n_clauses: usize,
pub max_var: Option<Var>,
pub avg_clause_len: f32,
pub cpu_solve_time: Duration,
}
pub trait SolveStats {
#[must_use]
fn stats(&self) -> SolverStats;
#[must_use]
fn n_sat_solves(&self) -> usize {
self.stats().n_sat
}
#[must_use]
fn n_unsat_solves(&self) -> usize {
self.stats().n_unsat
}
#[must_use]
fn n_terminated(&self) -> usize {
self.stats().n_terminated
}
#[must_use]
fn n_solves(&self) -> usize {
self.n_sat_solves() + self.n_unsat_solves() + self.n_terminated()
}
#[must_use]
fn n_clauses(&self) -> usize {
self.stats().n_clauses
}
#[must_use]
fn max_var(&self) -> Option<Var> {
self.stats().max_var
}
#[must_use]
fn n_vars(&self) -> usize {
match self.max_var() {
Some(var) => var.idx() + 1,
None => 0,
}
}
#[must_use]
fn avg_clause_len(&self) -> f32 {
self.stats().avg_clause_len
}
#[must_use]
fn cpu_solve_time(&self) -> Duration {
self.stats().cpu_solve_time
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SolverState {
Configuring,
Input,
Sat,
Unsat,
Unknown,
}
impl fmt::Display for SolverState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SolverState::Configuring => write!(f, "CONFIGURING"),
SolverState::Input => write!(f, "INPUT"),
SolverState::Sat => write!(f, "SAT"),
SolverState::Unsat => write!(f, "UNSAT"),
SolverState::Unknown => write!(f, "UNKNOWN"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SolverResult {
Sat,
Unsat,
Interrupted,
}
impl fmt::Display for SolverResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SolverResult::Sat => write!(f, "SAT"),
SolverResult::Unsat => write!(f, "UNSAT"),
SolverResult::Interrupted => write!(f, "Interrupted"),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum ControlSignal {
Continue,
Terminate,
}
#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub struct StateError {
pub required_state: SolverState,
pub actual_state: SolverState,
}
impl fmt::Display for StateError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"action requires {} state, but solver is in state {}",
self.required_state, self.actual_state
)
}
}
macro_rules! pass_oom_or_panic {
($result:expr) => {{
match $result {
Ok(res) => res,
Err(err) => match err.downcast::<crate::OutOfMemory>() {
Ok(oom) => return Err(oom),
Err(err) => panic!("unexpected error in clause collector: {err}"),
},
}
}};
}
impl<S: Solve + SolveStats> CollectClauses for S {
fn n_clauses(&self) -> usize {
self.n_clauses()
}
fn extend_clauses<T>(&mut self, cl_iter: T) -> Result<(), crate::OutOfMemory>
where
T: IntoIterator<Item = Clause>,
{
for cl in cl_iter {
pass_oom_or_panic!(self.add_clause(cl));
}
Ok(())
}
fn add_clause(&mut self, cl: Clause) -> Result<(), crate::OutOfMemory> {
pass_oom_or_panic!(self.add_clause(cl));
Ok(())
}
}
pub trait Initialize<T> {
fn init() -> T;
}
#[derive(Debug)]
pub struct DefaultInitializer;
impl<T: Default> Initialize<T> for DefaultInitializer {
#[inline]
fn init() -> T {
T::default()
}
}