use {
crate::callbacks::{Callbacks, ProgressStatus},
crate::clause::{
self, lbool, CRef, ClauseAllocator, ClauseRef, DeletePred, LSet, Lit, OccLists,
OccListsData, VMap, Var,
},
crate::interface::SolverInterface,
crate::intmap::{Comparator, Heap, HeapData},
crate::theory::Theory,
std::{cmp, f64, fmt, i32, mem},
};
#[cfg(feature = "logging")]
use crate::clause::display::Print;
pub struct Solver<Cb: Callbacks> {
model: Vec<lbool>,
conflict: LSet,
cb: Cb,
clauses: Vec<CRef>,
learnts: Vec<CRef>,
v: SolverV,
tmp_c_th: Vec<Lit>, tmp_c_add_cl: Vec<Lit>, }
struct VarState {
activity: VMap<f64>,
ass: VMap<lbool>,
vardata: VMap<VarData>,
var_inc: f64,
var_decay: f64,
trail: Vec<Lit>,
trail_lim: Vec<i32>,
}
struct SolverV {
opts: SolverOpts,
vars: VarState,
learntsize_adjust_start_confl: i32,
learntsize_adjust_inc: f64,
max_learnts: f64,
learntsize_adjust_confl: f64,
learntsize_adjust_cnt: i32,
remove_satisfied: bool,
solves: u64,
starts: u64,
decisions: u64,
rnd_decisions: u64,
propagations: u64,
conflicts: u64,
dec_vars: u64,
max_literals: u64,
tot_literals: u64,
num_clauses: u64,
num_learnts: u64,
clauses_literals: u64,
learnts_literals: u64,
polarity: VMap<bool>,
user_pol: VMap<lbool>,
decision: VMap<bool>,
watches_data: OccListsData<Lit, Watcher>,
order_heap_data: HeapData<Var>,
ok: bool,
cla_inc: f64,
qhead: i32,
simp_db_assigns: i32,
simp_db_props: i64,
progress_estimate: f64,
next_var: Var,
ca: ClauseAllocator,
free_vars: Vec<Var>,
assumptions: Vec<Lit>,
seen: VMap<Seen>,
minimize_stack: Vec<Lit>,
analyze_toclear: Vec<Lit>,
conflict_budget: i64,
propagation_budget: i64,
th_st: TheoryState,
}
struct TheoryState {
lemma_lits: Vec<Lit>,
lemma_offsets: Vec<(usize, usize)>, }
impl TheoryState {
fn new() -> Self {
TheoryState {
lemma_lits: vec![],
lemma_offsets: vec![],
}
}
fn clear(&mut self) {
self.lemma_lits.clear();
self.lemma_offsets.clear();
}
fn push_lemma(&mut self, lits: &[Lit]) {
let idx = self.lemma_lits.len();
self.lemma_offsets.push((idx, lits.len()));
self.lemma_lits.extend_from_slice(lits);
}
fn iter_lemmas(&mut self) -> impl Iterator<Item = &[Lit]> {
theory_st::LIter(self, 0)
}
}
mod theory_st {
use super::*;
pub(super) struct LIter<'a>(pub &'a TheoryState, pub usize);
impl<'a> Iterator for LIter<'a> {
type Item = &'a [Lit];
fn next(&mut self) -> Option<Self::Item> {
if self.1 >= self.0.lemma_offsets.len() {
None
} else {
let (offset, len) = self.0.lemma_offsets[self.1];
self.1 += 1;
let c = &self.0.lemma_lits[offset..offset + len];
Some(c)
}
}
}
}
pub struct SolverPrintDimacs<'a, Cb: Callbacks + 'a> {
s: &'a Solver<Cb>,
model: bool, }
mod dimacs {
use super::*;
impl<'a, Cb: Callbacks> fmt::Display for SolverPrintDimacs<'a, Cb> {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
if self.model {
write!(out, "v ")?;
for (i, &val) in self.s.model.iter().enumerate() {
if val == lbool::TRUE && i > 0 {
write!(out, "{} ", i + 1)?
} else if val == lbool::FALSE && i > 0 {
write!(out, "-{} ", i + 1)?
}
}
writeln!(out, "0")
} else {
Ok(())
}
}
}
}
impl<Cb: Callbacks> SolverInterface for Solver<Cb> {
fn new_var(&mut self, upol: lbool, dvar: bool) -> Var {
self.v.new_var(upol, dvar)
}
fn new_var_default(&mut self) -> Var {
self.new_var(lbool::UNDEF, true)
}
fn var_of_int(&mut self, v_idx: u32) -> Var {
while v_idx >= self.num_vars() {
self.new_var_default();
}
let var = Var::from_idx(v_idx);
debug_assert_eq!(var.idx(), v_idx);
var
}
fn add_clause_reuse(&mut self, clause: &mut Vec<Lit>) -> bool {
debug!("add toplevel clause {:?}", clause);
debug_assert_eq!(
self.v.decision_level(),
0,
"add clause at non-zero decision level"
);
clause.sort_unstable();
self.add_clause_(clause)
}
fn reset(&mut self) {
let new_v = SolverV::new(&self.v.opts);
self.v = new_v;
self.model.clear();
self.conflict.clear();
self.clauses.clear();
self.learnts.clear();
self.tmp_c_th.clear();
self.tmp_c_add_cl.clear();
}
fn solve_limited_preserving_trail_th<Th: Theory>(
&mut self,
th: &mut Th,
assumps: &[Lit],
) -> lbool {
self.v.assumptions.clear();
self.v.assumptions.extend_from_slice(assumps);
self.solve_internal(th)
}
fn pop_model<Th: Theory>(&mut self, th: &mut Th) {
self.cancel_until(th, 0)
}
fn raw_value_lit(&self, l: Lit) -> lbool {
self.v.value_lit(l)
}
#[inline(always)]
fn simplify_th<Th: Theory>(&mut self, th: &mut Th) -> bool {
self.simplify_internal(th)
}
fn value_var(&self, v: Var) -> lbool {
self.model
.get(v.idx() as usize)
.map_or(lbool::UNDEF, |&v| v)
}
fn value_lit(&self, v: Lit) -> lbool {
self.value_var(v.var()) ^ !v.sign()
}
fn get_model(&self) -> &[lbool] {
&self.model
}
fn is_ok(&self) -> bool {
self.v.ok
}
fn num_vars(&self) -> u32 {
self.v.num_vars()
}
fn num_clauses(&self) -> u64 {
self.v.num_clauses()
}
fn num_conflicts(&self) -> u64 {
self.v.num_conflicts()
}
fn num_propagations(&self) -> u64 {
self.v.num_props()
}
fn num_decisions(&self) -> u64 {
self.v.decisions
}
fn num_restarts(&self) -> u64 {
self.v.starts
}
fn value_lvl_0(&self, lit: Lit) -> lbool {
let mut res = self.v.value_lit(lit);
if self.v.level(lit.var()) != 0 {
res = lbool::UNDEF;
}
res
}
fn print_stats(&self) {
println!("c restarts : {}", self.v.starts);
println!("c conflicts : {:<12}", self.v.conflicts);
println!(
"c decisions : {:<12} ({:4.2} % random)",
self.v.decisions,
self.v.rnd_decisions as f32 * 100.0 / self.v.decisions as f32
);
println!("c propagations : {:<12}", self.v.propagations);
println!(
"c conflict literals : {:<12} ({:4.2} % deleted)",
self.v.tot_literals,
(self.v.max_literals - self.v.tot_literals) as f64 * 100.0 / self.v.max_literals as f64
);
}
fn unsat_core(&self) -> &[Lit] {
self.conflict.as_slice()
}
fn unsat_core_contains_lit(&self, lit: Lit) -> bool {
self.conflict.has(lit)
}
fn unsat_core_contains_var(&self, v: Var) -> bool {
let lit = Lit::new(v, true);
self.unsat_core_contains_lit(lit) || self.unsat_core_contains_lit(!lit)
}
fn proved_at_lvl_0(&self) -> &[Lit] {
self.v.vars.proved_at_lvl_0()
}
}
impl<Cb: Callbacks + Default> Default for Solver<Cb> {
fn default() -> Self {
Solver::new(SolverOpts::default(), Default::default())
}
}
impl<Cb: Callbacks> Solver<Cb> {
pub fn new(opts: SolverOpts, cb: Cb) -> Self {
Solver::new_with(opts, cb)
}
}
enum TheoryCall {
Partial,
Final,
}
struct ConflictAtLevel0;
impl<Cb: Callbacks> Solver<Cb> {
pub fn new_with(opts: SolverOpts, cb: Cb) -> Self {
assert!(opts.check());
Self {
model: vec![],
conflict: LSet::new(),
cb,
clauses: vec![],
learnts: vec![],
v: SolverV::new(&opts),
tmp_c_th: vec![],
tmp_c_add_cl: vec![],
}
}
fn new_decision_level<Th: Theory>(&mut self, th: &mut Th) {
trace!("new decision level {}", 1 + self.v.decision_level());
self.v.vars.new_decision_level();
th.create_level();
debug_assert_eq!(
self.v.decision_level() as usize,
th.n_levels(),
"same number of levels for theory and trail"
);
}
fn simplify_internal<Th>(&mut self, _: &mut Th) -> bool {
debug_assert_eq!(self.v.decision_level(), 0);
if !self.v.ok || self.v.propagate().is_some() {
self.v.ok = false;
return false;
}
if self.v.num_assigns() as i32 == self.v.simp_db_assigns || self.v.simp_db_props > 0 {
return true;
}
self.remove_satisfied(ClauseSetSelect::Learnt); if self.v.remove_satisfied {
self.remove_satisfied(ClauseSetSelect::Original); }
self.check_garbage();
self.v.rebuild_order_heap();
self.v.simp_db_assigns = self.v.num_assigns() as i32;
self.v.simp_db_props = (self.v.clauses_literals + self.v.learnts_literals) as i64;
true
}
fn search<Th: Theory>(
&mut self,
th: &mut Th,
nof_conflicts: i32,
tmp_learnt: &mut Vec<Lit>,
) -> lbool {
debug_assert!(self.v.ok);
let mut conflict_c = 0;
self.v.starts += 1;
'main: loop {
let confl = self.v.propagate();
if let Some(confl) = confl {
self.v.conflicts += 1;
conflict_c += 1;
if self.v.decision_level() == 0 {
return lbool::FALSE;
}
let learnt = self
.v
.analyze(Conflict::BCP(confl), &self.learnts, tmp_learnt, th);
self.add_learnt_and_backtrack(th, learnt, clause::Kind::Learnt);
self.v.vars.var_decay_activity();
self.v.cla_decay_activity();
self.v.learntsize_adjust_cnt -= 1;
if self.v.learntsize_adjust_cnt == 0 {
self.v.learntsize_adjust_confl *= self.v.learntsize_adjust_inc;
self.v.learntsize_adjust_cnt = self.v.learntsize_adjust_confl as i32;
self.v.max_learnts *= self.v.opts.learntsize_inc;
let trail_lim_head = self
.v
.vars
.trail_lim
.first()
.cloned()
.unwrap_or(self.v.vars.trail.len() as i32);
let v = &self.v;
self.cb.on_progress(|| ProgressStatus {
conflicts: v.conflicts as i32,
dec_vars: v.dec_vars as i32 - trail_lim_head,
n_clauses: v.num_clauses(),
n_clause_lits: v.clauses_literals as i32,
max_learnt: v.max_learnts as i32,
n_learnt: v.num_learnts(),
n_learnt_lits: v.learnts_literals as f64 / v.num_learnts as f64,
progress_estimate: v.progress_estimate() * 100.0,
});
}
} else {
if (nof_conflicts >= 0 && conflict_c >= nof_conflicts) || !self.within_budget() {
self.v.progress_estimate = self.v.progress_estimate();
self.cancel_until(th, 0);
return lbool::UNDEF;
}
if self.v.decision_level() == 0 && !self.simplify_th(th) {
return lbool::FALSE;
}
if self.learnts.len() as f64 - self.v.num_assigns() as f64 >= self.v.max_learnts {
self.reduce_db();
}
{
let th_res = self.call_theory(th, TheoryCall::Partial, tmp_learnt);
let Ok(th_res) = th_res else {
self.v.conflicts += 1;
return lbool::FALSE;
};
if th_res == lbool::UNDEF {
continue 'main;
} else if th_res == lbool::FALSE {
self.v.conflicts += 1;
conflict_c += 1;
continue 'main;
}
}
let mut next = Lit::UNDEF;
while (self.v.decision_level() as usize) < self.v.assumptions.len() {
let p = self.v.assumptions[self.v.decision_level() as usize];
if self.v.value_lit(p) == lbool::TRUE {
self.new_decision_level(th);
} else if self.v.value_lit(p) == lbool::FALSE {
let mut conflict = mem::replace(&mut self.conflict, LSet::new());
self.v.analyze_final(th, !p, &mut conflict);
self.cb.on_new_clause(&conflict, clause::Kind::Learnt);
self.conflict = conflict;
return lbool::FALSE;
} else {
next = p;
break;
}
}
if next == Lit::UNDEF {
next = self.v.pick_branch_lit();
if next == Lit::UNDEF {
let th_res = self.call_theory(th, TheoryCall::Final, tmp_learnt);
let Ok(th_res) = th_res else {
self.v.conflicts += 1;
return lbool::FALSE;
};
if th_res == lbool::TRUE {
return lbool::TRUE;
} else if th_res == lbool::UNDEF {
continue 'main;
} else {
assert_eq!(th_res, lbool::FALSE);
self.v.conflicts += 1;
conflict_c += 1;
continue 'main;
}
} else {
self.v.decisions += 1;
}
}
debug_assert_ne!(next, Lit::UNDEF);
self.new_decision_level(th);
debug!("pick-next {:?}", next);
self.v.vars.unchecked_enqueue(next, CRef::UNDEF);
}
}
}
fn add_learnt_and_backtrack<Th: Theory>(
&mut self,
th: &mut Th,
learnt: LearntClause,
k: clause::Kind,
) {
self.cb.on_new_clause(learnt.clause, k);
self.cancel_until(th, learnt.backtrack_lvl as u32);
if learnt.clause.len() == 1 {
self.v.vars.unchecked_enqueue(learnt.clause[0], CRef::UNDEF);
} else if learnt.clause.is_empty() {
self.v.ok = false;
} else {
let cr = self.v.ca.alloc_with_learnt(learnt.clause, true);
self.learnts.push(cr);
self.v.attach_clause(cr);
self.v.cla_bump_activity(&self.learnts, cr);
self.v.vars.unchecked_enqueue(learnt.clause[0], cr);
}
if learnt.add_orig {
debug!("add original lemma {:?}", learnt.orig_lits);
let mut c = vec![];
mem::swap(&mut c, &mut self.tmp_c_add_cl);
c.clear();
c.extend_from_slice(learnt.orig_lits);
self.add_clause_during_search(th, &mut c);
mem::swap(&mut c, &mut self.tmp_c_add_cl);
}
}
fn call_theory<Th: Theory>(
&mut self,
th: &mut Th,
k: TheoryCall,
tmp_learnt: &mut Vec<Lit>,
) -> Result<lbool, ConflictAtLevel0> {
let mut th_arg = {
let confl_cl = &mut self.tmp_c_th;
confl_cl.clear();
TheoryArg {
v: &mut self.v,
lits: confl_cl,
has_propagated: false,
conflict: TheoryConflict::Nil,
}
};
match k {
TheoryCall::Partial => th.partial_check(&mut th_arg),
TheoryCall::Final => th.final_check(&mut th_arg),
}
if let TheoryConflict::Clause { costly } = th_arg.conflict {
let mut local_confl_cl = vec![];
mem::swap(&mut local_confl_cl, th_arg.lits);
drop(th_arg);
debug!("theory conflict {:?} (costly: {})", local_confl_cl, costly);
self.v.sort_clause_lits(&mut local_confl_cl); local_confl_cl.dedup();
let r = Conflict::ThLemma {
lits: &local_confl_cl,
add: costly,
};
self.handle_theory_conflict(th, tmp_learnt, r)?;
mem::swap(&mut local_confl_cl, &mut self.tmp_c_th); Ok(lbool::FALSE)
} else if let TheoryConflict::Prop(p) = th_arg.conflict {
debug!("inconsistent theory propagation {:?}", p);
let r = Conflict::ThProp(p);
self.handle_theory_conflict(th, tmp_learnt, r)?;
Ok(lbool::FALSE)
} else {
debug_assert!(matches!(th_arg.conflict, TheoryConflict::Nil));
let mut has_propagated = th_arg.has_propagated;
let mut lemmas = vec![];
for c in self.v.th_st.iter_lemmas() {
trace!("add theory lemma {}", c.pp_dimacs());
has_propagated = true;
lemmas.push(c.into());
}
for mut c in lemmas {
self.add_clause_during_search(th, &mut c);
}
if has_propagated {
self.v.th_st.clear(); Ok(lbool::UNDEF)
} else {
Ok(lbool::TRUE) }
}
}
fn handle_theory_conflict<Th: Theory>(
&mut self,
th: &mut Th,
tmp_learnt: &mut Vec<Lit>,
r: Conflict,
) -> Result<(), ConflictAtLevel0> {
if self.v.decision_level() == 0 {
return Err(ConflictAtLevel0);
}
let learnt = self.v.analyze(r, &self.learnts, tmp_learnt, th);
self.add_learnt_and_backtrack(th, learnt, clause::Kind::Theory);
Ok(())
}
fn solve_internal<Th: Theory>(&mut self, th: &mut Th) -> lbool {
assert!(self.v.decision_level() == 0);
self.model.clear();
self.conflict.clear();
if !self.v.ok {
return lbool::FALSE;
}
self.v.solves += 1;
let mut tmp_learnt: Vec<Lit> = vec![];
self.v.max_learnts = self.num_clauses() as f64 * self.v.opts.learntsize_factor;
if self.v.max_learnts < self.v.opts.min_learnts_lim as f64 {
self.v.max_learnts = self.v.opts.min_learnts_lim as f64;
}
self.v.learntsize_adjust_confl = self.v.learntsize_adjust_start_confl as f64;
self.v.learntsize_adjust_cnt = self.v.learntsize_adjust_confl as i32;
let mut status;
info!("search.start");
self.cb.on_start();
let mut curr_restarts: i32 = 0;
loop {
let rest_base = if self.v.opts.luby_restart {
utils::luby(self.v.opts.restart_inc, curr_restarts)
} else {
f64::powi(self.v.opts.restart_inc, curr_restarts)
};
let nof_clauses = (rest_base * self.v.opts.restart_first as f64) as i32;
status = self.search(th, nof_clauses, &mut tmp_learnt);
if !self.within_budget() {
break;
}
if status != lbool::UNDEF {
break;
} else {
info!("search.restart({})", curr_restarts);
curr_restarts += 1;
self.cb.on_restart();
}
}
self.cb.on_result(status);
if status == lbool::TRUE {
let num_vars = self.num_vars();
self.model.resize(num_vars as usize, lbool::UNDEF);
for i in 0..num_vars {
self.model[i as usize] = self.v.value(Var::from_idx(i));
}
} else if status == lbool::FALSE && self.conflict.len() == 0 {
self.v.ok = false;
}
debug!("res: {:?}", status);
trace!("proved at lvl 0: {:?}", self.v.vars.proved_at_lvl_0());
status
}
fn reduce_db(&mut self) {
let extra_lim = self.v.cla_inc / self.learnts.len() as f64;
debug!("reduce_db.start");
{
let ca = &self.v.ca;
self.learnts.sort_unstable_by(|&x, &y| {
let x = ca.get_ref(x);
let y = ca.get_ref(y);
debug_assert!(x.learnt());
debug_assert!(y.learnt());
Ord::cmp(&(x.size() <= 2), &(y.size() <= 2)).then(
PartialOrd::partial_cmp(&x.activity(), &y.activity()).expect("NaN activity"),
)
});
}
let mut j = 0;
for i in 0..self.learnts.len() {
let cr = self.learnts[i];
let cond = {
let c = self.v.ca.get_ref(cr);
c.size() > 2
&& !self.v.locked(c)
&& (i < self.learnts.len() / 2 || (c.activity() as f64) < extra_lim)
};
if cond {
self.v.remove_clause(cr);
self.cb.on_delete_clause(self.v.ca.get_ref(cr).lits());
} else {
self.learnts[j] = cr;
j += 1;
}
}
let _deleted = self.learnts.len() - j;
self.learnts.resize(j, CRef::UNDEF);
debug!("reduce_db.done (deleted {})", _deleted);
self.check_garbage();
}
fn remove_satisfied(&mut self, which: ClauseSetSelect) {
assert_eq!(self.v.decision_level(), 0);
let cs: &mut Vec<CRef> = match which {
ClauseSetSelect::Learnt => &mut self.learnts,
ClauseSetSelect::Original => &mut self.clauses,
};
let self_v = &mut self.v;
cs.retain(|&cr| {
let satisfied = self_v.satisfied(self_v.ca.get_ref(cr));
if satisfied {
self_v.remove_clause(cr);
debug!("remove satisfied clause {:?}", self_v.ca.get_ref(cr).lits());
} else {
let amount_shaved = {
let mut c = self_v.ca.get_mut(cr);
debug_assert_eq!(self_v.vars.value_lit(c[0]), lbool::UNDEF);
debug_assert_eq!(self_v.vars.value_lit(c[1]), lbool::UNDEF);
let mut k = 2;
let orig_size = c.size();
let mut end = c.size();
while k < end {
if self_v.vars.value_lit(c[k]) == lbool::FALSE {
debug_assert!(self_v.vars.level(c[k].var()) == 0);
end -= 1;
c[k] = c[end];
} else {
k += 1;
}
}
c.shrink(end);
orig_size - end
};
self_v.ca.free_amount(amount_shaved);
}
!satisfied
});
}
fn cancel_until<Th: Theory>(&mut self, th: &mut Th, level: u32) {
let dl = self.v.decision_level();
if dl > level {
let n_th_levels = (dl - level) as usize;
trace!(
"solver.cancel-until {} (pop {} theory levels)",
level,
n_th_levels
);
self.v.cancel_until(level);
th.pop_levels(n_th_levels); }
}
fn garbage_collect(&mut self) {
let mut to = ClauseAllocator::with_start_cap(self.v.ca.len() - self.v.ca.wasted());
self.v
.reloc_all(&mut self.learnts, &mut self.clauses, &mut to);
self.cb.on_gc(
(self.v.ca.len() * ClauseAllocator::UNIT_SIZE) as usize,
(to.len() * ClauseAllocator::UNIT_SIZE) as usize,
);
self.v.ca = to;
}
fn check_garbage(&mut self) {
if self.v.ca.wasted() as f64 > self.v.ca.len() as f64 * self.v.opts.garbage_frac {
self.garbage_collect();
}
}
pub fn cb_mut(&mut self) -> &mut Cb {
&mut self.cb
}
pub fn cb(&self) -> &Cb {
&self.cb
}
pub fn dimacs_model(&self) -> SolverPrintDimacs<Cb> {
SolverPrintDimacs {
s: self,
model: true,
}
}
fn within_budget(&self) -> bool {
(self.v.conflict_budget < 0 || self.v.conflicts < self.v.conflict_budget as u64)
&& (self.v.propagation_budget < 0
|| self.v.propagations < self.v.propagation_budget as u64)
&& !self.cb.stop()
}
fn add_clause_(&mut self, clause: &mut Vec<Lit>) -> bool {
if !self.v.ok {
return false;
}
let mut last_lit = Lit::UNDEF;
let mut j = 0;
for i in 0..clause.len() {
let lit_i = clause[i];
let value = self.v.value_lit(lit_i);
let lvl = self.v.level_lit(lit_i);
if (value == lbool::TRUE && lvl == 0) || lit_i == !last_lit {
return true; } else if !(value == lbool::FALSE && lvl == 0) && lit_i != last_lit {
last_lit = lit_i;
clause[j] = lit_i;
j += 1;
}
}
clause.resize(j, Lit::UNDEF);
if clause.is_empty() {
self.v.ok = false;
return false;
} else if clause.len() == 1 {
self.v.vars.unchecked_enqueue(clause[0], CRef::UNDEF);
} else {
let cr = self.v.ca.alloc_with_learnt(clause, false);
self.clauses.push(cr);
self.v.attach_clause(cr);
}
true
}
fn add_clause_during_search<Th: Theory>(&mut self, th: &mut Th, clause: &mut Vec<Lit>) -> bool {
debug!("add internal clause {:?}", clause);
if !self.v.ok {
return false;
}
if clause.len() == 1 {
self.cancel_until(th, 0); }
self.v.sort_clause_lits(clause);
self.add_clause_(clause)
}
}
enum TheoryConflict {
Nil,
Clause { costly: bool },
Prop(Lit),
}
pub struct TheoryArg<'a> {
v: &'a mut SolverV,
lits: &'a mut Vec<Lit>,
has_propagated: bool,
conflict: TheoryConflict,
}
struct LearntClause<'a> {
orig_lits: &'a [Lit], add_orig: bool, clause: &'a [Lit], backtrack_lvl: i32, }
#[derive(Clone, Copy, Debug)]
enum Conflict<'a> {
BCP(CRef), ThLemma { lits: &'a [Lit], add: bool },
ThProp(Lit), }
#[derive(Clone, Copy, Debug)]
enum ResolveWith<'a> {
Init(Conflict<'a>), Resolve(Lit, CRef), }
impl SolverV {
#[inline(always)]
pub fn num_assigns(&self) -> u32 {
self.vars.num_assigns()
}
#[inline(always)]
fn num_vars(&self) -> u32 {
self.next_var.idx()
}
fn num_clauses(&self) -> u64 {
self.num_clauses
}
fn num_conflicts(&self) -> u64 {
self.conflicts
}
fn num_props(&self) -> u64 {
self.propagations
}
fn num_learnts(&self) -> u64 {
self.num_learnts
}
#[inline(always)]
pub fn level(&self, x: Var) -> i32 {
self.vars.level(x)
}
#[inline(always)]
pub fn level_lit(&self, x: Lit) -> i32 {
self.level(x.var())
}
#[inline(always)]
pub fn value(&self, x: Var) -> lbool {
self.vars.value(x)
}
#[inline(always)]
pub fn value_lit(&self, x: Lit) -> lbool {
self.vars.value_lit(x)
}
fn order_heap(&mut self) -> Heap<Var, VarOrder> {
self.order_heap_data.promote(VarOrder {
activity: &self.vars.activity,
})
}
fn set_decision_var(&mut self, v: Var, b: bool) {
if b && !self.decision[v] {
self.dec_vars += 1;
} else if !b && self.decision[v] {
self.dec_vars -= 1;
}
self.decision[v] = b;
self.insert_var_order(v);
}
fn insert_var_order(&mut self, x: Var) {
if !self.order_heap().in_heap(x) && self.decision[x] {
self.order_heap().insert(x);
}
}
fn cla_decay_activity(&mut self) {
self.cla_inc *= 1.0 / self.opts.clause_decay;
}
fn cla_bump_activity(&mut self, learnts: &[CRef], cr: CRef) {
let new_activity = {
let mut c = self.ca.get_mut(cr);
let r = c.activity() + self.cla_inc as f32;
c.set_activity(r);
r
};
if new_activity > 1e20 {
for &learnt in learnts.iter() {
let mut c = self.ca.get_mut(learnt);
let r = c.activity() * 1e-20;
c.set_activity(r);
}
self.cla_inc *= 1e-20;
}
}
fn pick_branch_lit(&mut self) -> Lit {
let mut next = Var::UNDEF;
if utils::drand(&mut self.opts.random_seed) < self.opts.random_var_freq
&& !self.order_heap().is_empty()
{
let idx_tmp = utils::irand(
&mut self.opts.random_seed,
self.order_heap_data.len() as i32,
) as usize;
next = self.order_heap_data[idx_tmp];
if self.value(next) == lbool::UNDEF && self.decision[next] {
self.rnd_decisions += 1;
}
}
while next == Var::UNDEF || self.value(next) != lbool::UNDEF || !self.decision[next] {
let mut order_heap = self.order_heap();
if order_heap.is_empty() {
next = Var::UNDEF;
break;
} else {
next = order_heap.remove_min();
}
}
if next == Var::UNDEF {
Lit::UNDEF
} else if self.user_pol[next] != lbool::UNDEF {
Lit::new(next, self.user_pol[next] == lbool::TRUE)
} else if self.opts.rnd_pol {
Lit::new(next, utils::drand(&mut self.opts.random_seed) < 0.5)
} else {
Lit::new(next, self.polarity[next])
}
}
fn watches(&mut self) -> OccLists<Lit, Watcher, WatcherDeleted> {
self.watches_data.promote(WatcherDeleted { ca: &self.ca })
}
fn new_var(&mut self, upol: lbool, dvar: bool) -> Var {
let v = self.free_vars.pop().unwrap_or_else(|| {
let v = self.next_var;
self.next_var = Var::from_idx(self.next_var.idx() + 1);
v
});
self.watches().init(Lit::new(v, false));
self.watches().init(Lit::new(v, true));
self.vars.ass.insert_default(v, lbool::UNDEF);
self.vars
.vardata
.insert_default(v, VarData::new(CRef::UNDEF, 0));
if self.opts.rnd_init_act {
self.vars
.activity
.insert_default(v, utils::drand(&mut self.opts.random_seed) * 0.00001);
} else {
self.vars.activity.insert_default(v, 0.0);
}
self.seen.insert_default(v, Seen::UNDEF);
self.polarity.insert_default(v, false);
self.user_pol.insert_default(v, upol);
self.decision.reserve_default(v);
let len = self.vars.trail.len();
if v.idx() as usize > len {
self.vars.trail.reserve(v.idx() as usize + 1 - len);
}
self.set_decision_var(v, dvar);
v
}
fn analyze<'a, Th: Theory>(
&mut self,
orig: Conflict<'a>,
learnts: &[CRef],
out_learnt: &'a mut Vec<Lit>,
th: &mut Th,
) -> LearntClause<'a> {
out_learnt.clear();
debug!("analyze.start {:?}", orig);
let conflict_level = match orig {
Conflict::BCP(_) | Conflict::ThProp(_) => {
self.decision_level() as i32 }
Conflict::ThLemma { lits, .. } => {
debug_assert!(lits.iter().all(|&p| self.value_lit(p) == lbool::FALSE));
debug_assert!(lits.len() >= 1, "theory lemma should have at least 1 lit");
let lvl = lits
.iter()
.map(|&lit| self.level_lit(lit))
.max()
.unwrap_or(0);
if lits.len() == 1 {
trace!("analyze: learn unit clause {:?} itself", lits);
out_learnt.extend_from_slice(lits);
return LearntClause {
clause: lits,
backtrack_lvl: 0,
orig_lits: lits,
add_orig: false,
};
} else if lvl == 0 {
trace!("analyze: conflict level 0, learn empty clause");
return LearntClause {
clause: &[],
backtrack_lvl: 0,
orig_lits: lits,
add_orig: false,
};
}
lvl
}
};
let mut cur_clause = ResolveWith::Init(orig);
let mut path_c = 0;
#[allow(unused)]
let mut p = Lit::UNDEF;
out_learnt.push(Lit::UNDEF);
let mut index = self.vars.trail.len();
loop {
let mut lits_are_true = false;
let lits = match cur_clause {
ResolveWith::Init(Conflict::ThLemma { lits, .. }) => lits,
ResolveWith::Init(Conflict::ThProp(lit)) => {
let lits = &mut self.th_st.lemma_lits;
lits.clear();
lits.push(lit);
lits.extend(th.explain_propagation(lit).iter().map(|&a| !a));
debug_assert!({
let vars = &self.vars;
lits.iter().all(|&q| vars.value_lit(q) == lbool::FALSE)
});
&self.th_st.lemma_lits
}
ResolveWith::Init(Conflict::BCP(cr)) => {
let mut c = self.ca.get_ref(cr);
if c.learnt() {
self.cla_bump_activity(learnts, cr);
c = self.ca.get_ref(cr); }
c.lits()
}
ResolveWith::Resolve(lit, cr) if cr == CRef::SPECIAL => {
lits_are_true = true;
let lits = th.explain_propagation(lit);
debug_assert!(lits.iter().all(|&q| self.value_lit(q) == lbool::TRUE));
lits
}
ResolveWith::Resolve(_lit, cr) if cr == CRef::UNDEF => {
panic!(
"analyze: reached a decision literal {:?}, path_c={}",
_lit, path_c
);
}
ResolveWith::Resolve(lit, cr) => {
let mut c = self.ca.get_ref(cr);
if c.learnt() {
self.cla_bump_activity(learnts, cr);
c = self.ca.get_ref(cr); }
let lits = c.lits();
debug_assert_eq!(lit.var(), lits[0].var());
&lits[1..]
}
};
trace!(
"analyze.resolve-with {:?} ((p: {:?}, path_c: {}, from {:?})",
lits,
p,
path_c,
cur_clause
);
for &q in lits {
let q = if lits_are_true { !q } else { q }; let lvl = self.vars.level(q.var());
assert!(lvl <= conflict_level);
if !self.seen[q.var()].is_seen() && lvl > 0 {
self.vars
.var_bump_activity(&mut self.order_heap_data, q.var());
self.seen[q.var()] = Seen::SOURCE;
if lvl == conflict_level {
path_c += 1;
} else {
out_learnt.push(q); }
} else if self.seen[q.var()] == Seen::REMOVABLE {
panic!(
"possible cycle in conflict graph between {:?} and {:?}",
p, q
);
}
}
while !self.seen[self.vars.trail[index - 1].var()].is_seen() {
debug_assert!(self.vars.level(self.vars.trail[index - 1].var()) >= conflict_level);
index -= 1;
}
p = self.vars.trail[index - 1];
index -= 1;
cur_clause = ResolveWith::Resolve(p, self.vars.reason(p.var()));
self.seen[p.var()] = Seen::REMOVABLE;
path_c -= 1;
if path_c <= 0 {
break;
}
}
index = self.vars.trail.len() - 1;
loop {
let q = self.vars.trail[index];
if self.seen[q.var()] == Seen::REMOVABLE {
self.seen[q.var()] = Seen::UNDEF;
}
if q == p {
break;
}
index -= 1;
}
assert_ne!(p, Lit::UNDEF);
debug_assert!(self.value_lit(p) == lbool::TRUE);
out_learnt[0] = !p;
trace!("analyze-learnt: {:?} (before minimization)", &out_learnt);
self.max_literals += out_learnt.len() as u64;
self.minimize_conflict(out_learnt);
let btlevel = if out_learnt.len() == 1 {
0
} else {
let mut max_i = 1;
let mut max_level = self.level(out_learnt[max_i].var());
for i in 2..out_learnt.len() {
let level = self.level(out_learnt[i].var());
if level > max_level {
max_i = i;
max_level = level;
}
}
out_learnt.swap(max_i, 1);
self.level_lit(out_learnt[1])
};
for &lit in &self.analyze_toclear {
self.seen[lit.var()] = Seen::UNDEF; }
debug_assert!(out_learnt
.iter()
.all(|&l| self.value_lit(l) == lbool::FALSE));
let (orig_lits, add_orig) = match orig {
Conflict::ThLemma { lits, add } => {
let not_eq = lits != out_learnt.as_slice();
(lits, add && not_eq)
}
Conflict::ThProp(_) | Conflict::BCP(_) => (&[][..], false),
};
LearntClause {
orig_lits,
add_orig,
backtrack_lvl: btlevel,
clause: out_learnt,
}
}
#[inline]
fn abstract_level(&self, v: Var) -> u32 {
1 << (self.level(v) & 31)
}
fn minimize_conflict(&mut self, out_learnt: &mut Vec<Lit>) {
self.analyze_toclear.clear();
self.analyze_toclear.extend_from_slice(out_learnt);
let new_size = if self.opts.ccmin_mode == 2 {
let mut abstract_levels = 0;
for a in out_learnt[1..].iter() {
abstract_levels |= self.abstract_level(a.var())
}
let mut j = 1;
for i in 1..out_learnt.len() {
let lit = out_learnt[i];
if self.reason(lit.var()) == CRef::UNDEF
|| !self.lit_redundant(lit, abstract_levels)
{
out_learnt[j] = lit;
j += 1;
}
}
j
} else if self.opts.ccmin_mode == 1 {
let mut j = 1;
for i in 1..out_learnt.len() {
let lit = out_learnt[i];
let x = lit.var();
let reason = self.reason(x);
let mut retain = true;
if reason == CRef::UNDEF || reason == CRef::SPECIAL {
debug_assert!(self.level(x) > 0);
retain = true;
} else {
let c = self.ca.get_ref(reason);
for k in 1..c.size() {
let v = c[k].var();
if !self.seen[v].is_seen() && self.level(v) > 0 {
retain = true;
break;
}
}
}
if retain {
out_learnt[j] = lit;
j += 1;
}
}
j
} else {
out_learnt.len()
};
self.tot_literals += new_size as u64;
debug_assert!(new_size <= out_learnt.len());
out_learnt.truncate(new_size);
}
fn analyze_final<Th: Theory>(&mut self, th: &mut Th, p: Lit, out_conflict: &mut LSet) {
out_conflict.clear();
out_conflict.insert(p);
debug!("analyze_final lit={:?}", p);
if self.decision_level() == 0 {
return; }
self.seen[p.var()] = Seen::SOURCE;
for &lit in self.vars.trail[self.vars.trail_lim[0] as usize..]
.iter()
.rev()
{
let x = lit.var();
if self.seen[x].is_seen() {
let reason = self.reason(x);
if reason == CRef::UNDEF {
debug_assert!(self.level(x) > 0);
out_conflict.insert(!lit);
} else if reason == CRef::SPECIAL {
let lits = th.explain_propagation(lit);
for &p in lits {
if self.vars.level(p.var()) > 0 {
self.seen[p.var()] = Seen::SOURCE;
}
}
} else {
let c = self.ca.get_mut(reason);
for j in 1..c.size() {
if self.vars.level(c[j].var()) > 0 {
self.seen[c[j].var()] = Seen::SOURCE;
}
}
}
self.seen[x] = Seen::UNDEF;
}
}
self.seen[p.var()] = Seen::UNDEF;
debug_assert!(self.seen.iter().all(|(_, &s)| s == Seen::UNDEF));
}
fn lit_redundant(&mut self, p: Lit, abstract_levels: u32) -> bool {
self.minimize_stack.clear();
self.minimize_stack.push(p);
let top = self.analyze_toclear.len();
while self.minimize_stack.len() > 0 {
let q = *self.minimize_stack.last().unwrap();
let cr = self.reason(q.var());
debug_assert_ne!(cr, CRef::UNDEF);
self.minimize_stack.pop();
if cr == CRef::SPECIAL {
if self.vars.level(q.var()) == 0 {
continue; } else {
for a in self.analyze_toclear[top..].iter() {
self.seen[a.var()] = Seen::UNDEF;
}
self.analyze_toclear.resize(top, Lit::UNDEF);
return false;
}
}
let c = self.ca.get_ref(cr);
for &l in c.lits()[1..].iter() {
if self.vars.level(l.var()) == 0 || self.seen[l.var()] == Seen::SOURCE {
continue;
}
if self.reason(l.var()) != CRef::UNDEF
&& (self.abstract_level(l.var()) & abstract_levels) != 0
{
self.seen[l.var()] = Seen::SOURCE;
self.minimize_stack.push(l);
self.analyze_toclear.push(l);
} else {
for a in self.analyze_toclear[top..].iter() {
self.seen[a.var()] = Seen::UNDEF;
}
self.analyze_toclear.resize(top, Lit::UNDEF);
return false;
}
}
}
true
}
fn propagate(&mut self) -> Option<CRef> {
let mut confl = None;
let mut num_props: u32 = 0;
while (self.qhead as usize) < self.vars.trail.len() {
let p = self.vars.trail[self.qhead as usize];
self.qhead += 1;
let watches_data_ptr: *mut OccListsData<_, _> = &mut self.watches_data;
let ws = self
.watches_data
.lookup_mut_pred(p, &WatcherDeleted { ca: &self.ca });
let mut i: usize = 0;
let mut j: usize = 0;
let end: usize = ws.len();
num_props += 1;
'clauses: while i < end {
let blocker = ws[i].blocker;
if self.vars.value_lit(blocker) == lbool::TRUE {
ws[j] = ws[i];
j += 1;
i += 1;
continue;
}
let cr = ws[i].cref;
let mut c = self.ca.get_mut(cr);
let false_lit = !p;
if c[0] == false_lit {
c[0] = c[1];
c[1] = false_lit;
}
debug_assert_eq!(c[1], false_lit);
i += 1;
let first = c[0];
let w = Watcher::new(cr, first);
if first != blocker && self.vars.value_lit(first) == lbool::TRUE {
ws[j] = w;
j += 1;
continue;
}
for k in 2..c.size() {
if self.vars.value_lit(c[k]) != lbool::FALSE {
c[1] = c[k];
c[k] = false_lit;
debug_assert_ne!(!c[1], p);
unsafe { &mut (*watches_data_ptr)[!c[1]] }.push(w);
continue 'clauses;
}
}
ws[j] = w;
j += 1;
if self.vars.value_lit(first) == lbool::FALSE {
confl = Some(cr);
self.qhead = self.vars.trail.len() as i32;
while i < end {
ws[j] = ws[i];
j += 1;
i += 1;
}
} else {
self.vars.unchecked_enqueue(first, cr);
}
}
let dummy = Watcher::DUMMY;
ws.resize(j, dummy);
}
self.propagations += num_props as u64;
self.simp_db_props -= num_props as i64;
confl
}
fn rebuild_order_heap(&mut self) {
let mut vs = vec![];
for v in (0..self.num_vars()).map(Var::from_idx) {
if self.decision[v] && self.value(v) == lbool::UNDEF {
vs.push(v);
}
}
self.order_heap().build(&vs);
}
fn sort_clause_lits(&self, clause: &mut [Lit]) {
clause.sort_unstable_by(|&lit1, &lit2| {
let has_val1 = self.value_lit(lit1) != lbool::UNDEF;
let has_val2 = self.value_lit(lit2) != lbool::UNDEF;
if has_val1 && !has_val2 {
return cmp::Ordering::Greater;
}
if !has_val1 && has_val2 {
return cmp::Ordering::Less;
}
let lvl1 = self.level_lit(lit1);
let lvl2 = self.level_lit(lit2);
if lvl1 != lvl2 {
lvl2.cmp(&lvl1) } else {
lit1.cmp(&lit2) }
});
debug_assert!(
self.value_lit(clause[0]) == lbool::UNDEF || {
let lvl0 = self.level_lit(clause[0]);
clause[1..].iter().all(|&lit2| self.level_lit(lit2) <= lvl0)
}
);
}
fn reloc_all(
&mut self,
learnts: &mut Vec<CRef>,
clauses: &mut Vec<CRef>,
to: &mut ClauseAllocator,
) {
macro_rules! is_removed {
($ca:expr, $cr:expr) => {
$ca.get_ref($cr).mark() == 1
};
}
self.watches().clean_all();
for v in (0..self.num_vars()).map(Var::from_idx) {
for s in 0..2 {
let p = Lit::new(v, s != 0);
for watch in &mut self.watches_data[p] {
self.ca.reloc(&mut watch.cref, to);
}
}
}
for &lit in &self.vars.trail {
let v = lit.var();
let reason = self.reason(v);
if reason != CRef::UNDEF && reason != CRef::SPECIAL {
let cond = {
let c = self.ca.get_ref(reason);
c.reloced() || self.locked(c)
};
if cond {
debug_assert!(!is_removed!(self.ca, reason));
self.ca.reloc(&mut self.vars.vardata[v].reason, to);
}
}
}
{
let mut j = 0;
for i in 0..learnts.len() {
let mut cr = learnts[i];
if !is_removed!(self.ca, cr) {
self.ca.reloc(&mut cr, to);
learnts[j] = cr;
j += 1;
}
}
learnts.resize(j, CRef::UNDEF);
}
{
let mut j = 0;
for i in 0..clauses.len() {
let mut cr = clauses[i];
if !is_removed!(self.ca, cr) {
self.ca.reloc(&mut cr, to);
clauses[j] = cr;
j += 1;
}
}
clauses.resize(j, CRef::UNDEF);
}
}
fn attach_clause(&mut self, cr: CRef) {
let (c0, c1, learnt, size) = {
let c = self.ca.get_ref(cr);
debug_assert!(c.size() > 1);
(c[0], c[1], c.learnt(), c.size())
};
self.watches()[!c0].push(Watcher::new(cr, c1));
self.watches()[!c1].push(Watcher::new(cr, c0));
if learnt {
self.num_learnts += 1;
self.learnts_literals += size as u64;
} else {
self.num_clauses += 1;
self.clauses_literals += size as u64;
}
}
fn cancel_until(&mut self, level: u32) {
debug_assert!(self.decision_level() > level);
let trail_lim_last = *self.vars.trail_lim.last().expect("trail_lim is empty") as usize;
let trail_lim_level = self.vars.trail_lim[level as usize] as usize;
for c in (trail_lim_level..self.vars.trail.len()).rev() {
let x = self.vars.trail[c].var();
self.vars.ass[x] = lbool::UNDEF;
if self.opts.phase_saving > 1 || (self.opts.phase_saving == 1 && c > trail_lim_last) {
self.polarity[x] = self.vars.trail[c].sign();
}
self.insert_var_order(x);
}
self.qhead = trail_lim_level as i32;
self.vars.trail.resize(trail_lim_level, Lit::UNDEF);
self.th_st.clear();
self.vars.trail_lim.resize(level as usize, 0);
}
fn detach_clause(&mut self, cr: CRef, strict: bool) {
let (c0, c1, csize, clearnt) = {
let c = self.ca.get_ref(cr);
(c[0], c[1], c.size(), c.learnt())
};
debug_assert!(csize > 1);
let mut watches = self.watches_data.promote(WatcherDeleted { ca: &self.ca });
if strict {
let pos = watches[!c0]
.iter()
.position(|x| x == &Watcher::new(cr, c1))
.expect("Watcher not found");
watches[!c0].remove(pos);
let pos = watches[!c1]
.iter()
.position(|x| x == &Watcher::new(cr, c0))
.expect("Watcher not found");
watches[!c1].remove(pos);
} else {
watches.smudge(!c0);
watches.smudge(!c1);
}
if clearnt {
self.num_learnts -= 1;
self.learnts_literals -= csize as u64;
} else {
self.num_clauses -= 1;
self.clauses_literals -= csize as u64;
}
}
fn remove_clause(&mut self, cr: CRef) {
self.detach_clause(cr, false);
{
let c = self.ca.get_ref(cr);
if self.locked(c) {
self.vars.vardata[c[0].var()].reason = CRef::UNDEF;
}
}
self.ca.get_mut(cr).set_mark(1); self.ca.free(cr);
}
pub fn satisfied(&self, c: ClauseRef) -> bool {
c.iter().any(|&lit| self.value_lit(lit) == lbool::TRUE)
}
#[inline(always)]
pub fn decision_level(&self) -> u32 {
self.vars.decision_level()
}
#[inline(always)]
fn reason(&self, x: Var) -> CRef {
self.vars.reason(x)
}
fn locked(&self, c: ClauseRef) -> bool {
let reason = self.reason(c[0].var());
self.value_lit(c[0]) == lbool::TRUE
&& reason != CRef::UNDEF
&& reason != CRef::SPECIAL
&& self.ca.get_ref(reason) == c
}
fn progress_estimate(&self) -> f64 {
let mut progress = 0.0;
let f = 1.0 / self.num_vars() as f64;
for i in 0..self.decision_level() + 1 {
let beg: i32 = if i == 0 {
0
} else {
self.vars.trail_lim[i as usize - 1]
};
let end: i32 = if i == self.decision_level() {
self.vars.trail.len() as i32
} else {
self.vars.trail_lim[i as usize]
};
progress += f64::powi(f, i as i32) * (end - beg) as f64;
}
progress / self.num_vars() as f64
}
fn new(opts: &SolverOpts) -> Self {
Self {
opts: opts.clone(),
vars: VarState::new(opts),
num_clauses: 0,
num_learnts: 0,
clauses_literals: 0,
learnts_literals: 0,
learntsize_adjust_start_confl: 100,
learntsize_adjust_inc: 1.5,
solves: 0,
starts: 0,
decisions: 0,
rnd_decisions: 0,
propagations: 0,
conflicts: 0,
dec_vars: 0,
max_literals: 0,
tot_literals: 0,
polarity: VMap::new(),
user_pol: VMap::new(),
decision: VMap::new(),
watches_data: OccListsData::new(),
order_heap_data: HeapData::new(),
ok: true,
cla_inc: 1.0,
qhead: 0,
simp_db_assigns: -1,
simp_db_props: 0,
progress_estimate: 0.0,
remove_satisfied: false, next_var: Var::from_idx(0),
ca: ClauseAllocator::new(),
free_vars: vec![],
assumptions: vec![],
seen: VMap::new(),
minimize_stack: vec![],
analyze_toclear: vec![],
max_learnts: 0.0,
learntsize_adjust_confl: 0.0,
learntsize_adjust_cnt: 0,
conflict_budget: -1,
propagation_budget: -1,
th_st: TheoryState::new(),
}
}
}
impl VarState {
fn new(opts: &SolverOpts) -> Self {
Self {
ass: VMap::new(),
vardata: VMap::new(),
activity: VMap::new(),
var_inc: 1.0,
var_decay: opts.var_decay,
trail: vec![],
trail_lim: vec![],
}
}
#[inline(always)]
pub fn num_assigns(&self) -> u32 {
self.trail.len() as u32
}
fn new_decision_level(&mut self) {
let lvl = self.trail.len() as i32;
self.trail_lim.push(lvl);
}
fn proved_at_lvl_0(&self) -> &[Lit] {
let end = self
.trail_lim
.get(0)
.map_or(self.trail.len(), |&x| x as usize);
&self.trail[..end]
}
#[inline(always)]
pub fn value(&self, x: Var) -> lbool {
self.ass[x]
}
#[inline(always)]
fn value_lit(&self, x: Lit) -> lbool {
self.ass[x.var()] ^ !x.sign()
}
#[inline(always)]
fn level(&self, x: Var) -> i32 {
self.vardata[x].level
}
#[inline(always)]
fn reason(&self, x: Var) -> CRef {
self.vardata[x].reason
}
fn var_decay_activity(&mut self) {
self.var_inc *= 1.0 / self.var_decay;
}
#[inline(always)]
pub fn decision_level(&self) -> u32 {
self.trail_lim.len() as u32
}
fn unchecked_enqueue(&mut self, p: Lit, from: CRef) {
debug_assert_eq!(
self.value_lit(p),
lbool::UNDEF,
"lit {:?} should be undef",
p
);
self.ass[p.var()] = lbool::new(p.sign());
self.vardata[p.var()] = VarData::new(from, self.decision_level() as i32);
self.trail.push(p);
}
fn var_bump_activity(&mut self, order_heap_data: &mut HeapData<Var>, v: Var) {
self.activity[v] += self.var_inc;
if self.activity[v] > 1e100 {
for (_, x) in self.activity.iter_mut() {
*x *= 1e-100;
}
self.var_inc *= 1e-100;
}
let mut order_heap = order_heap_data.promote(VarOrder {
activity: &self.activity,
});
if order_heap.in_heap(v) {
order_heap.decrease(v);
}
}
#[allow(dead_code)]
fn iter_trail<'a>(&'a self) -> impl Iterator<Item = Lit> + 'a {
self.trail.iter().map(|l| *l)
}
}
impl<'a> TheoryArg<'a> {
#[inline]
pub fn is_ok(&self) -> bool {
match self.conflict {
TheoryConflict::Nil => true,
TheoryConflict::Prop(_) | TheoryConflict::Clause { .. } => false,
}
}
#[inline(always)]
pub fn value(&self, v: Var) -> lbool {
self.v.vars.value(v)
}
#[inline(always)]
pub fn model(&self) -> &[Lit] {
&self.v.vars.trail
}
pub fn mk_new_lit(&mut self) -> Lit {
let v = self.v.new_var(lbool::FALSE, true);
Lit::new(v, true)
}
pub fn add_theory_lemma(&mut self, c: &[Lit]) {
if self.is_ok() {
self.v.th_st.push_lemma(c)
}
}
pub fn propagate(&mut self, p: Lit) -> bool {
if !self.is_ok() {
return false;
}
let v_p = self.v.vars.value_lit(p);
if v_p == lbool::TRUE {
true
} else if v_p == lbool::UNDEF {
self.has_propagated = true;
let cr = CRef::SPECIAL; self.v.vars.unchecked_enqueue(p, cr);
true
} else {
assert_eq!(v_p, lbool::FALSE);
self.conflict = TheoryConflict::Prop(p);
false
}
}
pub fn raise_conflict(&mut self, lits: &[Lit], costly: bool) {
if lits.len() == 0 {
panic!("conflicts must have a least one literal")
}
if self.is_ok() {
self.conflict = TheoryConflict::Clause { costly };
self.lits.clear();
self.lits.extend_from_slice(lits);
}
}
}
#[derive(Debug)]
enum ClauseSetSelect {
Original,
Learnt,
}
#[derive(Debug, Clone, Copy)]
struct VarData {
reason: CRef,
level: i32,
}
#[derive(Debug, Clone, Copy)]
struct Watcher {
cref: CRef,
blocker: Lit,
}
struct VarOrder<'a> {
activity: &'a VMap<f64>,
}
struct WatcherDeleted<'a> {
ca: &'a ClauseAllocator,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
enum Seen {
UNDEF,
SOURCE,
REMOVABLE,
}
mod utils {
use super::*;
pub(super) fn luby(y: f64, mut x: i32) -> f64 {
let mut size = 1;
let mut seq = 0;
while size < x + 1 {
seq += 1;
size = 2 * size + 1;
}
while size - 1 != x {
size = (size - 1) >> 1;
seq -= 1;
x = x % size;
}
return f64::powi(y, seq);
}
pub(super) fn drand(seed: &mut f64) -> f64 {
*seed *= 1389796.0;
let q = (*seed / 2147483647.0) as i32;
*seed -= q as f64 * 2147483647.0;
return *seed / 2147483647.0;
}
pub(super) fn irand(seed: &mut f64, size: i32) -> i32 {
(drand(seed) * size as f64) as i32
}
}
impl Default for VarData {
fn default() -> Self {
Self {
reason: CRef::UNDEF,
level: 0,
}
}
}
impl VarData {
#[inline(always)]
pub(super) fn new(reason: CRef, level: i32) -> Self {
Self { reason, level }
}
}
impl PartialEq for Watcher {
#[inline(always)]
fn eq(&self, rhs: &Self) -> bool {
self.cref == rhs.cref
}
}
impl Eq for Watcher {}
impl<'a> Comparator<Var> for VarOrder<'a> {
fn cmp(&self, lhs: &Var, rhs: &Var) -> cmp::Ordering {
PartialOrd::partial_cmp(&self.activity[*rhs], &self.activity[*lhs]).expect("NaN activity")
}
}
impl<'a> DeletePred<Watcher> for WatcherDeleted<'a> {
#[inline]
fn deleted(&self, w: &Watcher) -> bool {
self.ca.get_ref(w.cref).mark() == 1
}
}
impl Default for Seen {
#[inline]
fn default() -> Self {
Seen::UNDEF
}
}
impl Seen {
#[inline(always)]
fn is_seen(&self) -> bool {
*self != Seen::UNDEF
}
}
impl Watcher {
const DUMMY: Watcher = Watcher {
cref: CRef::UNDEF,
blocker: Lit::UNDEF,
};
fn new(cref: CRef, blocker: Lit) -> Self {
Self { cref, blocker }
}
}
#[derive(Clone)]
pub struct SolverOpts {
pub var_decay: f64,
pub clause_decay: f64,
pub random_var_freq: f64,
pub random_seed: f64,
pub luby_restart: bool,
pub ccmin_mode: i32,
pub phase_saving: i32,
pub rnd_pol: bool,
pub rnd_init_act: bool,
pub garbage_frac: f64,
pub min_learnts_lim: i32,
pub restart_first: i32,
pub restart_inc: f64,
pub learntsize_factor: f64,
pub learntsize_inc: f64,
}
impl Default for SolverOpts {
fn default() -> SolverOpts {
Self {
var_decay: 0.95,
clause_decay: 0.999,
random_var_freq: 0.0,
random_seed: 91648253.0,
ccmin_mode: 2,
phase_saving: 2,
rnd_init_act: false,
luby_restart: true,
restart_first: 100,
restart_inc: 2.0,
garbage_frac: 0.20,
min_learnts_lim: 0,
learntsize_factor: 1.0 / 3.0,
learntsize_inc: 1.1,
rnd_pol: false,
}
}
}
impl SolverOpts {
pub fn check(&self) -> bool {
(0.0 < self.var_decay && self.var_decay < 1.0)
&& (0.0 < self.clause_decay && self.clause_decay < 1.0)
&& (0.0 <= self.random_var_freq && self.random_var_freq <= 1.0)
&& (0.0 < self.random_seed && self.random_seed < f64::INFINITY)
&& (0 <= self.ccmin_mode && self.ccmin_mode <= 2)
&& (0 <= self.phase_saving && self.phase_saving <= 2)
&& 1 <= self.restart_first
&& (1.0 < self.restart_inc && self.restart_inc < f64::INFINITY)
&& (0.0 < self.garbage_frac && self.garbage_frac < f64::INFINITY)
&& 0 <= self.min_learnts_lim
}
}