/*****************************************************************************/
/*!
*\file cnf_manager.cpp
*\brief Implementation of CNF_Manager
*
* Author: Clark Barrett
*
* Created: Thu Jan 5 02:30:02 2006
*
*
*
* License to use, copy, modify, sell and/or distribute this software
* and its documentation for any purpose is hereby granted without
* royalty, subject to the terms and conditions defined in the \ref
* LICENSE file provided with this distribution.
*
*
*/
/*****************************************************************************/
#include "cnf_manager.h"
#include "cnf_rules.h"
#include "common_proof_rules.h"
#include "theorem_manager.h"
#include "vc.h"
#include "command_line_flags.h"
using namespace std;
using namespace CVC3;
using namespace SAT;
CNF_Manager::CNF_Manager(TheoremManager* tm, Statistics& statistics, bool minimizeClauses)
: d_vc(NULL), d_minimizeClauses(minimizeClauses),
d_commonRules(tm->getRules()),
// d_theorems(tm->getCM()->getCurrentContext()),
d_clauseIdNext(0),
// d_translated(tm->getCM()->getCurrentContext()),
d_bottomScope(-1),
d_statistics(statistics),
d_cnfCallback(NULL)
{
d_rules = createProofRules(tm);
// Push dummy varinfo onto d_varInfo since Var's are indexed from 1 not 0
Varinfo v;
d_varInfo.push_back(v);
if (minimizeClauses) {
CLFlags flags = ValidityChecker::createFlags();
flags.setFlag("minimizeClauses",false);
d_vc = ValidityChecker::create(flags);
}
}
CNF_Manager::~CNF_Manager()
{
if (d_vc) delete d_vc;
delete d_rules;
}
void CNF_Manager::registerAtom(const Expr& e, const Theorem& thm)
{
DebugAssert(!e.isRegisteredAtom() || e.isUserRegisteredAtom(), "Atom already registered");
if (d_cnfCallback && !e.isRegisteredAtom()) d_cnfCallback->registerAtom(e, thm);
}
Theorem CNF_Manager::replaceITErec(const Expr& e, Var v, bool translateOnly)
{
// Quick exit for atomic expressions
if (e.isAtomic()) return d_commonRules->reflexivityRule(e);
// Check cache
Theorem thm;
bool foundInCache = false;
ExprMap::iterator iMap = d_iteMap.find(e);
if (iMap != d_iteMap.end()) {
thm = (*iMap).second;
foundInCache = true;
}
if (e.getKind() == ITE) {
// Replace non-Bool ITE expressions
DebugAssert(!e.getType().isBool(), "Expected non-Bool ITE");
// generate e = x for new x
if (!foundInCache) thm = d_commonRules->varIntroSkolem(e);
Theorem thm2 = d_commonRules->symmetryRule(thm);
thm2 = d_commonRules->iffMP(thm2, d_rules->ifLiftRule(thm2.getExpr(), 1));
d_translateQueueVars.push_back(v);
d_translateQueueThms.push_back(thm2);
d_translateQueueFlags.push_back(translateOnly);
}
else {
// Recursively traverse, replacing ITE's
vector thms;
vector changed;
unsigned index = 0;
Expr::iterator i, iend;
if (foundInCache) {
for(i = e.begin(), iend = e.end(); i!=iend; ++i, ++index) {
replaceITErec(*i, v, translateOnly);
}
}
else {
for(i = e.begin(), iend = e.end(); i!=iend; ++i, ++index) {
thm = replaceITErec(*i, v, translateOnly);
if(thm.getLHS() != thm.getRHS()) {
thms.push_back(thm);
changed.push_back(index);
}
}
if(changed.size() > 0) {
thm = d_commonRules->substitutivityRule(e, changed, thms);
}
else thm = d_commonRules->reflexivityRule(e);
}
}
// Update cache and return
if (!foundInCache) d_iteMap[e] = thm;
return thm;
}
Lit CNF_Manager::translateExprRec(const Expr& e, CNF_Formula& cnf, const Theorem& thmIn)
{
if (e.isFalse()) return Lit::getFalse();
if (e.isTrue()) return Lit::getTrue();
if (e.isNot()) return !translateExprRec(e[0], cnf, thmIn);
ExprMap::iterator iMap = d_cnfVars.find(e);
if (e.isTranslated()) {
DebugAssert(iMap != d_cnfVars.end(), "Translated expr should be in map");
return Lit((*iMap).second);
}
else e.setTranslated(d_bottomScope);
Var v(int(d_varInfo.size()));
bool translateOnly = false;
if (iMap != d_cnfVars.end()) {
v = (*iMap).second;
translateOnly = true;
d_varInfo[v].fanouts.clear();
}
else {
d_varInfo.resize(v+1);
d_varInfo.back().expr = e;
d_cnfVars[e] = v;
}
Expr::iterator i, iend;
bool isAnd = false;
switch (e.getKind()) {
case AND:
isAnd = true;
case OR: {
vector lits;
unsigned idx;
for (i = e.begin(), iend = e.end(); i != iend; ++i) {
lits.push_back(translateExprRec(*i, cnf, thmIn));
}
for (idx = 0; idx < lits.size(); ++idx) {
cnf.newClause();
cnf.addLiteral(Lit(v),isAnd);
cnf.addLiteral(lits[idx], !isAnd);
}
cnf.newClause();
cnf.addLiteral(Lit(v),!isAnd);
for (idx = 0; idx < lits.size(); ++idx) {
cnf.addLiteral(lits[idx], isAnd);
}
break;
}
case IMPLIES: {
Lit arg0 = translateExprRec(e[0], cnf, thmIn);
Lit arg1 = translateExprRec(e[1], cnf, thmIn);
cnf.newClause();
cnf.addLiteral(Lit(v));
cnf.addLiteral(arg0);
cnf.newClause();
cnf.addLiteral(Lit(v));
cnf.addLiteral(arg1,true);
cnf.newClause();
cnf.addLiteral(Lit(v),true);
cnf.addLiteral(arg0,true);
cnf.addLiteral(arg1);
break;
}
case IFF: {
Lit arg0 = translateExprRec(e[0], cnf, thmIn);
Lit arg1 = translateExprRec(e[1], cnf, thmIn);
cnf.newClause();
cnf.addLiteral(Lit(v));
cnf.addLiteral(arg0);
cnf.addLiteral(arg1);
cnf.newClause();
cnf.addLiteral(Lit(v));
cnf.addLiteral(arg0,true);
cnf.addLiteral(arg1,true);
cnf.newClause();
cnf.addLiteral(Lit(v),true);
cnf.addLiteral(arg0,true);
cnf.addLiteral(arg1);
cnf.newClause();
cnf.addLiteral(Lit(v),true);
cnf.addLiteral(arg0);
cnf.addLiteral(arg1,true);
break;
}
case XOR: {
Lit arg0 = translateExprRec(e[0], cnf, thmIn);
Lit arg1 = translateExprRec(e[1], cnf, thmIn);
cnf.newClause();
cnf.addLiteral(Lit(v),true);
cnf.addLiteral(arg0);
cnf.addLiteral(arg1);
cnf.newClause();
cnf.addLiteral(Lit(v),true);
cnf.addLiteral(arg0,true);
cnf.addLiteral(arg1,true);
cnf.newClause();
cnf.addLiteral(Lit(v));
cnf.addLiteral(arg0,true);
cnf.addLiteral(arg1);
cnf.newClause();
cnf.addLiteral(Lit(v));
cnf.addLiteral(arg0);
cnf.addLiteral(arg1,true);
break;
}
case ITE:
{
Lit arg0 = translateExprRec(e[0], cnf, thmIn);
Lit arg1 = translateExprRec(e[1], cnf, thmIn);
Lit arg2 = translateExprRec(e[2], cnf, thmIn);
cnf.newClause();
cnf.addLiteral(Lit(v),true);
cnf.addLiteral(arg0);
cnf.addLiteral(arg2);
cnf.newClause();
cnf.addLiteral(Lit(v));
cnf.addLiteral(arg0);
cnf.addLiteral(arg2,true);
cnf.newClause();
cnf.addLiteral(Lit(v));
cnf.addLiteral(arg0,true);
cnf.addLiteral(arg1,true);
cnf.newClause();
cnf.addLiteral(Lit(v),true);
cnf.addLiteral(arg0,true);
cnf.addLiteral(arg1);
cnf.newClause();
cnf.addLiteral(Lit(v));
cnf.addLiteral(arg1,true);
cnf.addLiteral(arg2,true);
cnf.newClause();
cnf.addLiteral(Lit(v),true);
cnf.addLiteral(arg1);
cnf.addLiteral(arg2);
break;
}
default:
DebugAssert(!e.isAbsAtomicFormula() || d_varInfo[v].expr == e,
"Corrupted Varinfo");
if (e.isAbsAtomicFormula()) {
registerAtom(e, thmIn);
return Lit(v);
}
Theorem thm = replaceITErec(e, v, translateOnly);
const Expr& e2 = thm.getRHS();
DebugAssert(e2.isAbsAtomicFormula(), "Expected AbsAtomicFormula");
if (e2.isTranslated()) {
// Ugly corner case: we happen to create an expression that has been
// created before. We remove the current variable and fix up the
// translation stack.
if (translateOnly) {
DebugAssert(v == d_cnfVars[e2], "Expected literal match");
}
else {
d_varInfo.resize(v);
while (!d_translateQueueVars.empty() &&
d_translateQueueVars.back() == v) {
d_translateQueueVars.pop_back();
}
DebugAssert(d_cnfVars.find(e2) != d_cnfVars.end(),
"Expected existing literal");
v = d_cnfVars[e2];
d_cnfVars[e] = v;
while (d_translateQueueVars.size() < d_translateQueueThms.size()) {
d_translateQueueVars.push_back(v);
}
}
}
else {
e2.setTranslated(d_bottomScope);
registerAtom(e2, thmIn);
if (!translateOnly) {
DebugAssert(d_cnfVars.find(e2) == d_cnfVars.end(),
"Expected new literal");
d_varInfo[v].expr = e2;
d_cnfVars[e2] = v;
}
}
return Lit(v);
}
// Record fanins / fanouts
Lit l;
for (i = e.begin(), iend = e.end(); i != iend; ++i) {
l = getCNFLit(*i);
DebugAssert(!l.isNull(), "Expected non-null literal");
if (!translateOnly) d_varInfo[v].fanins.push_back(l);
if (l.isVar()) d_varInfo[l.getVar()].fanouts.push_back(v);
}
return Lit(v);
}
Lit CNF_Manager::translateExpr(const Theorem& thmIn, CNF_Formula& cnf)
{
Lit l;
Var v;
Expr e = thmIn.getExpr();
Theorem thm;
bool translateOnly;
Lit ret = translateExprRec(e, cnf, thmIn);
while (d_translateQueueVars.size()) {
v = d_translateQueueVars.front();
d_translateQueueVars.pop_front();
thm = d_translateQueueThms.front();
d_translateQueueThms.pop_front();
translateOnly = d_translateQueueFlags.front();
d_translateQueueFlags.pop_front();
l = translateExprRec(thm.getExpr(), cnf, thmIn);
cnf.newClause();
cnf.addLiteral(l);
cnf.registerUnit();
// d_theorems.insert(d_clauseIdNext, thm);
cnf.getCurrentClause().setId(d_clauseIdNext++);
FatalAssert(d_clauseIdNext != 0, "Overflow of clause id's");
if (!translateOnly) d_varInfo[v].fanins.push_back(l);
d_varInfo[l.getVar()].fanouts.push_back(v);
}
return ret;
}
void CNF_Manager::cons(unsigned lb, unsigned ub, const Expr& e2, vector& newLits)
{
if (lb == ub) {
newLits.push_back(lb);
return;
}
unsigned new_lb = (ub-lb+1)/2 + lb;
unsigned index;
QueryResult res;
d_vc->push();
for (index = new_lb; index <= ub; ++index) {
d_vc->assertFormula(e2[index].negate());
}
res = d_vc->query(d_vc->falseExpr());
d_vc->pop();
if (res == VALID) {
cons(new_lb, ub, e2, newLits);
return;
}
unsigned new_ub = new_lb-1;
d_vc->push();
for (index = lb; index <= new_ub; ++index) {
d_vc->assertFormula(e2[index].negate());
}
res = d_vc->query(d_vc->falseExpr());
if (res == VALID) {
d_vc->pop();
cons(lb, new_ub, e2, newLits);
return;
}
cons(new_lb, ub, e2, newLits);
d_vc->pop();
d_vc->push();
for (index = 0; index < newLits.size(); ++index) {
d_vc->assertFormula(e2[newLits[index]].negate());
}
cons(lb, new_ub, e2, newLits);
d_vc->pop();
}
void CNF_Manager::convertLemma(const Theorem& thm, Clause& c)
{
DebugAssert(c.size() == 0, "Expected empty clause");
Theorem clause = d_rules->learnedClause(thm);
Expr e = clause.getExpr();
if (!e.isOr()) {
DebugAssert(!getCNFLit(e).isNull(), "Unknown literal");
c.addLiteral(getCNFLit(e));
}
else {
if (e.arity() > 3 && d_minimizeClauses) {
// Clause minimization
Expr e2 = d_vc->importExpr(e);
vector newLits;
DebugAssert(d_vc->scopeLevel() == 1, "expected scope level 1");
// d_vc->push();
// d_vc->assertFormula(e2[e.arity()-1].negate());
cons(0, e.arity()-1, e2, newLits);
// d_vc->pop();
DebugAssert(d_vc->scopeLevel() == 1, "expected scope level 1");
d_statistics.counter("clauses processed")++;
if (newLits.size() < (unsigned)e.arity()) {
d_statistics.counter("clauses minimized")++;
vector newKids;
for (unsigned index = 0; index < newLits.size(); ++index) {
newKids.push_back(e[newLits[index]]);
}
// newKids.push_back(e[e.arity()-1]);
e = Expr(OR, newKids);
IF_DEBUG(
d_vc->push();
DebugAssert(d_vc->query(d_vc->importExpr(e)) == VALID, "expected valid");
d_vc->pop();
)
}
}
Expr::iterator iend = e.end();
for (Expr::iterator i = e.begin(); i != iend; ++i) {
DebugAssert(!getCNFLit(*i).isNull(), "Unknown literal");
c.addLiteral(getCNFLit(*i));
}
}
if (c.size() == 1) c.setUnit();
// d_theorems.insert(d_clauseIdNext, clause);
c.setId(d_clauseIdNext++);
FatalAssert(d_clauseIdNext != 0, "Overflow of clause id's");
}
Lit CNF_Manager::addAssumption(const Theorem& thm, CNF_Formula& cnf)
{
Lit l = translateExpr(thm, cnf);
cnf.newClause();
cnf.addLiteral(l);
cnf.registerUnit();
// d_theorems[d_clauseIdNext] = thm;
cnf.getCurrentClause().setId(d_clauseIdNext++);
FatalAssert(d_clauseIdNext != 0, "Overflow of clause id's");
return l;
}
Lit CNF_Manager::addLemma(const Theorem& thm, CNF_Formula& cnf)
{
Theorem clause = d_rules->learnedClause(thm);
Lit l = translateExpr(clause, cnf);
cnf.newClause();
cnf.addLiteral(l);
cnf.registerUnit();
// d_theorems.insert(d_clauseIdNext, clause);
cnf.getCurrentClause().setId(d_clauseIdNext++);
FatalAssert(d_clauseIdNext != 0, "Overflow of clause id's");
return l;
}