use crate::ast::cnf::CNFCache;
use crate::ast::{ATerm, Arena, FlatConnectivesExt, Term};
use crate::traits::Repr;
use sat_interface::{Clause, SatSolver, SolverState};
use std::collections::HashSet;
pub trait Model {
fn evaluate(&mut self, t: &Term) -> Option<bool>;
fn block(&mut self, t: &Term);
}
trait FindImplicantImpl<Env, Model> {
fn find_one_implicant_impl(&self, env: Env, model: &mut Model, block: bool) -> Self;
}
impl<M> FindImplicantImpl<&mut Arena, M> for Term
where
M: Model,
{
fn find_one_implicant_impl(&self, env: &mut Arena, model: &mut M, block: bool) -> Self {
match self.repr() {
ATerm::Annotated(t, _) => t.find_one_implicant_impl(&mut *env, model, block),
ATerm::And(ts) => {
let nts = ts
.iter()
.map(|t| t.find_one_implicant_impl(&mut *env, model, block))
.collect();
env.flat_and(nts)
}
ATerm::Or(ts) => ts
.iter()
.find(|t| model.evaluate(t).unwrap()) .unwrap()
.find_one_implicant_impl(&mut *env, model, true),
ATerm::Not(t) => {
if block {
model.block(t);
}
self.clone()
}
_ => {
if block {
model.block(self);
}
self.clone()
}
}
}
}
impl<M> FindImplicantImpl<&mut Arena, M> for Vec<Term>
where
M: Model,
{
fn find_one_implicant_impl(&self, env: &mut Arena, model: &mut M, block: bool) -> Self {
let mut result = vec![];
let mut existing_id = HashSet::new();
let mut insert = |t: Term| {
if existing_id.insert(t.uid()) {
result.push(t);
}
};
for t in self {
let ti = t.find_one_implicant_impl(&mut *env, model, block);
match ti.repr() {
ATerm::And(ts) => {
for t in ts {
insert(t.clone());
}
}
_ => insert(ti),
}
}
result
}
}
struct SatState<'a, 'b, Solver> {
solver: &'a mut Solver,
cache: &'b CNFCache,
blocking_literals: HashSet<i32>,
}
impl<Solver> Model for SatState<'_, '_, Solver>
where
Solver: SatSolver,
{
fn evaluate(&mut self, t: &Term) -> Option<bool> {
let i = self.cache.var_map.get(&t.uid())?;
let r = self.solver.val(*i);
Some(r)
}
fn block(&mut self, t: &Term) {
let i = self.cache.var_map.get(&t.uid()).unwrap();
let r = self.solver.val(*i);
self.blocking_literals.insert(if r { -*i } else { *i });
}
}
pub trait FindImplicant<Env, Solver>
where
Self: Sized,
Solver: SatSolver,
{
fn find_one_implicant(&self, env: Env, solver: &mut Solver)
-> Option<crate::ast::Result<Self>>;
}
pub(crate) struct ImplicantEnv<'a> {
pub arena: &'a mut Arena,
pub cnf_cache: &'a CNFCache,
}
impl<'a, Solver> FindImplicant<ImplicantEnv<'a>, Solver> for Vec<Term>
where
Solver: SatSolver,
{
fn find_one_implicant(
&self,
env: ImplicantEnv<'a>,
solver: &mut Solver,
) -> Option<crate::ast::Result<Self>> {
let status = solver.solve();
if status.is_unsat() {
None
} else if status.is_unknown() {
Some(Err("Implicant: SAT returns unknown!".into()))
} else {
let mut state = SatState {
solver,
cache: env.cnf_cache,
blocking_literals: HashSet::new(),
};
let result = self.find_one_implicant_impl(env.arena, &mut state, false);
let blocking_clause = Clause::new(state.blocking_literals.iter().cloned().collect());
solver.insert(&blocking_clause);
Some(Ok(result))
}
}
}
pub trait ImplicantIterator<T>: Iterator<Item = crate::ast::Result<T>> {
fn block(&mut self, item: &T);
}
pub(crate) struct ImplicantEnumerator<'a, 'b, Solver, T> {
pub arena: &'b mut Arena,
pub solver: &'a mut Solver,
pub cache: &'b CNFCache,
pub nnf: T,
pub finished: bool,
}
impl<Solver, T> Iterator for ImplicantEnumerator<'_, '_, Solver, T>
where
Solver: SatSolver,
T: for<'c> FindImplicant<ImplicantEnv<'c>, Solver>,
{
type Item = crate::ast::Result<T>;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
let implicant = self.nnf.find_one_implicant(
ImplicantEnv {
arena: self.arena,
cnf_cache: self.cache,
},
self.solver,
);
match &implicant {
None => {
self.finished = true;
}
Some(Err(_)) => {
self.finished = true;
}
&Some(Ok(_)) => {}
}
implicant
}
}
impl<Solver> ImplicantIterator<Vec<Term>> for ImplicantEnumerator<'_, '_, Solver, Vec<Term>>
where
Solver: SatSolver,
{
fn block(&mut self, item: &Vec<Term>) {
let vars = item
.iter()
.flat_map(|t| self.cache.var_map.get(&t.uid()).map(|i| -*i))
.collect();
self.solver.insert(&Clause::new(vars));
}
}