/*****************************************************************************/ /*! *\file dpllt_basic.cpp *\brief Basic implementation of dpllt module using generic sat solver * * Author: Clark Barrett * * Created: Mon Dec 12 19:09:43 2005 * *
* * 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 "dpllt_basic.h" #include "cnf.h" #include "sat_api.h" #include "exception.h" using namespace std; using namespace CVC3; using namespace SAT; //int level_ = 0; static void SATDLevelHook(void *cookie, int change) { //cout << "backtrack to: " << level_ << " " << change << endl; //level_ += change; // cout<<"decision level called"<(cookie); for (; change > 0; change--) { db->theoryAPI()->push(); } for (; change < 0; change++) { db->theoryAPI()->pop(); } } static SatSolver::Lit SATDecisionHook(void *cookie, bool *done) { // cout<<"sat decision called"<(cookie); if (db->theoryAPI()->outOfResources()) { // Tell SAT solver to exit with satisfiable result *done = true; return SatSolver::Lit(); } decide: if (!db->decider()) { // Tell SAT solver to make its own choice if (!*done) return SatSolver::Lit(); } else { Lit lit = db->decider()->makeDecision(); if (!lit.isNull()) { //cout << "Split: " << lit.getVar().getIndex() << endl; // Tell SAT solver which literal to split on *done = false; return db->cvc2SAT(lit); } } Clause c; DPLLT::ConsistentResult result; result = db->theoryAPI()->checkConsistent(c, true); if (result == DPLLT::MAYBE_CONSISTENT) { CNF_Formula_Impl cnf; bool added = db->theoryAPI()->getNewClauses(cnf); db->addNewClauses(cnf); if (!added) goto decide; // Fall through: SAT solver will continue because clauses have been added } else if (result == DPLLT::INCONSISTENT) { db->addNewClause(c); } // Tell SAT solver that we are done *done = true; return SatSolver::Lit(); } static void SATAssignmentHook(void *cookie, SatSolver::Var var, int value) { // cout<<"assignment called"<(cookie); TRACE("DPLL Assign", var.id, " := ", value); if (value == 0) db->theoryAPI()->assertLit(Lit(db->satSolver()->GetVarIndex(var), false)); else if (value == 1) db->theoryAPI()->assertLit(Lit(db->satSolver()->GetVarIndex(var), true)); else return; Clause c; DPLLT::ConsistentResult result; result = db->theoryAPI()->checkConsistent(c, false); if (result == DPLLT::INCONSISTENT) { db->addNewClause(c); } } static void SATDeductionHook(void *cookie) { // cout<<"deduction called"<(cookie); Clause c; CNF_Formula_Impl cnf; if (db->theoryAPI()->getNewClauses(cnf)) db->addNewClauses(cnf); Lit l = db->theoryAPI()->getImplication(); while (!l.isNull()) { db->theoryAPI()->getExplanation(l, c); db->addNewClause(c); c.clear(); l = db->theoryAPI()->getImplication(); } } void DPLLTBasic::createManager() { d_mng = SatSolver::Create(); d_mng->RegisterDLevelHook(SATDLevelHook, this); d_mng->RegisterDecisionHook(SATDecisionHook, this); d_mng->RegisterAssignmentHook(SATAssignmentHook, this); d_mng->RegisterDeductionHook(SATDeductionHook, this); } void DPLLTBasic::generate_CDB (CNF_Formula_Impl& cnf) { CNF_Formula::const_iterator i, iend; Clause::const_iterator j, jend; vector clause; if (cnf.numVars() > unsigned(d_mng->NumVariables())) { d_mng->AddVariables(cnf.numVars() - d_mng->NumVariables()); } cnf.simplify(); for (i = cnf.end()-1, iend = cnf.begin()-1; i != iend; --i) { //for (i = cnf.begin(), iend = cnf.end(); i != iend; i++) { if ((*i).isSatisfied()) continue; for (j = (*i).begin(), jend = (*i).end(); j != jend; ++j) { if (!(*j).isFalse()) clause.push_back(cvc2SAT(*j)); } if (clause.size() != 0) { d_mng->AddClause(clause); clause.clear(); } } } void DPLLTBasic::handle_result(SatSolver::SATStatus outcome) { char * result = "UNKNOWN"; switch (outcome) { case SatSolver::SATISFIABLE: // if (d_printStats) { // cout << "Instance satisfiable" << endl; // for (int i=1, sz = d_mng->NumVariables(); i <= sz; ++i) { // switch(d_mng->GetVarAssignment(d_mng->GetVar(i))) { // case -1: // cout <<"("<< i<<")"; break; // case 0: // cout << "-" << i; break; // case 1: // cout << i ; break; // default: // throw Exception("Unknown variable value state"); // } // cout << " "; // } // cout << endl; // } result = "SAT"; break; case SatSolver::UNSATISFIABLE: result = "UNSAT"; if (d_printStats) cout << "Instance unsatisfiable" << endl; break; case SatSolver::BUDGET_EXCEEDED: result = "ABORT : TIME OUT"; cout << "Time out, unable to determine the satisfiablility of the instance"; cout << endl; break; case SatSolver::OUT_OF_MEMORY: result = "ABORT : MEM OUT"; cout << "Memory out, unable to determing the satisfiablility of the instance"; cout << endl; break; default: throw Exception("DPLTBasic::handle_result: Unknown outcome"); } if (d_printStats) d_mng->PrintStatistics(); } void DPLLTBasic::verify_solution() { // Used to check that all clauses are true, but our decision maker // may ignore some clauses that are no longer relevant, so now we check to // make sure no clause is false. for (SatSolver::Clause cl = d_mng->GetFirstClause(); !cl.IsNull(); cl = d_mng->GetNextClause(cl)) { vector lits; d_mng->GetClauseLits(cl, &lits); for (; lits.size() != 0;) { SatSolver::Lit lit = lits.back(); SatSolver::Var var = d_mng->GetVarFromLit(lit); int sign = d_mng->GetPhaseFromLit(lit); int var_value = d_mng->GetVarAssignment(var); if( (var_value == 1 && sign == 0) || (var_value == 0 && sign == 1) || (var_value == -1) ) break; lits.pop_back(); } DebugAssert(lits.size() != 0, "DPLLTBasic::verify_solution failed"); } } DPLLTBasic::DPLLTBasic(TheoryAPI* theoryAPI, Decider* decider, ContextManager* cm, bool printStats) : DPLLT(theoryAPI, decider), d_cm(cm), d_ready(true), d_printStats(printStats), d_pushLevel(cm->getCurrentContext(), 0), d_readyPrev(cm->getCurrentContext(), true), d_prevStackSize(cm->getCurrentContext(), 0), d_prevAStackSize(cm->getCurrentContext(), 0) { createManager(); d_cnf = new CNF_Formula_Impl(); d_assertions = new CD_CNF_Formula(d_cm->getCurrentContext()); } DPLLTBasic::~DPLLTBasic() { if (d_assertions) delete d_assertions; if (d_cnf) delete d_cnf; if (d_mng) delete d_mng; while (d_assertionsStack.size() > 0) { d_assertions = d_assertionsStack.back(); d_assertionsStack.pop_back(); delete d_assertions; } while (d_mngStack.size() > 0) { d_mng = d_mngStack.back(); d_mngStack.pop_back(); delete d_mng; d_cnf = d_cnfStack.back(); d_cnfStack.pop_back(); delete d_cnf; } } void DPLLTBasic::addNewClause(const Clause& c) { DebugAssert(c.size() > 0, "Expected non-empty clause"); DebugAssert(c.getMaxVar() <= unsigned(d_mng->NumVariables()), "Expected no new variables"); vector lits; for (Clause::const_iterator i = c.begin(), iend = c.end(); i < iend; ++i) { if (!(*i).isFalse()) lits.push_back(cvc2SAT(*i)); } satSolver()->AddClause(lits); (*d_cnf) += c; } void DPLLTBasic::addNewClauses(CNF_Formula_Impl& cnf) { CNF_Formula::const_iterator i, iend; Clause::const_iterator j, jend; vector clause; if (cnf.numVars() > unsigned(d_mng->NumVariables())) { d_mng->AddVariables(cnf.numVars() - d_mng->NumVariables()); } cnf.simplify(); for (i = cnf.end()-1, iend = cnf.begin()-1; i != iend; --i) { //for (i = cnf.begin(), iend = cnf.end(); i != iend; i++) { if ((*i).isSatisfied()) continue; for (j = (*i).begin(), jend = (*i).end(); j != jend; ++j) { if (!(*j).isFalse()) clause.push_back(cvc2SAT(*j)); } if (clause.size() != 0) { d_mng->AddClause(clause); clause.clear(); } } generate_CDB(cnf); (*d_cnf) += cnf; } void DPLLTBasic::push() { d_theoryAPI->push(); d_pushLevel = d_pushLevel + 1; d_prevStackSize = d_mngStack.size(); d_prevAStackSize = d_assertionsStack.size(); d_readyPrev = d_ready; } void DPLLTBasic::pop() { unsigned pushLevel = d_pushLevel; unsigned prevStackSize = d_prevStackSize; unsigned prevAStackSize = d_prevAStackSize; bool readyPrev = d_readyPrev; while (d_assertionsStack.size() > prevAStackSize) { delete d_assertions; d_assertions = d_assertionsStack.back(); d_assertionsStack.pop_back(); } while (d_mngStack.size() > prevStackSize) { delete d_mng; delete d_cnf; d_mng = d_mngStack.back(); d_mngStack.pop_back(); d_cnf = d_cnfStack.back(); d_cnfStack.pop_back(); DebugAssert(d_mngStack.size() == d_cnfStack.size(), "size mismatch"); } if (d_mngStack.size() == 0) { if (readyPrev && !d_ready) { delete d_mng; delete d_cnf; createManager(); d_cnf = new CNF_Formula_Impl(); d_ready = true; } else { DebugAssert(readyPrev == d_ready, "Unexpected ready values"); } } else { DebugAssert(!d_ready, "Expected ready to be false"); } while (d_pushLevel == pushLevel) d_theoryAPI->pop(); } void DPLLTBasic::addAssertion(const CNF_Formula& cnf) { // Immediately assert unit clauses CNF_Formula::const_iterator i, iend; Clause::const_iterator j, jend; for (i = cnf.end()-1, iend = cnf.begin()-1; i != iend; --i) { if ((*i).isUnit()) { j = (*i).begin(); d_theoryAPI->assertLit(*j); } } // Accumulate assertions in d_assertions (*d_assertions) += cnf; } QueryResult DPLLTBasic::checkSat(const CNF_Formula& cnf) { SatSolver::SATStatus result; if (!d_ready) { // Copy current formula d_cnfStack.push_back(d_cnf); d_cnf = new CNF_Formula_Impl(*d_cnf); // make unit clauses for current assignment int value; for (int i = 1; i <= d_mng->NumVariables(); ++i) { value = d_mng->GetVarAssignment(d_mng->GetVar(i)); if (value == 1) { d_cnf->newClause(); d_cnf->addLiteral(Lit(i)); } else if (value == 0) { d_cnf->newClause(); d_cnf->addLiteral(Lit(i, false)); } } // Create new manager d_mngStack.push_back(d_mng); DebugAssert(d_mngStack.size() == d_cnfStack.size(), "size mismatch"); createManager(); } d_ready = false; if (d_assertions) (*d_cnf) += (*d_assertions); (*d_cnf) += cnf; generate_CDB(*d_cnf); d_theoryAPI->push(); result = d_mng->Satisfiable(true); if (result == SatSolver::SATISFIABLE && theoryAPI()->outOfResources()) result = SatSolver::BUDGET_EXCEEDED; // Print result if (result == SatSolver::SATISFIABLE) { verify_solution(); if (d_assertions->numClauses() > 0) { d_assertionsStack.push_back(d_assertions); d_assertions = new CD_CNF_Formula(d_cm->getCurrentContext()); } } handle_result (result); if (result == SatSolver::UNSATISFIABLE) { d_theoryAPI->pop(); delete d_mng; delete d_cnf; if (d_mngStack.size() == 0) { createManager(); d_cnf = new CNF_Formula_Impl(); d_ready = true; } else { d_mng = d_mngStack.back(); d_mngStack.pop_back(); d_cnf = d_cnfStack.back(); d_cnfStack.pop_back(); DebugAssert(d_mngStack.size() == d_cnfStack.size(), "size mismatch"); } } return (result == SatSolver::UNSATISFIABLE ? UNSATISFIABLE : result == SatSolver::SATISFIABLE ? SATISFIABLE : ABORT); } QueryResult DPLLTBasic::continueCheck(const CNF_Formula& cnf) { SatSolver::SATStatus result; if (d_ready) { throw Exception ("continueCheck should be called after a previous satisfiable result"); } if (d_assertions->numClauses() > 0) { throw Exception ("a check cannot be continued if new assertions have been made"); } CNF_Formula_Impl cnfImpl(cnf); generate_CDB(cnfImpl); (*d_cnf) += cnfImpl; result = d_mng->Continue(); if (result == SatSolver::SATISFIABLE && theoryAPI()->outOfResources()) result = SatSolver::BUDGET_EXCEEDED; // Print result if (result == SatSolver::SATISFIABLE) verify_solution(); handle_result (result); if (result == SatSolver::UNSATISFIABLE) { d_theoryAPI->pop(); delete d_mng; delete d_cnf; if (d_mngStack.size() == 0) { createManager(); d_cnf = new CNF_Formula_Impl(); d_ready = true; } else { d_mng = d_mngStack.back(); d_mngStack.pop_back(); d_cnf = d_cnfStack.back(); d_cnfStack.pop_back(); DebugAssert(d_mngStack.size() == d_cnfStack.size(), "size mismatch"); } } return (result == SatSolver::UNSATISFIABLE ? UNSATISFIABLE : result == SatSolver::SATISFIABLE ? SATISFIABLE : ABORT); }