mod activity;
mod binary;
mod cid;
mod clause;
mod db;
mod ema;
mod sls;
mod unsat_certificate;
mod vivify;
mod watch_cache;
pub use self::{
binary::{BinaryLinkDB, BinaryLinkList},
cid::ClauseIdIF,
property::*,
sls::StochasticLocalSearchIF,
unsat_certificate::CertificationStore,
vivify::VivifyIF,
};
use {
self::ema::ProgressLBD,
crate::{assign::AssignIF, types::*},
std::{
num::NonZeroU32,
ops::IndexMut,
slice::{Iter, IterMut},
},
watch_cache::*,
};
#[cfg(not(feature = "no_IO"))]
use std::path::Path;
pub trait ClauseIF {
fn is_empty(&self) -> bool;
fn is_dead(&self) -> bool;
fn lit0(&self) -> Lit;
fn lit1(&self) -> Lit;
fn contains(&self, lit: Lit) -> bool;
fn is_satisfied_under(&self, asg: &impl AssignIF) -> bool;
fn iter(&self) -> Iter<'_, Lit>;
fn len(&self) -> usize;
#[cfg(feature = "boundary_check")]
fn timestamp(&self) -> usize;
#[cfg(feature = "boundary_check")]
fn set_birth(&mut self, time: usize);
}
pub trait ClauseDBIF:
Instantiate
+ IndexMut<ClauseId, Output = Clause>
+ 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 binary_links(&self, l: Lit) -> &BinaryLinkList;
fn fetch_watch_cache_entry(&self, lit: Lit, index: WatchCacheProxy) -> (ClauseId, Lit);
fn watch_cache_iter(&mut self, l: Lit) -> WatchCacheIterator;
fn detach_watch_cache(&mut self, l: Lit, iter: &mut WatchCacheIterator);
fn merge_watch_cache(&mut self, l: Lit, wc: WatchCache);
fn swap_watch(&mut self, cid: ClauseId);
fn transform_by_restoring_watch_cache(
&mut self,
l: Lit,
iter: &mut WatchCacheIterator,
p: Option<Lit>,
);
fn transform_by_updating_watch(&mut self, cid: ClauseId, old: usize, new: usize, removed: bool);
fn new_clause(&mut self, asg: &mut impl AssignIF, v: &mut Vec<Lit>, learnt: bool) -> RefClause;
fn new_clause_sandbox(&mut self, asg: &mut impl AssignIF, v: &mut Vec<Lit>) -> RefClause;
fn remove_clause(&mut self, cid: ClauseId);
fn remove_clause_sandbox(&mut self, cid: ClauseId);
fn transform_by_elimination(&mut self, cid: ClauseId, p: Lit) -> RefClause;
fn transform_by_replacement(&mut self, cid: ClauseId, vec: &mut Vec<Lit>) -> RefClause;
fn transform_by_simplification(&mut self, asg: &mut impl AssignIF, cid: ClauseId) -> RefClause;
fn reduce(&mut self, asg: &mut impl AssignIF, setting: ReductionType);
fn reset(&mut self);
fn update_at_analysis(&mut self, asg: &impl AssignIF, cid: ClauseId) -> bool;
fn certificate_add_assertion(&mut self, lit: Lit);
fn certificate_save(&mut self);
fn check_size(&self) -> Result<bool, SolverError>;
fn validate(&self, model: &[Option<bool>], strict: bool) -> Option<ClauseId>;
fn minimize_with_bi_clauses(&mut self, asg: &impl AssignIF, vec: &mut Vec<Lit>);
fn complete_bi_clauses(&mut self, asg: &mut impl AssignIF);
#[cfg(feature = "incremental_solver")]
fn make_permanent_immortal(&mut self, cid: ClauseId);
#[cfg(feature = "boundary_check")]
fn watch_cache_contains(&self, lit: Lit, cid: ClauseId) -> bool;
#[cfg(feature = "boundary_check")]
fn watch_caches(&self, cid: ClauseId, message: &str) -> (Vec<Lit>, Vec<Lit>);
#[cfg(feature = "boundary_check")]
fn is_garbage_collected(&mut self, cid: ClauseId) -> Option<bool>;
#[cfg(not(feature = "no_IO"))]
fn dump_cnf(&self, asg: &impl AssignIF, fname: &Path);
}
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ClauseId {
pub ordinal: NonZeroU32,
}
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd)]
pub struct Clause {
lits: Vec<Lit>,
flags: FlagClause,
pub rank: u16,
pub rank_old: u16,
pub search_from: u16,
#[cfg(any(feature = "boundary_check", feature = "clause_rewarding"))]
timestamp: usize,
#[cfg(feature = "clause_rewarding")]
reward: f64,
#[cfg(feature = "boundary_check")]
pub birth: usize,
#[cfg(feature = "boundary_check")]
pub moved_at: Propagate,
}
#[derive(Clone, Debug)]
pub struct ClauseDB {
clause: Vec<Clause>,
binary_link: BinaryLinkDB,
watch_cache: Vec<WatchCache>,
freelist: Vec<ClauseId>,
certification_store: CertificationStore,
soft_limit: usize,
co_lbd_bound: u16,
bi_clause_completion_queue: Vec<Lit>,
num_bi_clause_completion: usize,
#[cfg(feature = "clause_rewarding")]
tick: usize,
#[cfg(feature = "clause_rewarding")]
activity_decay: f64,
#[cfg(feature = "clause_rewarding")]
activity_anti_decay: f64,
lbd_temp: Vec<usize>,
lbd: ProgressLBD,
num_clause: usize,
num_bi_clause: usize,
num_bi_learnt: usize,
num_lbd2: usize,
num_learnt: usize,
num_reduction: usize,
num_reregistration: usize,
lb_entanglement: Ema2,
reduction_threshold: f64,
pub eliminated_permanent: Vec<Vec<Lit>>,
}
#[derive(Clone, Debug)]
pub enum ReductionType {
RASonADD(usize),
RASonALL(f64, f64),
LBDonADD(usize),
LBDonALL(u16, f64),
}
pub mod property {
use super::ClauseDB;
use crate::types::*;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Tusize {
NumBiClause,
NumBiClauseCompletion,
NumBiLearnt,
NumClause,
NumLBD2,
NumLearnt,
NumReduction,
NumReRegistration,
Timestamp,
}
pub const USIZES: [Tusize; 9] = [
Tusize::NumBiClause,
Tusize::NumBiClauseCompletion,
Tusize::NumBiLearnt,
Tusize::NumClause,
Tusize::NumLBD2,
Tusize::NumLearnt,
Tusize::NumReduction,
Tusize::NumReRegistration,
Tusize::Timestamp,
];
impl PropertyDereference<Tusize, usize> for ClauseDB {
#[inline]
fn derefer(&self, k: Tusize) -> usize {
match k {
Tusize::NumClause => self.num_clause,
Tusize::NumBiClause => self.num_bi_clause,
Tusize::NumBiClauseCompletion => self.num_bi_clause_completion,
Tusize::NumBiLearnt => self.num_bi_learnt,
Tusize::NumLBD2 => self.num_lbd2,
Tusize::NumLearnt => self.num_learnt,
Tusize::NumReduction => self.num_reduction,
Tusize::NumReRegistration => self.num_reregistration,
#[cfg(feature = "clause_rewarding")]
Tusize::Timestamp => self.tick,
#[cfg(not(feature = "clause_rewarding"))]
Tusize::Timestamp => 0,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Tf64 {
LiteralBlockDistance,
LiteralBlockEntanglement,
ReductionThreshold,
}
pub const F64: [Tf64; 3] = [
Tf64::LiteralBlockDistance,
Tf64::LiteralBlockEntanglement,
Tf64::ReductionThreshold,
];
impl PropertyDereference<Tf64, f64> for ClauseDB {
#[inline]
fn derefer(&self, k: Tf64) -> f64 {
match k {
Tf64::LiteralBlockDistance => self.lbd.get(),
Tf64::LiteralBlockEntanglement => self.lb_entanglement.get(),
Tf64::ReductionThreshold => self.reduction_threshold,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TEma {
Entanglement,
LBD,
}
pub const EMAS: [TEma; 2] = [TEma::Entanglement, TEma::LBD];
impl PropertyReference<TEma, EmaView> for ClauseDB {
#[inline]
fn refer(&self, k: TEma) -> &EmaView {
match k {
TEma::Entanglement => self.lb_entanglement.as_view(),
TEma::LBD => self.lbd.as_view(),
}
}
}
}
#[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[NonZeroU32::get(cid.ordinal) as usize];
if c.lits.is_empty() {
println!("skip checking watches of an empty clause");
return;
}
assert!(c.lits[0..2]
.iter()
.all(|l| cdb.watch_cache[!*l].iter().any(|(c, _)| *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);
let c0 = cdb
.new_clause(&mut asg, &mut vec![lit(1), lit(2), lit(3), lit(4)], false)
.as_cid();
assert_eq!(cdb[c0].rank, 4);
asg.assign_by_decision(lit(-2)); asg.assign_by_decision(lit(1)); let c1 = cdb
.new_clause(&mut asg, &mut vec![lit(1), lit(2), lit(3)], false)
.as_cid();
let c = &cdb[c1];
assert_eq!(c.rank, 3);
assert!(!c.is_dead());
assert!(!c.is(FlagClause::LEARNT));
#[cfg(feature = "just_used")]
assert!(!c.is(Flag::USED));
let c2 = cdb
.new_clause(&mut asg, &mut vec![lit(-1), lit(2), lit(3)], true)
.as_cid();
let c = &cdb[c2];
assert_eq!(c.rank, 3);
assert!(!c.is_dead());
assert!(c.is(FlagClause::LEARNT));
#[cfg(feature = "just_used")]
assert!(!c.is(Flag::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)
.as_cid();
let c2 = cdb
.new_clause(&mut asg, &mut vec![lit(-1), lit(4)], false)
.as_cid();
assert_eq!(c1, c1);
assert_ne!(c1, c2);
}
#[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)
.as_cid();
assert_eq!(cdb[c1][0..].iter().map(|l| i32::from(*l)).sum::<i32>(), 6);
let mut iter = cdb[c1][0..].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);
}
}