mod cid;
mod clause;
mod db;
mod watch;
pub use self::{
cid::ClauseIdIF,
clause::ClauseIF,
property::*,
watch::{Watch, WatchDBIF},
};
use {
crate::{assign::AssignIF, types::*},
std::{
ops::IndexMut,
slice::{Iter, IterMut},
},
};
pub trait ClauseDBIF:
ActivityIF<ClauseId>
+ IndexMut<ClauseId, Output = Clause>
+ Instantiate
+ PropertyDereference<property::Tusize, usize>
+ PropertyDereference<property::Tf64, f64>
{
fn len(&self) -> usize;
fn is_empty(&self) -> bool;
fn iter(&self) -> Iter<'_, Clause>;
fn iter_mut(&mut self) -> IterMut<'_, Clause>;
fn bin_watcher_lists(&self) -> &[Vec<Watch>];
fn watcher_list(&self, l: Lit) -> &[Watch];
fn watcher_lists_mut(&mut self) -> &mut [Vec<Watch>];
fn detach(&mut self, cid: ClauseId);
fn reduce<A>(&mut self, asg: &mut A, nc: usize) -> bool
where
A: AssignIF;
fn reset(&mut self);
fn garbage_collect(&mut self);
fn registered_bin_clause(&self, l0: Lit, l1: Lit) -> bool;
fn new_clause<A>(
&mut self,
asg: &mut A,
v: &mut Vec<Lit>,
learnt: bool,
level_sort: bool,
) -> ClauseId
where
A: AssignIF;
fn mark_clause_as_used<A>(&mut self, asg: &mut A, cid: ClauseId) -> bool
where
A: AssignIF;
fn count(&self) -> usize;
fn countf(&self, mask: Flag) -> usize;
fn certificate_add(&mut self, vec: &[Lit]);
fn certificate_delete(&mut self, vec: &[Lit]);
fn touch_var(&mut self, vi: VarId);
fn check_size(&self) -> Result<bool, SolverError>;
fn validate(&self, model: &[Option<bool>], strict: bool) -> Option<ClauseId>;
fn strengthen(&mut self, cid: ClauseId, p: Lit) -> bool;
fn minimize_with_biclauses<A>(&mut self, asg: &A, vec: &mut Vec<Lit>)
where
A: AssignIF;
#[cfg(feature = "incremental_solver")]
fn make_permanent_immortal(&mut self, cid: ClauseId);
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CertifiedRecord {
SENTINEL,
ADD,
DELETE,
}
type DRAT = Vec<(CertifiedRecord, Vec<i32>)>;
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct ClauseId {
pub ordinal: u32,
}
impl ClauseId {
#[inline]
pub fn is_none(&self) -> bool {
self.ordinal == 0
}
}
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Clause {
pub lits: Vec<Lit>,
pub rank: u16,
pub search_from: usize,
reward: f64,
timestamp: usize,
flags: Flag,
}
#[derive(Clone, Debug)]
pub struct ClauseDB {
clause: Vec<Clause>,
pub bin_watcher: Vec<Vec<Watch>>,
pub watcher: Vec<Vec<Watch>>,
pub certified: DRAT,
soft_limit: usize,
use_chan_seok: bool,
co_lbd_bound: usize,
ordinal: usize,
activity_inc: f64,
activity_decay: f64,
activity_anti_decay: f64,
activity_ema: Ema,
touched: Vec<bool>,
lbd_temp: Vec<usize>,
num_lbd_update: usize,
inc_step: usize,
extra_inc: usize,
first_reduction: usize,
next_reduction: usize,
reducible: bool,
reduction_coeff: usize,
num_active: usize,
num_bi_clause: usize,
num_bi_learnt: usize,
num_lbd2: usize,
num_learnt: usize,
num_reduction: usize,
pub lbd_of_dp_ema: Ema,
pub during_vivification: bool,
pub eliminated_permanent: Vec<Vec<Lit>>,
}
pub mod property {
use super::ClauseDB;
use crate::types::*;
#[derive(Clone, Debug, PartialEq)]
pub enum Tusize {
NumActive,
NumBiClause,
NumBiLearnt,
NumLBD2,
NumLearnt,
NumReduction,
}
impl PropertyDereference<Tusize, usize> for ClauseDB {
#[inline]
fn derefer(&self, k: Tusize) -> usize {
match k {
Tusize::NumActive => self.num_active,
Tusize::NumBiClause => self.num_bi_clause,
Tusize::NumBiLearnt => self.num_bi_learnt,
Tusize::NumLBD2 => self.num_lbd2,
Tusize::NumLearnt => self.num_learnt,
Tusize::NumReduction => self.num_reduction,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Tf64 {
DpAverageLBD,
}
impl PropertyDereference<Tf64, f64> for ClauseDB {
#[inline]
fn derefer(&self, k: Tf64) -> f64 {
match k {
Tf64::DpAverageLBD => self.lbd_of_dp_ema.get(),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::assign::{AssignStack, PropagateIF};
fn lit(i: i32) -> Lit {
Lit::from(i)
}
#[allow(dead_code)]
fn check_watches(cdb: &ClauseDB, cid: ClauseId) {
let c = &cdb.clause[cid.ordinal as usize];
if c.lits.is_empty() {
println!("skip checking watches of an empty clause");
return;
}
for l in &c.lits[0..=1] {
let ws = &cdb.watcher[!*l];
assert!(ws.iter().any(|w| w.c == cid));
}
println!("pass to check watches");
}
#[test]
fn test_clause_instantiation() -> () {
let config = Config::default();
let cnf = CNFDescription {
num_of_variables: 4,
..CNFDescription::default()
};
let mut asg = AssignStack::instantiate(&config, &cnf);
let mut cdb = ClauseDB::instantiate(&config, &cnf);
asg.assign_by_decision(lit(1));
asg.assign_by_decision(lit(-2));
let c1 = cdb.new_clause(&mut asg, &mut vec![lit(1), lit(2), lit(3)], false, false);
let c = &cdb[c1];
assert_eq!(c.rank, 2);
assert!(!c.is(Flag::DEAD));
assert!(!c.is(Flag::LEARNT));
#[cfg(feature = "just_used")]
assert!(!c.is(Flag::JUST_USED));
let c2 = cdb.new_clause(&mut asg, &mut vec![lit(-1), lit(2), lit(3)], true, true);
let c = &cdb[c2];
assert_eq!(c.rank, 2);
assert!(!c.is(Flag::DEAD));
assert!(c.is(Flag::LEARNT));
#[cfg(feature = "just_used")]
assert!(!c.is(Flag::JUST_USED));
}
#[test]
fn test_clause_equality() -> () {
let config = Config::default();
let cnf = CNFDescription {
num_of_variables: 4,
..CNFDescription::default()
};
let mut asg = AssignStack::instantiate(&config, &cnf);
let mut cdb = ClauseDB::instantiate(&config, &cnf);
let c1 = cdb.new_clause(&mut asg, &mut vec![lit(1), lit(2), lit(3)], false, false);
let c2 = cdb.new_clause(&mut asg, &mut vec![lit(-1), lit(4)], false, false);
cdb[c2].reward = 2.4;
assert_eq!(c1, c1);
assert_eq!(c1 == c1, true);
assert_ne!(c1, c2);
assert_eq!(cdb.activity(c2), 2.4);
}
#[test]
fn test_clause_iterator() -> () {
let config = Config::default();
let cnf = CNFDescription {
num_of_variables: 4,
..CNFDescription::default()
};
let mut asg = AssignStack::instantiate(&config, &cnf);
let mut cdb = ClauseDB::instantiate(&config, &cnf);
let c1 = cdb.new_clause(&mut asg, &mut vec![lit(1), lit(2), lit(3)], false, false);
assert_eq!(cdb[c1][0..].iter().map(|l| i32::from(*l)).sum::<i32>(), 6);
let mut iter = cdb[c1][0..].into_iter();
assert_eq!(iter.next(), Some(&lit(1)));
assert_eq!(iter.next(), Some(&lit(2)));
assert_eq!(iter.next(), Some(&lit(3)));
assert_eq!(iter.next(), None);
}
}