From 2a19474cdb6761fd4c9aeb0165e661c531ba3e38 Mon Sep 17 00:00:00 2001 From: Andrew Reynolds Date: Sun, 2 Dec 2018 08:49:17 -0600 Subject: Optimizations for PBE strings (#2728) --- src/theory/quantifiers/sygus/sygus_invariance.cpp | 19 ++- src/theory/quantifiers/sygus/sygus_invariance.h | 16 ++- src/theory/quantifiers/sygus/sygus_unif_io.cpp | 142 +++++++++++++++++++--- src/theory/quantifiers/sygus/sygus_unif_io.h | 25 +++- 4 files changed, 168 insertions(+), 34 deletions(-) diff --git a/src/theory/quantifiers/sygus/sygus_invariance.cpp b/src/theory/quantifiers/sygus/sygus_invariance.cpp index 24b47b216..5ea01ef57 100644 --- a/src/theory/quantifiers/sygus/sygus_invariance.cpp +++ b/src/theory/quantifiers/sygus/sygus_invariance.cpp @@ -218,15 +218,22 @@ bool NegContainsSygusInvarianceTest::invariant(TermDbSygus* tds, NodeManager::currentNM()->mkNode(kind::STRING_STRCTN, out, nbvre); Trace("sygus-pbe-cterm-debug") << "Check: " << cont << std::endl; Node contr = Rewriter::rewrite(cont); - if (contr == tds->d_false) + if (!contr.isConst()) + { + if (d_isUniversal) + { + return false; + } + } + else if (contr.getConst() == d_isUniversal) { if (Trace.isOn("sygus-pbe-cterm")) { Trace("sygus-pbe-cterm") << "PBE-cterm : enumerator : do not consider "; - Trace("sygus-pbe-cterm") << nbv << " for any " - << tds->sygusToBuiltin(x) << " since " - << std::endl; + Trace("sygus-pbe-cterm") + << nbv << " for any " << tds->sygusToBuiltin(x) << " since " + << std::endl; Trace("sygus-pbe-cterm") << " PBE-cterm : for input example : "; for (unsigned j = 0, size = d_ex[ii].size(); j < size; j++) { @@ -238,13 +245,13 @@ bool NegContainsSygusInvarianceTest::invariant(TermDbSygus* tds, Trace("sygus-pbe-cterm") << " PBE-cterm : and is not in output : " << out << std::endl; } - return true; + return !d_isUniversal; } Trace("sygus-pbe-cterm-debug2") << "...check failed, rewrites to : " << contr << std::endl; } } - return false; + return d_isUniversal; } } /* CVC4::theory::quantifiers namespace */ diff --git a/src/theory/quantifiers/sygus/sygus_invariance.h b/src/theory/quantifiers/sygus/sygus_invariance.h index 59761da5c..02c249411 100644 --- a/src/theory/quantifiers/sygus/sygus_invariance.h +++ b/src/theory/quantifiers/sygus/sygus_invariance.h @@ -249,7 +249,7 @@ class DivByZeroSygusInvarianceTest : public SygusInvarianceTest class NegContainsSygusInvarianceTest : public SygusInvarianceTest { public: - NegContainsSygusInvarianceTest() {} + NegContainsSygusInvarianceTest() : d_isUniversal(false) {} /** initialize this invariance test * e is the enumerator which we are reasoning about (associated with a synth @@ -266,9 +266,19 @@ class NegContainsSygusInvarianceTest : public SygusInvarianceTest std::vector >& ex, std::vector& exo, std::vector& ncind); + /** set universal + * + * This updates the semantics of this check such that *all* instead of some + * examples must fail the containment test. + */ + void setUniversal() { d_isUniversal = true; } protected: - /** checks if contains( out_i, nvn[in_i] ) --> false for some I/O pair i. */ + /** + * Checks if contains( out_i, nvn[in_i] ) --> false for some I/O pair i; if + * d_isUniversal is true, then we check if the rewrite holds for *all* I/O + * pairs. + */ bool invariant(TermDbSygus* tds, Node nvn, Node x) override; private: @@ -282,6 +292,8 @@ class NegContainsSygusInvarianceTest : public SygusInvarianceTest * contains( out_i, nvn[in_i] ) ---> false */ std::vector d_neg_con_indices; + /** requires not being in all examples */ + bool d_isUniversal; }; } /* CVC4::theory::quantifiers namespace */ diff --git a/src/theory/quantifiers/sygus/sygus_unif_io.cpp b/src/theory/quantifiers/sygus/sygus_unif_io.cpp index 89619639d..a6e6b54c6 100644 --- a/src/theory/quantifiers/sygus/sygus_unif_io.cpp +++ b/src/theory/quantifiers/sygus/sygus_unif_io.cpp @@ -431,10 +431,13 @@ void SubsumeTrie::getLeavesInternal(const std::vector& vals, { if (index == vals.size()) { + // by convention, if we did not test any points, then we consider the + // evaluation along the current path to be always false. + int rstatus = status == -2 ? -1 : status; Assert(!d_term.isNull()); - Assert(std::find(v[status].begin(), v[status].end(), d_term) - == v[status].end()); - v[status].push_back(d_term); + Assert(std::find(v[rstatus].begin(), v[rstatus].end(), d_term) + == v[rstatus].end()); + v[rstatus].push_back(d_term); } else { @@ -806,9 +809,13 @@ Node SygusUnifIo::constructSolutionNode(std::vector& lemmas) || (!d_solution.isNull() && d_tds->getSygusTermSize(vcc) < d_sol_term_size))) { - Trace("sygus-pbe") << "**** SygusUnif SOLVED : " << c << " = " << vcc - << std::endl; - Trace("sygus-pbe") << "...solved at iteration " << i << std::endl; + if (Trace.isOn("sygus-pbe")) + { + Trace("sygus-pbe") << "**** SygusUnif SOLVED : " << c << " = "; + TermDbSygus::toStreamSygus("sygus-pbe", vcc); + Trace("sygus-pbe") << std::endl; + Trace("sygus-pbe") << "...solved at iteration " << i << std::endl; + } d_solution = vcc; newSolution = vcc; d_sol_term_size = d_tds->getSygusTermSize(vcc); @@ -867,12 +874,12 @@ bool SygusUnifIo::useStrContainsEnumeratorExclude(Node e) d_use_str_contains_eexc[e] = false; return false; } + d_use_str_contains_eexc_conditional[e] = false; if (eis.isConditional()) { Trace("sygus-sui-enum-debug") << " conditional slave : " << sn << std::endl; - d_use_str_contains_eexc[e] = false; - return false; + d_use_str_contains_eexc_conditional[e] = true; } } Trace("sygus-sui-enum-debug") @@ -895,6 +902,9 @@ bool SygusUnifIo::getExplanationForEnumeratorExclude( // the output for some input/output pair. If so, then this term is never // useful. We generalize its explanation below. + // if the enumerator is in a conditional context, then we are stricter + // about when to exclude + bool isConditional = d_use_str_contains_eexc_conditional[e]; if (Trace.isOn("sygus-sui-cterm-debug")) { Trace("sygus-sui-enum") << std::endl; @@ -921,12 +931,20 @@ bool SygusUnifIo::getExplanationForEnumeratorExclude( else { Trace("sygus-sui-cterm-debug") << "...contained." << std::endl; + if (isConditional) + { + return false; + } } } if (!cmp_indices.empty()) { // we check invariance with respect to a negative contains test NegContainsSygusInvarianceTest ncset; + if (isConditional) + { + ncset.setUniversal(); + } ncset.init(e, d_examples, d_examples_out, cmp_indices); // construct the generalized explanation d_tds->getExplain()->getExplanationFor(e, v, exp, ncset); @@ -992,10 +1010,12 @@ Node SygusUnifIo::constructSol( EnumCache& ecache = d_ecache[e]; + bool retValMod = x.isReturnValueModified(); + Node ret_dt; if (nrole == role_equal) { - if (!x.isReturnValueModified()) + if (!retValMod) { if (ecache.isSolved()) { @@ -1069,11 +1089,67 @@ Node SygusUnifIo::constructSol( } } } + // maybe we can find one in the cache + if (ret_dt.isNull() && !retValMod) + { + bool firstTime = true; + std::unordered_set intersection; + std::map>::iterator + pit; + for (size_t i = 0, nvals = x.d_vals.size(); i < nvals; i++) + { + if (x.d_vals[i].getConst()) + { + pit = d_psolutions.find(i); + if (pit == d_psolutions.end()) + { + // no cached solution + intersection.clear(); + break; + } + if (firstTime) + { + intersection = pit->second; + firstTime = false; + } + else + { + std::vector rm; + for (const Node& a : intersection) + { + if (pit->second.find(a) == pit->second.end()) + { + rm.push_back(a); + } + } + for (const Node& a : rm) + { + intersection.erase(a); + } + if (intersection.empty()) + { + break; + } + } + } + } + if (!intersection.empty()) + { + ret_dt = *intersection.begin(); + if (Trace.isOn("sygus-sui-dt")) + { + indent("sygus-sui-dt", ind); + Trace("sygus-sui-dt") << "ConstructPBE: found in cache: "; + TermDbSygus::toStreamSygus("sygus-sui-dt", ret_dt); + Trace("sygus-sui-dt") << std::endl; + } + } + } } else if (nrole == role_string_prefix || nrole == role_string_suffix) { // check if each return value is a prefix/suffix of all open examples - if (!x.isReturnValueModified() || x.getCurrentRole() == nrole) + if (!retValMod || x.getCurrentRole() == nrole) { std::map > incr; bool isPrefix = nrole == role_string_prefix; @@ -1264,11 +1340,12 @@ Node SygusUnifIo::constructSol( Assert(set_split_cond_res_index); Assert(split_cond_res_index < ecache_cond.d_enum_vals_res.size()); prev = x.d_vals; - bool ret = x.updateContext( - this, - ecache_cond.d_enum_vals_res[split_cond_res_index], - sc == 1); - AlwaysAssert(ret); + x.updateContext(this, + ecache_cond.d_enum_vals_res[split_cond_res_index], + sc == 1); + // return value of above call may be false in corner cases where we + // must choose a non-separating condition to traverse to another + // strategy node } // recurse @@ -1284,7 +1361,7 @@ Node SygusUnifIo::constructSol( std::map solved_cond; // stores branch ecache_child.d_term_trie.getLeaves(x.d_vals, true, possible_cond); - std::map >::iterator itpc = + std::map>::iterator itpc = possible_cond.find(0); if (itpc != possible_cond.end()) { @@ -1301,8 +1378,6 @@ Node SygusUnifIo::constructSol( << d_tds->sygusToBuiltin(cond) << std::endl; } } - - // otherwise, guess a conditional if (rec_c.isNull()) { rec_c = constructBestConditional(ce, itpc->second); @@ -1381,8 +1456,35 @@ Node SygusUnifIo::constructSol( } Assert(ret_dt.isNull() || ret_dt.getType() == e.getType()); - indent("sygus-sui-dt", ind); - Trace("sygus-sui-dt") << "ConstructPBE: returned " << ret_dt << std::endl; + if (Trace.isOn("sygus-sui-dt")) + { + indent("sygus-sui-dt", ind); + Trace("sygus-sui-dt") << "ConstructPBE: returned "; + TermDbSygus::toStreamSygus("sygus-sui-dt", ret_dt); + Trace("sygus-sui-dt") << std::endl; + } + // remember the solution + if (nrole == role_equal) + { + if (!retValMod && !ret_dt.isNull()) + { + for (size_t i = 0, nvals = x.d_vals.size(); i < nvals; i++) + { + if (x.d_vals[i].getConst()) + { + if (Trace.isOn("sygus-sui-cache")) + { + indent("sygus-sui-cache", ind); + Trace("sygus-sui-cache") << "Cache solution (#" << i << ") : "; + TermDbSygus::toStreamSygus("sygus-sui-cache", ret_dt); + Trace("sygus-sui-cache") << std::endl; + } + d_psolutions[i].insert(ret_dt); + } + } + } + } + return ret_dt; } diff --git a/src/theory/quantifiers/sygus/sygus_unif_io.h b/src/theory/quantifiers/sygus/sygus_unif_io.h index 2f87c0552..f189353b0 100644 --- a/src/theory/quantifiers/sygus/sygus_unif_io.h +++ b/src/theory/quantifiers/sygus/sygus_unif_io.h @@ -183,12 +183,14 @@ class SubsumeTrie bool pol, std::vector& subsumed_by); /** - * Get the leaves of the trie, which we store in the map v. - * v[-1] stores the children that always evaluate to !pol, - * v[1] stores the children that always evaluate to pol, - * v[0] stores the children that both evaluate to true and false for at least - * one example. - */ + * Get the leaves of the trie, which we store in the map v. We consider their + * evaluation on points such that (pol ? vals : !vals) is true. + * + * v[-1] stores the children that always evaluate to !pol, + * v[1] stores the children that always evaluate to pol, + * v[0] stores the children that both evaluate to true and false for at least + * one example. + */ void getLeaves(const std::vector& vals, bool pol, std::map>& v); @@ -300,6 +302,11 @@ class SygusUnifIo : public SygusUnif Node d_solution; /** the term size of the above solution */ unsigned d_sol_term_size; + /** partial solutions + * + * Maps indices for I/O points to a list of solutions for that point. + */ + std::map> d_psolutions; /** * This flag is set to true if the solution construction was * non-deterministic with respect to failure/success. @@ -427,6 +434,12 @@ class SygusUnifIo : public SygusUnif bool useStrContainsEnumeratorExclude(Node e); /** cache for the above function */ std::map d_use_str_contains_eexc; + /** + * cache for the above function, stores whether enumerators e are in + * a conditional context, e.g. used for enumerating the return values for + * leaves of ITE trees. + */ + std::map d_use_str_contains_eexc_conditional; /** the unification context used within constructSolution */ UnifContextIo d_context; -- cgit v1.2.3 From aa0a875dfd40bd9dfa810238327db51498b74677 Mon Sep 17 00:00:00 2001 From: Alex Ozdemir Date: Mon, 3 Dec 2018 11:56:47 -0800 Subject: Bit vector proof superclass (#2599) * Split BitvectorProof into a sub/superclass The superclass contains general printing knowledge. The subclass contains CNF or Resolution-specific knowledge. * Renames & code moves * Nits cleaned in prep for PR * Moved CNF-proof from ResolutionBitVectorProof to BitVectorProof Since DRAT BV proofs will also contain a CNF-proof, the CNF proof should be stored in `BitVectorProof`. * Unique pointers, comments, and code movement. Adjusted the distribution of code between BVP and RBVP. Notably, put the CNF proof in BVP because it isn't resolution-specific. Added comments to the headers of both files -- mostly BVP. Changed two owned pointers into unique_ptr. BVP's pointer to a CNF proof RBVP's pointer to a resolution proof BVP: `BitVectorProof` RBVP: `ResolutionBitVectorProof` * clang-format * Undo manual copyright modification * s/superclass/base class/ Co-Authored-By: alex-ozdemir * make LFSCBitVectorProof::printOwnedSort public * Andres's Comments Mostly cleaning up (or trying to clean up) includes. * Cleaned up one header cycle However, this only allowed me to move the forward-decl, not eliminate it, because there were actually two underlying include cycles that the forward-decl solved. * Added single _s to header gaurds * Fix Class name in debug output Credits to Andres Co-Authored-By: alex-ozdemir * Reordered methods in BitVectorProof per original ordering --- src/CMakeLists.txt | 3 + src/proof/bitvector_proof.cpp | 567 ++++++---------------------- src/proof/bitvector_proof.h | 219 ++++++----- src/proof/proof_manager.cpp | 7 +- src/proof/proof_manager.h | 8 +- src/proof/resolution_bitvector_proof.cpp | 522 +++++++++++++++++++++++++ src/proof/resolution_bitvector_proof.h | 132 +++++++ src/proof/theory_proof.cpp | 16 +- src/prop/bv_sat_solver_notify.h | 49 +++ src/prop/bvminisat/bvminisat.cpp | 5 +- src/prop/bvminisat/bvminisat.h | 10 +- src/prop/bvminisat/core/Solver.cc | 5 +- src/prop/bvminisat/core/Solver.h | 13 +- src/prop/sat_solver.h | 31 +- src/prop/sat_solver_types.h | 3 +- src/theory/bv/bitblast/aig_bitblaster.h | 1 + src/theory/bv/bitblast/bitblaster.h | 9 +- src/theory/bv/bitblast/eager_bitblaster.cpp | 11 +- src/theory/bv/bitblast/eager_bitblaster.h | 6 +- src/theory/bv/bitblast/lazy_bitblaster.cpp | 17 +- src/theory/bv/bitblast/lazy_bitblaster.h | 10 +- src/theory/bv/bv_eager_solver.cpp | 9 +- src/theory/bv/bv_eager_solver.h | 5 +- src/theory/bv/bv_subtheory.h | 9 +- src/theory/bv/bv_subtheory_bitblast.cpp | 4 +- src/theory/bv/bv_subtheory_bitblast.h | 7 +- src/theory/bv/theory_bv.cpp | 5 +- src/theory/bv/theory_bv.h | 4 +- 28 files changed, 1061 insertions(+), 626 deletions(-) create mode 100644 src/proof/resolution_bitvector_proof.cpp create mode 100644 src/proof/resolution_bitvector_proof.h create mode 100644 src/prop/bv_sat_solver_notify.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9a1cfe9e7..9e93bd953 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -142,6 +142,8 @@ libcvc4_add_sources( proof/proof_output_channel.h proof/proof_utils.cpp proof/proof_utils.h + proof/resolution_bitvector_proof.cpp + proof/resolution_bitvector_proof.h proof/sat_proof.h proof/sat_proof_implementation.h proof/simplify_boolean_node.cpp @@ -202,6 +204,7 @@ libcvc4_add_sources( prop/sat_solver.h prop/sat_solver_factory.cpp prop/sat_solver_factory.h + prop/bv_sat_solver_notify.h prop/sat_solver_types.h prop/theory_proxy.cpp prop/theory_proxy.h diff --git a/src/proof/bitvector_proof.cpp b/src/proof/bitvector_proof.cpp index 8f001ffa1..c9e98d170 100644 --- a/src/proof/bitvector_proof.cpp +++ b/src/proof/bitvector_proof.cpp @@ -9,31 +9,19 @@ ** All rights reserved. See the file COPYING in the top-level source ** directory for licensing information.\endverbatim ** - ** [[ Add lengthier description here ]] - - ** \todo document this file - -**/ + ** Contains implementions (e.g. code for printing bitblasting bindings that is + ** common to all kinds of bitvector proofs. + **/ #include "proof/bitvector_proof.h" #include "options/bv_options.h" #include "options/proof_options.h" -#include "proof/array_proof.h" -#include "proof/clause_id.h" -#include "proof/lfsc_proof_printer.h" #include "proof/proof_output_channel.h" -#include "proof/proof_utils.h" -#include "proof/sat_proof_implementation.h" -#include "prop/bvminisat/bvminisat.h" +#include "proof/theory_proof.h" #include "theory/bv/bitblast/bitblaster.h" #include "theory/bv/theory_bv.h" -#include "theory/bv/theory_bv_rewrite_rules.h" - -using namespace CVC4::theory; -using namespace CVC4::theory::bv; namespace CVC4 { - BitVectorProof::BitVectorProof(theory::bv::TheoryBV* bv, TheoryProofEngine* proofEngine) : TheoryProof(bv, proofEngine), @@ -41,73 +29,44 @@ BitVectorProof::BitVectorProof(theory::bv::TheoryBV* bv, d_seenBBTerms(), d_bbTerms(), d_bbAtoms(), - d_resolutionProof(NULL), - d_cnfProof(NULL), - d_isAssumptionConflict(false), - d_bitblaster(NULL), - d_useConstantLetification(false) {} - -void BitVectorProof::initSatProof(CVC4::BVMinisat::Solver* solver) { - Assert (d_resolutionProof == NULL); - d_resolutionProof = new BVSatProof(solver, &d_fakeContext, "bb", true); -} - -theory::TheoryId BitVectorProof::getTheoryId() { return theory::THEORY_BV; } -void BitVectorProof::initCnfProof(prop::CnfStream* cnfStream, - context::Context* cnf) { - Assert (d_resolutionProof != NULL); - Assert (d_cnfProof == NULL); - d_cnfProof = new LFSCCnfProof(cnfStream, cnf, "bb"); - - // true and false have to be setup in a special way - Node true_node = NodeManager::currentNM()->mkConst(true); - Node false_node = NodeManager::currentNM()->mkConst(false).notNode(); - - d_cnfProof->pushCurrentAssertion(true_node); - d_cnfProof->pushCurrentDefinition(true_node); - d_cnfProof->registerConvertedClause(d_resolutionProof->getTrueUnit()); - d_cnfProof->popCurrentAssertion(); - d_cnfProof->popCurrentDefinition(); - - d_cnfProof->pushCurrentAssertion(false_node); - d_cnfProof->pushCurrentDefinition(false_node); - d_cnfProof->registerConvertedClause(d_resolutionProof->getFalseUnit()); - d_cnfProof->popCurrentAssertion(); - d_cnfProof->popCurrentDefinition(); + d_bitblaster(nullptr), + d_useConstantLetification(false), + d_cnfProof() +{ } -void BitVectorProof::setBitblaster(bv::TBitblaster* bb) { - Assert (d_bitblaster == NULL); +void BitVectorProof::setBitblaster(theory::bv::TBitblaster* bb) +{ + Assert(d_bitblaster == NULL); d_bitblaster = bb; } -BVSatProof* BitVectorProof::getSatProof() { - Assert (d_resolutionProof != NULL); - return d_resolutionProof; -} - -void BitVectorProof::registerTermBB(Expr term) { - Debug("pf::bv") << "BitVectorProof::registerTermBB( " << term << " )" << std::endl; +void BitVectorProof::registerTermBB(Expr term) +{ + Debug("pf::bv") << "BitVectorProof::registerTermBB( " << term + << " )" << std::endl; - if (d_seenBBTerms.find(term) != d_seenBBTerms.end()) - return; + if (d_seenBBTerms.find(term) != d_seenBBTerms.end()) return; d_seenBBTerms.insert(term); d_bbTerms.push_back(term); - // If this term gets used in the final proof, we will want to register it. However, - // we don't know this at this point; and when the theory proof engine sees it, if it belongs - // to another theory, it won't register it with this proof. So, we need to tell the - // engine to inform us. + // If this term gets used in the final proof, we will want to register it. + // However, we don't know this at this point; and when the theory proof engine + // sees it, if it belongs to another theory, it won't register it with this + // proof. So, we need to tell the engine to inform us. - if (theory::Theory::theoryOf(term) != theory::THEORY_BV) { - Debug("pf::bv") << "\tMarking term " << term << " for future BV registration" << std::endl; + if (theory::Theory::theoryOf(term) != theory::THEORY_BV) + { + Debug("pf::bv") << "\tMarking term " << term + << " for future BV registration" << std::endl; d_proofEngine->markTermForFutureRegistration(term, theory::THEORY_BV); } } void BitVectorProof::registerAtomBB(Expr atom, Expr atom_bb) { - Debug("pf::bv") << "BitVectorProof::registerAtomBB( " << atom << ", " << atom_bb << " )" << std::endl; + Debug("pf::bv") << "BitVectorProof::registerAtomBB( " << atom + << ", " << atom_bb << " )" << std::endl; Expr def = atom.iffExpr(atom_bb); d_bbAtoms.insert(std::make_pair(atom, def)); @@ -119,7 +78,8 @@ void BitVectorProof::registerAtomBB(Expr atom, Expr atom_bb) { } void BitVectorProof::registerTerm(Expr term) { - Debug("pf::bv") << "BitVectorProof::registerTerm( " << term << " )" << std::endl; + Debug("pf::bv") << "BitVectorProof::registerTerm( " << term << " )" + << std::endl; if (options::lfscLetification() && term.isConst()) { if (d_constantLetMap.find(term) == d_constantLetMap.end()) { @@ -131,8 +91,8 @@ void BitVectorProof::registerTerm(Expr term) { d_usedBB.insert(term); - if (Theory::isLeafOf(term, theory::THEORY_BV) && - !term.isConst()) { + if (theory::Theory::isLeafOf(term, theory::THEORY_BV) && !term.isConst()) + { d_declarations.insert(term); } @@ -147,149 +107,32 @@ void BitVectorProof::registerTerm(Expr term) { } } -std::string BitVectorProof::getBBTermName(Expr expr) { - Debug("pf::bv") << "BitVectorProof::getBBTermName( " << expr << " ) = bt" << expr.getId() << std::endl; +std::string BitVectorProof::getBBTermName(Expr expr) +{ + Debug("pf::bv") << "BitVectorProof::getBBTermName( " << expr << " ) = bt" + << expr.getId() << std::endl; std::ostringstream os; - os << "bt"<< expr.getId(); + os << "bt" << expr.getId(); return os.str(); } -void BitVectorProof::startBVConflict(CVC4::BVMinisat::Solver::TCRef cr) { - d_resolutionProof->startResChain(cr); -} - -void BitVectorProof::startBVConflict(CVC4::BVMinisat::Solver::TLit lit) { - d_resolutionProof->startResChain(lit); -} - -void BitVectorProof::endBVConflict(const CVC4::BVMinisat::Solver::TLitVec& confl) { - Debug("pf::bv") << "BitVectorProof::endBVConflict called" << std::endl; - - std::vector expr_confl; - for (int i = 0; i < confl.size(); ++i) { - prop::SatLiteral lit = prop::BVMinisatSatSolver::toSatLiteral(confl[i]); - Expr atom = d_cnfProof->getAtom(lit.getSatVariable()).toExpr(); - Expr expr_lit = lit.isNegated() ? atom.notExpr() : atom; - expr_confl.push_back(expr_lit); - } - - Expr conflict = utils::mkSortedExpr(kind::OR, expr_confl); - Debug("pf::bv") << "Make conflict for " << conflict << std::endl; - - if (d_bbConflictMap.find(conflict) != d_bbConflictMap.end()) { - Debug("pf::bv") << "Abort...already conflict for " << conflict << std::endl; - // This can only happen when we have eager explanations in the bv solver - // if we don't get to propagate p before ~p is already asserted - d_resolutionProof->cancelResChain(); - return; - } - - // we don't need to check for uniqueness in the sat solver then - ClauseId clause_id = d_resolutionProof->registerAssumptionConflict(confl); - d_bbConflictMap[conflict] = clause_id; - d_resolutionProof->endResChain(clause_id); - Debug("pf::bv") << "BitVectorProof::endBVConflict id" < " << conflict << "\n"; - d_isAssumptionConflict = false; -} - -void BitVectorProof::finalizeConflicts(std::vector& conflicts) { - - if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { - Debug("pf::bv") << "Construct full proof." << std::endl; - d_resolutionProof->constructProof(); - return; - } - - for (unsigned i = 0; i < conflicts.size(); ++i) { - Expr confl = conflicts[i]; - Debug("pf::bv") << "Finalize conflict #" << i << ": " << confl << std::endl; - - // Special case: if the conflict has a (true) or a (not false) in it, it is trivial... - bool ignoreConflict = false; - if ((confl.isConst() && confl.getConst()) || - (confl.getKind() == kind::NOT && confl[0].isConst() && !confl[0].getConst())) { - ignoreConflict = true; - } else if (confl.getKind() == kind::OR) { - for (unsigned k = 0; k < confl.getNumChildren(); ++k) { - if ((confl[k].isConst() && confl[k].getConst()) || - (confl[k].getKind() == kind::NOT && confl[k][0].isConst() && !confl[k][0].getConst())) { - ignoreConflict = true; - } - } - } - if (ignoreConflict) { - Debug("pf::bv") << "Ignoring conflict due to (true) or (not false)" << std::endl; - continue; - } - - if (d_bbConflictMap.find(confl) != d_bbConflictMap.end()) { - ClauseId id = d_bbConflictMap[confl]; - d_resolutionProof->collectClauses(id); - } else { - // There is no exact match for our conflict, but maybe it is a subset of another conflict - ExprToClauseId::const_iterator it; - bool matchFound = false; - for (it = d_bbConflictMap.begin(); it != d_bbConflictMap.end(); ++it) { - Expr possibleMatch = it->first; - if (possibleMatch.getKind() != kind::OR) { - // This is a single-node conflict. If this node is in the conflict we're trying to prove, - // we have a match. - for (unsigned k = 0; k < confl.getNumChildren(); ++k) { - if (confl[k] == possibleMatch) { - matchFound = true; - d_resolutionProof->collectClauses(it->second); - break; - } - } - } else { - if (possibleMatch.getNumChildren() > confl.getNumChildren()) - continue; - - unsigned k = 0; - bool matching = true; - for (unsigned j = 0; j < possibleMatch.getNumChildren(); ++j) { - // j is the index in possibleMatch - // k is the index in confl - while (k < confl.getNumChildren() && confl[k] != possibleMatch[j]) { - ++k; - } - if (k == confl.getNumChildren()) { - // We couldn't find a match for possibleMatch[j], so not a match - matching = false; - break; - } - } - - if (matching) { - Debug("pf::bv") << "Collecting info from a sub-conflict" << std::endl; - d_resolutionProof->collectClauses(it->second); - matchFound = true; - break; - } - } - } - - if (!matchFound) { - Debug("pf::bv") << "Do not collect clauses for " << confl << std::endl - << "Dumping existing conflicts:" << std::endl; - - i = 0; - for (it = d_bbConflictMap.begin(); it != d_bbConflictMap.end(); ++it) { - ++i; - Debug("pf::bv") << "\tConflict #" << i << ": " << it->first << std::endl; - } - - Unreachable(); - } - } - } +void BitVectorProof::initCnfProof(prop::CnfStream* cnfStream, + context::Context* cnf) +{ + Assert(d_cnfProof == nullptr); + d_cnfProof.reset(new LFSCCnfProof(cnfStream, cnf, "bb")); } -void LFSCBitVectorProof::printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map) { - Debug("pf::bv") << std::endl << "(pf::bv) LFSCBitVectorProof::printOwnedTerm( " << term << " ), theory is: " - << Theory::theoryOf(term) << std::endl; +void BitVectorProof::printOwnedTerm(Expr term, + std::ostream& os, + const ProofLetMap& map) +{ + Debug("pf::bv") << std::endl + << "(pf::bv) BitVectorProof::printOwnedTerm( " << term + << " ), theory is: " << theory::Theory::theoryOf(term) + << std::endl; - Assert (Theory::theoryOf(term) == THEORY_BV); + Assert(theory::Theory::theoryOf(term) == theory::THEORY_BV); // peel off eager bit-blasting trick if (term.getKind() == kind::BITVECTOR_EAGER_ATOM) { @@ -380,21 +223,24 @@ void LFSCBitVectorProof::printOwnedTerm(Expr term, std::ostream& os, const Proof } } -void LFSCBitVectorProof::printBitOf(Expr term, std::ostream& os, const ProofLetMap& map) { +void BitVectorProof::printBitOf(Expr term, + std::ostream& os, + const ProofLetMap& map) +{ Assert (term.getKind() == kind::BITVECTOR_BITOF); unsigned bit = term.getOperator().getConst().bitIndex; Expr var = term[0]; - Debug("pf::bv") << "LFSCBitVectorProof::printBitOf( " << term << " ), " - << "bit = " << bit - << ", var = " << var << std::endl; + Debug("pf::bv") << "BitVectorProof::printBitOf( " << term << " ), " + << "bit = " << bit << ", var = " << var << std::endl; os << "(bitof "; os << d_exprToVariableName[var]; os << " " << bit << ")"; } -void LFSCBitVectorProof::printConstant(Expr term, std::ostream& os) { +void BitVectorProof::printConstant(Expr term, std::ostream& os) +{ Assert (term.isConst()); os << "(a_bv " << utils::getSize(term) << " "; @@ -413,7 +259,10 @@ void LFSCBitVectorProof::printConstant(Expr term, std::ostream& os) { } } -void LFSCBitVectorProof::printOperatorNary(Expr term, std::ostream& os, const ProofLetMap& map) { +void BitVectorProof::printOperatorNary(Expr term, + std::ostream& os, + const ProofLetMap& map) +{ std::string op = utils::toLFSCKindTerm(term); std::ostringstream paren; std::string holes = term.getKind() == kind::BITVECTOR_CONCAT ? "_ _ " : ""; @@ -431,7 +280,10 @@ void LFSCBitVectorProof::printOperatorNary(Expr term, std::ostream& os, const Pr } } -void LFSCBitVectorProof::printOperatorUnary(Expr term, std::ostream& os, const ProofLetMap& map) { +void BitVectorProof::printOperatorUnary(Expr term, + std::ostream& os, + const ProofLetMap& map) +{ os <<"("; os << utils::toLFSCKindTerm(term) << " " << utils::getSize(term) <<" "; os << " "; @@ -439,7 +291,10 @@ void LFSCBitVectorProof::printOperatorUnary(Expr term, std::ostream& os, const P os <<")"; } -void LFSCBitVectorProof::printPredicate(Expr term, std::ostream& os, const ProofLetMap& map) { +void BitVectorProof::printPredicate(Expr term, + std::ostream& os, + const ProofLetMap& map) +{ os <<"("; os << utils::toLFSCKindTerm(term) << " " << utils::getSize(term[0]) <<" "; os << " "; @@ -449,7 +304,10 @@ void LFSCBitVectorProof::printPredicate(Expr term, std::ostream& os, const Proof os <<")"; } -void LFSCBitVectorProof::printOperatorParametric(Expr term, std::ostream& os, const ProofLetMap& map) { +void BitVectorProof::printOperatorParametric(Expr term, + std::ostream& os, + const ProofLetMap& map) +{ os <<"("; os << utils::toLFSCKindTerm(term) << " " << utils::getSize(term) <<" "; os <<" "; @@ -477,185 +335,25 @@ void LFSCBitVectorProof::printOperatorParametric(Expr term, std::ostream& os, co os <<")"; } -void LFSCBitVectorProof::printOwnedSort(Type type, std::ostream& os) { - Debug("pf::bv") << std::endl << "(pf::bv) LFSCBitVectorProof::printOwnedSort( " << type << " )" << std::endl; +void BitVectorProof::printOwnedSort(Type type, std::ostream& os) +{ + Debug("pf::bv") << std::endl + << "(pf::bv) BitVectorProof::printOwnedSort( " << type << " )" + << std::endl; Assert (type.isBitVector()); unsigned width = utils::getSize(type); os << "(BitVec " << width << ")"; } -void LFSCBitVectorProof::printTheoryLemmaProof(std::vector& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map) { - Debug("pf::bv") << "(pf::bv) LFSCBitVectorProof::printTheoryLemmaProof called" << std::endl; - Expr conflict = utils::mkSortedExpr(kind::OR, lemma); - Debug("pf::bv") << "\tconflict = " << conflict << std::endl; - - if (d_bbConflictMap.find(conflict) != d_bbConflictMap.end()) { - std::ostringstream lemma_paren; - for (unsigned i = 0; i < lemma.size(); ++i) { - Expr lit = lemma[i]; - - if (lit.getKind() == kind::NOT) { - os << "(intro_assump_t _ _ _ "; - } else { - os << "(intro_assump_f _ _ _ "; - } - lemma_paren <<")"; - // print corresponding literal in main sat solver - ProofManager* pm = ProofManager::currentPM(); - CnfProof* cnf = pm->getCnfProof(); - prop::SatLiteral main_lit = cnf->getLiteral(lit); - os << pm->getLitName(main_lit); - os <<" "; - // print corresponding literal in bv sat solver - prop::SatVariable bb_var = d_cnfProof->getLiteral(lit).getSatVariable(); - os << pm->getAtomName(bb_var, "bb"); - os <<"(\\ unit"<first; - - if (possibleMatch.getKind() != kind::OR) { - // This is a single-node conflict. If this node is in the conflict we're trying to prove, - // we have a match. - matching = false; - - for (unsigned k = 0; k < conflict.getNumChildren(); ++k) { - if (conflict[k] == possibleMatch) { - matching = true; - break; - } - } - } else { - if (possibleMatch.getNumChildren() > conflict.getNumChildren()) - continue; - - unsigned k = 0; - - matching = true; - for (unsigned j = 0; j < possibleMatch.getNumChildren(); ++j) { - // j is the index in possibleMatch - // k is the index in conflict - while (k < conflict.getNumChildren() && conflict[k] != possibleMatch[j]) { - ++k; - } - if (k == conflict.getNumChildren()) { - // We couldn't find a match for possibleMatch[j], so not a match - matching = false; - break; - } - } - } - - if (matching) { - Debug("pf::bv") << "Found a match with conflict #" << i << ": " << std::endl << possibleMatch << std::endl; - // The rest is just a copy of the usual handling, if a precise match is found. - // We only use the literals that appear in the matching conflict, though, and not in the - // original lemma - as these may not have even been bit blasted! - std::ostringstream lemma_paren; - - if (possibleMatch.getKind() == kind::OR) { - for (unsigned i = 0; i < possibleMatch.getNumChildren(); ++i) { - Expr lit = possibleMatch[i]; - - if (lit.getKind() == kind::NOT) { - os << "(intro_assump_t _ _ _ "; - } else { - os << "(intro_assump_f _ _ _ "; - } - lemma_paren <<")"; - // print corresponding literal in main sat solver - ProofManager* pm = ProofManager::currentPM(); - CnfProof* cnf = pm->getCnfProof(); - prop::SatLiteral main_lit = cnf->getLiteral(lit); - os << pm->getLitName(main_lit); - os <<" "; - // print corresponding literal in bv sat solver - prop::SatVariable bb_var = d_cnfProof->getLiteral(lit).getSatVariable(); - os << pm->getAtomName(bb_var, "bb"); - os <<"(\\ unit"<getCnfProof(); - prop::SatLiteral main_lit = cnf->getLiteral(lit); - os << pm->getLitName(main_lit); - os <<" "; - // print corresponding literal in bv sat solver - prop::SatVariable bb_var = d_cnfProof->getLiteral(lit).getSatVariable(); - os << pm->getAtomName(bb_var, "bb"); - os <<"(\\ unit"<second; - proof::LFSCProofPrinter::printAssumptionsResolution( - d_resolutionProof, lemma_id, os, lemma_paren); - os <first << std::endl; - } - - Unreachable(); - } -} - -void LFSCBitVectorProof::printSortDeclarations(std::ostream& os, std::ostream& paren) { +void BitVectorProof::printSortDeclarations(std::ostream& os, + std::ostream& paren) +{ // Nothing to do here at this point. } -void LFSCBitVectorProof::printTermDeclarations(std::ostream& os, std::ostream& paren) { +void BitVectorProof::printTermDeclarations(std::ostream& os, + std::ostream& paren) +{ ExprSet::const_iterator it = d_declarations.begin(); ExprSet::const_iterator end = d_declarations.end(); for (; it != end; ++it) { @@ -671,7 +369,9 @@ void LFSCBitVectorProof::printTermDeclarations(std::ostream& os, std::ostream& p } } -void LFSCBitVectorProof::printDeferredDeclarations(std::ostream& os, std::ostream& paren) { +void BitVectorProof::printDeferredDeclarations(std::ostream& os, + std::ostream& paren) +{ if (options::lfscLetification()) { os << std::endl << ";; BV const letification\n" << std::endl; std::map::const_iterator it; @@ -694,7 +394,10 @@ void LFSCBitVectorProof::printDeferredDeclarations(std::ostream& os, std::ostrea } } -void LFSCBitVectorProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) { +void BitVectorProof::printAliasingDeclarations(std::ostream& os, + std::ostream& paren, + const ProofLetMap& globalLetMap) +{ // Print "trust" statements to bind complex bv variables to their associated terms ExprToString::const_iterator it = d_assignedAliases.begin(); @@ -720,13 +423,15 @@ void LFSCBitVectorProof::printAliasingDeclarations(std::ostream& os, std::ostrea os << "\n"; } -void LFSCBitVectorProof::printTermBitblasting(Expr term, std::ostream& os) { +void BitVectorProof::printTermBitblasting(Expr term, std::ostream& os) +{ // TODO: once we have the operator elimination rules remove those that we // eliminated Assert (term.getType().isBitVector()); Kind kind = term.getKind(); - if (Theory::isLeafOf(term, theory::THEORY_BV) && !term.isConst()) { + if (theory::Theory::isLeafOf(term, theory::THEORY_BV) && !term.isConst()) + { // A term is a leaf if it has no children, or if it belongs to another theory os << "(bv_bbl_var " << utils::getSize(term) << " " << d_exprToVariableName[term]; os << " _)"; @@ -857,12 +562,14 @@ void LFSCBitVectorProof::printTermBitblasting(Expr term, std::ostream& os) { return; } - default: - Unreachable("LFSCBitVectorProof Unknown operator"); + default: Unreachable("BitVectorProof Unknown operator"); } } -void LFSCBitVectorProof::printAtomBitblasting(Expr atom, std::ostream& os, bool swap) { +void BitVectorProof::printAtomBitblasting(Expr atom, + std::ostream& os, + bool swap) +{ Kind kind = atom.getKind(); switch(kind) { case kind::BITVECTOR_ULT : @@ -888,12 +595,12 @@ void LFSCBitVectorProof::printAtomBitblasting(Expr atom, std::ostream& os, bool return; } - default: - Unreachable("LFSCBitVectorProof Unknown atom kind"); + default: Unreachable("BitVectorProof Unknown atom kind"); } } -void LFSCBitVectorProof::printAtomBitblastingToFalse(Expr atom, std::ostream& os) { +void BitVectorProof::printAtomBitblastingToFalse(Expr atom, std::ostream& os) +{ Assert(atom.getKind() == kind::EQUAL); os << "(bv_bbl_=_false"; @@ -907,10 +614,13 @@ void LFSCBitVectorProof::printAtomBitblastingToFalse(Expr atom, std::ostream& os os << ")"; } -void LFSCBitVectorProof::printBitblasting(std::ostream& os, std::ostream& paren) { +void BitVectorProof::printBitblasting(std::ostream& os, std::ostream& paren) +{ // bit-blast terms { - Debug("pf::bv") << "LFSCBitVectorProof::printBitblasting: the bitblasted terms are: " << std::endl; + Debug("pf::bv") + << "BitVectorProof::printBitblasting: the bitblasted terms are: " + << std::endl; std::vector::const_iterator it = d_bbTerms.begin(); std::vector::const_iterator end = d_bbTerms.end(); @@ -999,52 +709,13 @@ void LFSCBitVectorProof::printBitblasting(std::ostream& os, std::ostream& paren) } } -void LFSCBitVectorProof::calculateAtomsInBitblastingProof() { - // Collect the input clauses used - IdToSatClause used_lemmas; - IdToSatClause used_inputs; - d_resolutionProof->collectClausesUsed(used_inputs, used_lemmas); - d_cnfProof->collectAtomsForClauses(used_inputs, d_atomsInBitblastingProof); - Assert(used_lemmas.empty()); -} - -const std::set* LFSCBitVectorProof::getAtomsInBitblastingProof() { +const std::set* BitVectorProof::getAtomsInBitblastingProof() +{ return &d_atomsInBitblastingProof; } -void LFSCBitVectorProof::printResolutionProof(std::ostream& os, - std::ostream& paren, - ProofLetMap& letMap) { - // print mapping between theory atoms and internal SAT variables - os << std::endl << ";; BB atom mapping\n" << std::endl; - - std::set::iterator atomIt; - Debug("pf::bv") << std::endl << "BV Dumping atoms from inputs: " << std::endl << std::endl; - for (atomIt = d_atomsInBitblastingProof.begin(); atomIt != d_atomsInBitblastingProof.end(); ++atomIt) { - Debug("pf::bv") << "\tAtom: " << *atomIt << std::endl; - } - Debug("pf::bv") << std::endl; - - // first print bit-blasting - printBitblasting(os, paren); - - // print CNF conversion proof for bit-blasted facts - IdToSatClause used_lemmas; - IdToSatClause used_inputs; - d_resolutionProof->collectClausesUsed(used_inputs, used_lemmas); - - d_cnfProof->printAtomMapping(d_atomsInBitblastingProof, os, paren, letMap); - os << std::endl << ";; Bit-blasting definitional clauses \n" << std::endl; - for (IdToSatClause::iterator it = used_inputs.begin(); - it != used_inputs.end(); ++it) { - d_cnfProof->printCnfProofForClause(it->first, it->second, os, paren); - } - - os << std::endl << " ;; Bit-blasting learned clauses \n" << std::endl; - proof::LFSCProofPrinter::printResolutions(d_resolutionProof, os, paren); -} - -std::string LFSCBitVectorProof::assignAlias(Expr expr) { +std::string BitVectorProof::assignAlias(Expr expr) +{ Assert(d_exprToVariableName.find(expr) == d_exprToVariableName.end()); std::stringstream ss; @@ -1054,11 +725,14 @@ std::string LFSCBitVectorProof::assignAlias(Expr expr) { return ss.str(); } -bool LFSCBitVectorProof::hasAlias(Expr expr) { +bool BitVectorProof::hasAlias(Expr expr) +{ return d_assignedAliases.find(expr) != d_assignedAliases.end(); } -void LFSCBitVectorProof::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap) { +void BitVectorProof::printConstantDisequalityProof( + std::ostream& os, Expr c1, Expr c2, const ProofLetMap& globalLetMap) +{ Assert (c1.isConst()); Assert (c2.isConst()); Assert (utils::getSize(c1) == utils::getSize(c2)); @@ -1088,7 +762,10 @@ void LFSCBitVectorProof::printConstantDisequalityProof(std::ostream& os, Expr c1 os << ")"; } -void LFSCBitVectorProof::printRewriteProof(std::ostream& os, const Node &n1, const Node &n2) { +void BitVectorProof::printRewriteProof(std::ostream& os, + const Node& n1, + const Node& n2) +{ ProofLetMap emptyMap; os << "(rr_bv_default "; d_proofEngine->printBoundTerm(n2.toExpr(), os, emptyMap); @@ -1097,4 +774,4 @@ void LFSCBitVectorProof::printRewriteProof(std::ostream& os, const Node &n1, con os << ")"; } -} /* namespace CVC4 */ +} // namespace CVC4 diff --git a/src/proof/bitvector_proof.h b/src/proof/bitvector_proof.h index 63f1cdf63..466efa6a7 100644 --- a/src/proof/bitvector_proof.h +++ b/src/proof/bitvector_proof.h @@ -9,118 +9,166 @@ ** All rights reserved. See the file COPYING in the top-level source ** directory for licensing information.\endverbatim ** - ** \brief Bitvector proof + ** \brief Bitvector proof base class ** - ** Bitvector proof + ** Contains code (e.g. proof printing code) which is common to all bitvector + *proofs. **/ #include "cvc4_private.h" -#ifndef __CVC4__BITVECTOR__PROOF_H -#define __CVC4__BITVECTOR__PROOF_H +#ifndef __CVC4__BITVECTOR_PROOF_H +#define __CVC4__BITVECTOR_PROOF_H -#include #include -#include #include #include #include - #include "expr/expr.h" +#include "proof/cnf_proof.h" #include "proof/theory_proof.h" -#include "prop/bvminisat/core/Solver.h" - +#include "theory/bv/bitblast/bitblaster.h" +#include "theory/bv/theory_bv.h" namespace CVC4 { -namespace prop { -class CnfStream; -} /* namespace CVC4::prop */ - -namespace theory { -namespace bv { -class TheoryBV; -template class TBitblaster; -} /* namespace CVC4::theory::bv */ -} /* namespace CVC4::theory */ - -class CnfProof; -} /* namespace CVC4 */ - -namespace CVC4 { - -template class TSatProof; -typedef TSatProof< CVC4::BVMinisat::Solver> BVSatProof; - typedef std::unordered_set ExprSet; typedef std::unordered_map ExprToClauseId; typedef std::unordered_map ExprToId; typedef std::unordered_map ExprToExpr; typedef std::unordered_map ExprToString; -class BitVectorProof : public TheoryProof { -protected: +/** + * A bitvector proof is best understood as having + * + * 1. A declaration of a "bitblasted formulas" -- boolean formulas + * that are each translations of a BV-literal (a comparison between BVs). + * + * (and a proof that each "bitblasted formula" is implied by the + * corresponding BV literal) + * + * 2. A declaration of a cnf formula equisatisfiable to the bitblasted + * formula + * + * (and a proof that each clause is implied by some bitblasted formula) + * + * 3. A proof of UNSAT from the clauses. + * + * This class is responsible for 1 & 2. The proof of UNSAT is delegated to a + * subclass. + */ +class BitVectorProof : public TheoryProof +{ + protected: + BitVectorProof(theory::bv::TheoryBV* bv, TheoryProofEngine* proofEngine); + virtual ~BitVectorProof(){}; + + // Set of BV variables in the input. (e.g. "a" in [ a = 000 ] ^ [ a == 001 ]) ExprSet d_declarations; - ExprSet d_usedBB; // terms and formulas that are actually relevant to the proof + // terms and formulas that are actually relevant to the proof + ExprSet d_usedBB; + + ExprSet d_seenBBTerms; // terms that need to be bit-blasted + std::vector d_bbTerms; // order of bit-blasting - ExprSet d_seenBBTerms; // terms that need to be bit-blasted - std::vector d_bbTerms; // order of bit-blasting - ExprToExpr d_bbAtoms; // atoms that need to be bit-blasted + /** atoms that need to be bit-blasted, + * BV-literals -> (BV-literals <=> bool formula) + * where a BV literal is a signed or unsigned comparison. + */ + ExprToExpr d_bbAtoms; // map from Expr representing normalized lemma to ClauseId in SAT solver ExprToClauseId d_bbConflictMap; - BVSatProof* d_resolutionProof; - CnfProof* d_cnfProof; - - bool d_isAssumptionConflict; theory::bv::TBitblaster* d_bitblaster; + + /** In an LFSC proof the manifestation of this expression bit-level + * representation will have a string name. This method returns that name. + */ std::string getBBTermName(Expr expr); - std::map d_constantLetMap; + /** A mapping from constant BV terms to identifiers that will refer to them in + * an LFSC proof, if constant-letification is enabled. + */ + std::map d_constantLetMap; + + /** Should we introduced identifiers to refer to BV constant terms? It may + * reduce the textual size of a proof! + */ bool d_useConstantLetification; - theory::TheoryId getTheoryId() override; - context::Context d_fakeContext; -public: - BitVectorProof(theory::bv::TheoryBV* bv, TheoryProofEngine* proofEngine); - void initSatProof(CVC4::BVMinisat::Solver* solver); - void initCnfProof(prop::CnfStream* cnfStream, context::Context* ctx); - void setBitblaster(theory::bv::TBitblaster* bb); + /** Temporary storage for the set of nodes in the bitblasted formula which + * correspond to CNF variables eventually used in the proof of unsat on the + * CNF formula + */ + std::set d_atomsInBitblastingProof; - BVSatProof* getSatProof(); - CnfProof* getCnfProof() {return d_cnfProof; } - void finalizeConflicts(std::vector& conflicts); + /** + * Prints out + * (a) a declaration of bit-level interpretations corresponding to bits in + * the input BV terms. + * (b) a proof that the each BV literal entails a boolean formula on + * bitof expressions. + */ + void printBitblasting(std::ostream& os, std::ostream& paren); - void startBVConflict(CVC4::BVMinisat::Solver::TCRef cr); - void startBVConflict(CVC4::BVMinisat::Solver::TLit lit); /** - * All the - * - * @param confl an inconsistent set of bv literals + * The proof that the bit-blasted SAT formula is correctly converted to CNF */ - void endBVConflict(const BVMinisat::Solver::TLitVec& confl); - void markAssumptionConflict() { d_isAssumptionConflict = true; } - bool isAssumptionConflict() { return d_isAssumptionConflict; } + std::unique_ptr d_cnfProof; + + public: + void printOwnedTerm(Expr term, + std::ostream& os, + const ProofLetMap& map) override; + + void printOwnedSort(Type type, std::ostream& os) override; + + /** + * Populate the d_atomsInBitblastingProof member. + * See its documentation + */ + virtual void calculateAtomsInBitblastingProof() = 0; + + /** + * Read the d_atomsInBitblastingProof member. + * See its documentation. + */ + const std::set* getAtomsInBitblastingProof(); void registerTermBB(Expr term); + + /** + * Informs the proof that the `atom` predicate was bitblasted into the + * `atom_bb` term. + * + * The `atom` term must be a comparison of bitvectors, and the `atom_bb` term + * a boolean formula on bitof expressions + */ void registerAtomBB(Expr atom, Expr atom_bb); void registerTerm(Expr term) override; - virtual void printTermBitblasting(Expr term, std::ostream& os) = 0; - virtual void printAtomBitblasting(Expr term, std::ostream& os, bool swap) = 0; - virtual void printAtomBitblastingToFalse(Expr term, std::ostream& os) = 0; + /** + * This must be done before registering any terms or atoms, since the CNF + * proof must reflect the result of bitblasting those + */ + virtual void initCnfProof(prop::CnfStream* cnfStream, context::Context* ctx); - virtual void printBitblasting(std::ostream& os, std::ostream& paren) = 0; - virtual void printResolutionProof(std::ostream& os, std::ostream& paren, ProofLetMap& letMap) = 0; - virtual const std::set* getAtomsInBitblastingProof() = 0; - virtual void calculateAtomsInBitblastingProof() = 0; -}; + CnfProof* getCnfProof() { return d_cnfProof.get(); } + + void setBitblaster(theory::bv::TBitblaster* bb); -class LFSCBitVectorProof: public BitVectorProof { + private: + ExprToString d_exprToVariableName; + + ExprToString d_assignedAliases; + std::map d_aliasToBindDeclaration; + std::string assignAlias(Expr expr); + bool hasAlias(Expr expr); + // Functions for printing various BV terms. Helpers for BV's `printOwnedTerm` void printConstant(Expr term, std::ostream& os); void printOperatorNary(Expr term, std::ostream& os, const ProofLetMap& map); void printOperatorUnary(Expr term, std::ostream& os, const ProofLetMap& map); @@ -128,29 +176,19 @@ class LFSCBitVectorProof: public BitVectorProof { void printOperatorParametric(Expr term, std::ostream& os, const ProofLetMap& map); void printBitOf(Expr term, std::ostream& os, const ProofLetMap& map); - ExprToString d_exprToVariableName; - ExprToString d_assignedAliases; - std::map d_aliasToBindDeclaration; - std::string assignAlias(Expr expr); - bool hasAlias(Expr expr); + /** + * Prints the LFSC construction of a bblast_term for `term` + */ + void printTermBitblasting(Expr term, std::ostream& os); - std::set d_atomsInBitblastingProof; + /** + * For a given BV-atom (a comparison), prints a proof that that comparison + * holds iff the bitblasted equivalent of it holds. + * Uses a side-condidition to do the bit-blasting. + */ + void printAtomBitblasting(Expr term, std::ostream& os, bool swap); + void printAtomBitblastingToFalse(Expr term, std::ostream& os); -public: - LFSCBitVectorProof(theory::bv::TheoryBV* bv, TheoryProofEngine* proofEngine) - :BitVectorProof(bv, proofEngine) - {} - void printOwnedTerm(Expr term, - std::ostream& os, - const ProofLetMap& map) override; - void printOwnedSort(Type type, std::ostream& os) override; - void printTermBitblasting(Expr term, std::ostream& os) override; - void printAtomBitblasting(Expr term, std::ostream& os, bool swap) override; - void printAtomBitblastingToFalse(Expr term, std::ostream& os) override; - void printTheoryLemmaProof(std::vector& lemma, - std::ostream& os, - std::ostream& paren, - const ProofLetMap& map) override; void printSortDeclarations(std::ostream& os, std::ostream& paren) override; void printTermDeclarations(std::ostream& os, std::ostream& paren) override; void printDeferredDeclarations(std::ostream& os, @@ -158,12 +196,7 @@ public: void printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap& globalLetMap) override; - void printBitblasting(std::ostream& os, std::ostream& paren) override; - void printResolutionProof(std::ostream& os, - std::ostream& paren, - ProofLetMap& letMap) override; - void calculateAtomsInBitblastingProof() override; - const std::set* getAtomsInBitblastingProof() override; + void printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, diff --git a/src/proof/proof_manager.cpp b/src/proof/proof_manager.cpp index e7b00068a..5b26432dd 100644 --- a/src/proof/proof_manager.cpp +++ b/src/proof/proof_manager.cpp @@ -21,11 +21,11 @@ #include "context/context.h" #include "options/bv_options.h" #include "options/proof_options.h" -#include "proof/bitvector_proof.h" #include "proof/clause_id.h" #include "proof/cnf_proof.h" #include "proof/lfsc_proof_printer.h" #include "proof/proof_utils.h" +#include "proof/resolution_bitvector_proof.h" #include "proof/sat_proof_implementation.h" #include "proof/theory_proof.h" #include "smt/smt_engine.h" @@ -116,10 +116,11 @@ UFProof* ProofManager::getUfProof() { return (UFProof*)pf; } -BitVectorProof* ProofManager::getBitVectorProof() { +proof::ResolutionBitVectorProof* ProofManager::getBitVectorProof() +{ Assert (options::proof()); TheoryProof* pf = getTheoryProofEngine()->getTheoryProof(theory::THEORY_BV); - return (BitVectorProof*)pf; + return static_cast(pf); } ArrayProof* ProofManager::getArrayProof() { diff --git a/src/proof/proof_manager.h b/src/proof/proof_manager.h index 0342288fe..82efbab0f 100644 --- a/src/proof/proof_manager.h +++ b/src/proof/proof_manager.h @@ -69,7 +69,10 @@ class TheoryProof; class UFProof; class ArithProof; class ArrayProof; -class BitVectorProof; + +namespace proof { +class ResolutionBitVectorProof; +} template class LFSCSatProof; typedef TSatProof CoreSatProof; @@ -77,7 +80,6 @@ typedef TSatProof CoreSatProof; class LFSCCnfProof; class LFSCTheoryProofEngine; class LFSCUFProof; -class LFSCBitVectorProof; class LFSCRewriterProof; namespace prop { @@ -189,7 +191,7 @@ public: static TheoryProofEngine* getTheoryProofEngine(); static TheoryProof* getTheoryProof( theory::TheoryId id ); static UFProof* getUfProof(); - static BitVectorProof* getBitVectorProof(); + static proof::ResolutionBitVectorProof* getBitVectorProof(); static ArrayProof* getArrayProof(); static ArithProof* getArithProof(); diff --git a/src/proof/resolution_bitvector_proof.cpp b/src/proof/resolution_bitvector_proof.cpp new file mode 100644 index 000000000..667d630f8 --- /dev/null +++ b/src/proof/resolution_bitvector_proof.cpp @@ -0,0 +1,522 @@ +/********************* */ +/*! \file resolution_bitvector_proof.cpp + ** \verbatim + ** Top contributors (to current version): + ** Liana Hadarean, Guy Katz, Paul Meng + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2018 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** [[ Add lengthier description here ]] + + ** \todo document this file + +**/ + +#include "proof/resolution_bitvector_proof.h" +#include "options/bv_options.h" +#include "options/proof_options.h" +#include "proof/array_proof.h" +#include "proof/bitvector_proof.h" +#include "proof/clause_id.h" +#include "proof/lfsc_proof_printer.h" +#include "proof/proof_output_channel.h" +#include "proof/proof_utils.h" +#include "proof/sat_proof_implementation.h" +#include "prop/bvminisat/bvminisat.h" +#include "theory/bv/bitblast/bitblaster.h" +#include "theory/bv/theory_bv.h" +#include "theory/bv/theory_bv_rewrite_rules.h" + +#include +#include + +using namespace CVC4::theory; +using namespace CVC4::theory::bv; + +namespace CVC4 { + +namespace proof { + +ResolutionBitVectorProof::ResolutionBitVectorProof( + theory::bv::TheoryBV* bv, TheoryProofEngine* proofEngine) + : BitVectorProof(bv, proofEngine), + d_resolutionProof(), + d_isAssumptionConflict(false) +{ +} + +void ResolutionBitVectorProof::initSatProof(CVC4::BVMinisat::Solver* solver) +{ + Assert(d_resolutionProof == NULL); + d_resolutionProof.reset(new BVSatProof(solver, &d_fakeContext, "bb", true)); +} + +theory::TheoryId ResolutionBitVectorProof::getTheoryId() +{ + return theory::THEORY_BV; +} + +void ResolutionBitVectorProof::initCnfProof(prop::CnfStream* cnfStream, + context::Context* cnf) +{ + Assert(d_resolutionProof != NULL); + BitVectorProof::initCnfProof(cnfStream, cnf); + + // true and false have to be setup in a special way + Node true_node = NodeManager::currentNM()->mkConst(true); + Node false_node = NodeManager::currentNM()->mkConst(false).notNode(); + + d_cnfProof->pushCurrentAssertion(true_node); + d_cnfProof->pushCurrentDefinition(true_node); + d_cnfProof->registerConvertedClause(d_resolutionProof->getTrueUnit()); + d_cnfProof->popCurrentAssertion(); + d_cnfProof->popCurrentDefinition(); + + d_cnfProof->pushCurrentAssertion(false_node); + d_cnfProof->pushCurrentDefinition(false_node); + d_cnfProof->registerConvertedClause(d_resolutionProof->getFalseUnit()); + d_cnfProof->popCurrentAssertion(); + d_cnfProof->popCurrentDefinition(); +} + +BVSatProof* ResolutionBitVectorProof::getSatProof() +{ + Assert(d_resolutionProof != NULL); + return d_resolutionProof.get(); +} + +void ResolutionBitVectorProof::startBVConflict( + CVC4::BVMinisat::Solver::TCRef cr) +{ + d_resolutionProof->startResChain(cr); +} + +void ResolutionBitVectorProof::startBVConflict( + CVC4::BVMinisat::Solver::TLit lit) +{ + d_resolutionProof->startResChain(lit); +} + +void ResolutionBitVectorProof::endBVConflict( + const CVC4::BVMinisat::Solver::TLitVec& confl) +{ + Debug("pf::bv") << "ResolutionBitVectorProof::endBVConflict called" + << std::endl; + + std::vector expr_confl; + for (int i = 0; i < confl.size(); ++i) + { + prop::SatLiteral lit = prop::BVMinisatSatSolver::toSatLiteral(confl[i]); + Expr atom = d_cnfProof->getAtom(lit.getSatVariable()).toExpr(); + Expr expr_lit = lit.isNegated() ? atom.notExpr() : atom; + expr_confl.push_back(expr_lit); + } + + Expr conflict = utils::mkSortedExpr(kind::OR, expr_confl); + Debug("pf::bv") << "Make conflict for " << conflict << std::endl; + + if (d_bbConflictMap.find(conflict) != d_bbConflictMap.end()) + { + Debug("pf::bv") << "Abort...already conflict for " << conflict << std::endl; + // This can only happen when we have eager explanations in the bv solver + // if we don't get to propagate p before ~p is already asserted + d_resolutionProof->cancelResChain(); + return; + } + + // we don't need to check for uniqueness in the sat solver then + ClauseId clause_id = d_resolutionProof->registerAssumptionConflict(confl); + d_bbConflictMap[conflict] = clause_id; + d_resolutionProof->endResChain(clause_id); + Debug("pf::bv") << "ResolutionBitVectorProof::endBVConflict id" << clause_id + << " => " << conflict << "\n"; + d_isAssumptionConflict = false; +} + +void ResolutionBitVectorProof::finalizeConflicts(std::vector& conflicts) +{ + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) + { + Debug("pf::bv") << "Construct full proof." << std::endl; + d_resolutionProof->constructProof(); + return; + } + + for (unsigned i = 0; i < conflicts.size(); ++i) + { + Expr confl = conflicts[i]; + Debug("pf::bv") << "Finalize conflict #" << i << ": " << confl << std::endl; + + // Special case: if the conflict has a (true) or a (not false) in it, it is + // trivial... + bool ignoreConflict = false; + if ((confl.isConst() && confl.getConst()) + || (confl.getKind() == kind::NOT && confl[0].isConst() + && !confl[0].getConst())) + { + ignoreConflict = true; + } + else if (confl.getKind() == kind::OR) + { + for (unsigned k = 0; k < confl.getNumChildren(); ++k) + { + if ((confl[k].isConst() && confl[k].getConst()) + || (confl[k].getKind() == kind::NOT && confl[k][0].isConst() + && !confl[k][0].getConst())) + { + ignoreConflict = true; + } + } + } + if (ignoreConflict) + { + Debug("pf::bv") << "Ignoring conflict due to (true) or (not false)" + << std::endl; + continue; + } + + if (d_bbConflictMap.find(confl) != d_bbConflictMap.end()) + { + ClauseId id = d_bbConflictMap[confl]; + d_resolutionProof->collectClauses(id); + } + else + { + // There is no exact match for our conflict, but maybe it is a subset of + // another conflict + ExprToClauseId::const_iterator it; + bool matchFound = false; + for (it = d_bbConflictMap.begin(); it != d_bbConflictMap.end(); ++it) + { + Expr possibleMatch = it->first; + if (possibleMatch.getKind() != kind::OR) + { + // This is a single-node conflict. If this node is in the conflict + // we're trying to prove, we have a match. + for (unsigned k = 0; k < confl.getNumChildren(); ++k) + { + if (confl[k] == possibleMatch) + { + matchFound = true; + d_resolutionProof->collectClauses(it->second); + break; + } + } + } + else + { + if (possibleMatch.getNumChildren() > confl.getNumChildren()) continue; + + unsigned k = 0; + bool matching = true; + for (unsigned j = 0; j < possibleMatch.getNumChildren(); ++j) + { + // j is the index in possibleMatch + // k is the index in confl + while (k < confl.getNumChildren() && confl[k] != possibleMatch[j]) + { + ++k; + } + if (k == confl.getNumChildren()) + { + // We couldn't find a match for possibleMatch[j], so not a match + matching = false; + break; + } + } + + if (matching) + { + Debug("pf::bv") + << "Collecting info from a sub-conflict" << std::endl; + d_resolutionProof->collectClauses(it->second); + matchFound = true; + break; + } + } + } + + if (!matchFound) + { + Debug("pf::bv") << "Do not collect clauses for " << confl << std::endl + << "Dumping existing conflicts:" << std::endl; + + i = 0; + for (it = d_bbConflictMap.begin(); it != d_bbConflictMap.end(); ++it) + { + ++i; + Debug("pf::bv") << "\tConflict #" << i << ": " << it->first + << std::endl; + } + + Unreachable(); + } + } + } +} + +void LFSCBitVectorProof::printTheoryLemmaProof(std::vector& lemma, + std::ostream& os, + std::ostream& paren, + const ProofLetMap& map) +{ + Debug("pf::bv") << "(pf::bv) LFSCBitVectorProof::printTheoryLemmaProof called" + << std::endl; + Expr conflict = utils::mkSortedExpr(kind::OR, lemma); + Debug("pf::bv") << "\tconflict = " << conflict << std::endl; + + if (d_bbConflictMap.find(conflict) != d_bbConflictMap.end()) + { + std::ostringstream lemma_paren; + for (unsigned i = 0; i < lemma.size(); ++i) + { + Expr lit = lemma[i]; + + if (lit.getKind() == kind::NOT) + { + os << "(intro_assump_t _ _ _ "; + } + else + { + os << "(intro_assump_f _ _ _ "; + } + lemma_paren << ")"; + // print corresponding literal in main sat solver + ProofManager* pm = ProofManager::currentPM(); + CnfProof* cnf = pm->getCnfProof(); + prop::SatLiteral main_lit = cnf->getLiteral(lit); + os << pm->getLitName(main_lit); + os << " "; + // print corresponding literal in bv sat solver + prop::SatVariable bb_var = d_cnfProof->getLiteral(lit).getSatVariable(); + os << pm->getAtomName(bb_var, "bb"); + os << "(\\ unit" << bb_var << "\n"; + lemma_paren << ")"; + } + Expr lem = utils::mkOr(lemma); + Assert(d_bbConflictMap.find(lem) != d_bbConflictMap.end()); + ClauseId lemma_id = d_bbConflictMap[lem]; + proof::LFSCProofPrinter::printAssumptionsResolution( + d_resolutionProof.get(), lemma_id, os, lemma_paren); + os << lemma_paren.str(); + } + else + { + Debug("pf::bv") << "Found a non-recorded conflict. Looking for a matching " + "sub-conflict..." + << std::endl; + + bool matching; + + ExprToClauseId::const_iterator it; + unsigned i = 0; + for (it = d_bbConflictMap.begin(); it != d_bbConflictMap.end(); ++it) + { + // Our conflict is sorted, and the records are also sorted. + ++i; + Expr possibleMatch = it->first; + + if (possibleMatch.getKind() != kind::OR) + { + // This is a single-node conflict. If this node is in the conflict we're + // trying to prove, we have a match. + matching = false; + + for (unsigned k = 0; k < conflict.getNumChildren(); ++k) + { + if (conflict[k] == possibleMatch) + { + matching = true; + break; + } + } + } + else + { + if (possibleMatch.getNumChildren() > conflict.getNumChildren()) + continue; + + unsigned k = 0; + + matching = true; + for (unsigned j = 0; j < possibleMatch.getNumChildren(); ++j) + { + // j is the index in possibleMatch + // k is the index in conflict + while (k < conflict.getNumChildren() + && conflict[k] != possibleMatch[j]) + { + ++k; + } + if (k == conflict.getNumChildren()) + { + // We couldn't find a match for possibleMatch[j], so not a match + matching = false; + break; + } + } + } + + if (matching) + { + Debug("pf::bv") << "Found a match with conflict #" << i << ": " + << std::endl + << possibleMatch << std::endl; + // The rest is just a copy of the usual handling, if a precise match is + // found. We only use the literals that appear in the matching conflict, + // though, and not in the original lemma - as these may not have even + // been bit blasted! + std::ostringstream lemma_paren; + + if (possibleMatch.getKind() == kind::OR) + { + for (unsigned i = 0; i < possibleMatch.getNumChildren(); ++i) + { + Expr lit = possibleMatch[i]; + + if (lit.getKind() == kind::NOT) + { + os << "(intro_assump_t _ _ _ "; + } + else + { + os << "(intro_assump_f _ _ _ "; + } + lemma_paren << ")"; + // print corresponding literal in main sat solver + ProofManager* pm = ProofManager::currentPM(); + CnfProof* cnf = pm->getCnfProof(); + prop::SatLiteral main_lit = cnf->getLiteral(lit); + os << pm->getLitName(main_lit); + os << " "; + // print corresponding literal in bv sat solver + prop::SatVariable bb_var = + d_cnfProof->getLiteral(lit).getSatVariable(); + os << pm->getAtomName(bb_var, "bb"); + os << "(\\ unit" << bb_var << "\n"; + lemma_paren << ")"; + } + } + else + { + // The conflict only consists of one node, either positive or + // negative. + Expr lit = possibleMatch; + if (lit.getKind() == kind::NOT) + { + os << "(intro_assump_t _ _ _ "; + } + else + { + os << "(intro_assump_f _ _ _ "; + } + lemma_paren << ")"; + // print corresponding literal in main sat solver + ProofManager* pm = ProofManager::currentPM(); + CnfProof* cnf = pm->getCnfProof(); + prop::SatLiteral main_lit = cnf->getLiteral(lit); + os << pm->getLitName(main_lit); + os << " "; + // print corresponding literal in bv sat solver + prop::SatVariable bb_var = + d_cnfProof->getLiteral(lit).getSatVariable(); + os << pm->getAtomName(bb_var, "bb"); + os << "(\\ unit" << bb_var << "\n"; + lemma_paren << ")"; + } + + ClauseId lemma_id = it->second; + proof::LFSCProofPrinter::printAssumptionsResolution( + d_resolutionProof.get(), lemma_id, os, lemma_paren); + os << lemma_paren.str(); + + return; + } + } + + // We failed to find a matching sub conflict. The last hope is that the + // conflict has a FALSE assertion in it; this can happen in some corner + // cases, where the FALSE is the result of a rewrite. + + for (unsigned i = 0; i < lemma.size(); ++i) + { + if (lemma[i].getKind() == kind::NOT && lemma[i][0] == utils::mkFalse()) + { + Debug("pf::bv") << "Lemma has a (not false) literal" << std::endl; + os << "(clausify_false "; + os << ProofManager::getLitName(lemma[i]); + os << ")"; + return; + } + } + + Debug("pf::bv") << "Failed to find a matching sub-conflict..." << std::endl + << "Dumping existing conflicts:" << std::endl; + + i = 0; + for (it = d_bbConflictMap.begin(); it != d_bbConflictMap.end(); ++it) + { + ++i; + Debug("pf::bv") << "\tConflict #" << i << ": " << it->first << std::endl; + } + + Unreachable(); + } +} + +void LFSCBitVectorProof::calculateAtomsInBitblastingProof() +{ + // Collect the input clauses used + IdToSatClause used_lemmas; + IdToSatClause used_inputs; + d_resolutionProof->collectClausesUsed(used_inputs, used_lemmas); + d_cnfProof->collectAtomsForClauses(used_inputs, d_atomsInBitblastingProof); + Assert(used_lemmas.empty()); +} + +void LFSCBitVectorProof::printResolutionProof(std::ostream& os, + std::ostream& paren, + ProofLetMap& letMap) +{ + // print mapping between theory atoms and internal SAT variables + os << std::endl << ";; BB atom mapping\n" << std::endl; + + std::set::iterator atomIt; + Debug("pf::bv") << std::endl + << "BV Dumping atoms from inputs: " << std::endl + << std::endl; + for (atomIt = d_atomsInBitblastingProof.begin(); + atomIt != d_atomsInBitblastingProof.end(); + ++atomIt) + { + Debug("pf::bv") << "\tAtom: " << *atomIt << std::endl; + } + Debug("pf::bv") << std::endl; + + // first print bit-blasting + printBitblasting(os, paren); + + // print CNF conversion proof for bit-blasted facts + IdToSatClause used_lemmas; + IdToSatClause used_inputs; + d_resolutionProof->collectClausesUsed(used_inputs, used_lemmas); + + d_cnfProof->printAtomMapping(d_atomsInBitblastingProof, os, paren, letMap); + os << std::endl << ";; Bit-blasting definitional clauses \n" << std::endl; + for (IdToSatClause::iterator it = used_inputs.begin(); + it != used_inputs.end(); + ++it) + { + d_cnfProof->printCnfProofForClause(it->first, it->second, os, paren); + } + + os << std::endl << " ;; Bit-blasting learned clauses \n" << std::endl; + proof::LFSCProofPrinter::printResolutions(d_resolutionProof.get(), os, paren); +} + +} /* namespace proof */ + +} /* namespace CVC4 */ diff --git a/src/proof/resolution_bitvector_proof.h b/src/proof/resolution_bitvector_proof.h new file mode 100644 index 000000000..a54d72d3f --- /dev/null +++ b/src/proof/resolution_bitvector_proof.h @@ -0,0 +1,132 @@ +/********************* */ +/*! \file resolution_bitvector_proof.h + ** \verbatim + ** Top contributors (to current version): + ** Liana Hadarean, Mathias Preiner, Guy Katz + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2018 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Bitvector proof + ** + ** Bitvector proof + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__PROOF__RESOLUTION_BITVECTOR_PROOF_H +#define __CVC4__PROOF__RESOLUTION_BITVECTOR_PROOF_H + +#include + +#include "context/context.h" +#include "expr/expr.h" +#include "proof/bitvector_proof.h" +#include "proof/theory_proof.h" +#include "prop/bvminisat/core/Solver.h" + +namespace CVC4 { + +namespace theory { +namespace bv { +class TheoryBV; +template +class TBitblaster; +} // namespace bv +} // namespace theory + +// TODO(aozdemir) break the sat_solver - resolution_bitvectorproof - cnf_stream +// header cycle and remove this. +namespace prop { +class CnfStream; +} + +} /* namespace CVC4 */ + + +namespace CVC4 { + +template +class TSatProof; +typedef TSatProof BVSatProof; + +namespace proof { + +/** + * Represents a bitvector proof which is backed by + * (a) bitblasting and + * (b) a resolution unsat proof. + * + * Contains tools for constructing BV conflicts + */ +class ResolutionBitVectorProof : public BitVectorProof +{ + public: + ResolutionBitVectorProof(theory::bv::TheoryBV* bv, + TheoryProofEngine* proofEngine); + + /** + * Create an (internal) SAT proof object + * Must be invoked before manipulating BV conflicts, + * or initializing a BNF proof + */ + void initSatProof(CVC4::BVMinisat::Solver* solver); + + BVSatProof* getSatProof(); + + /** + * Kind of a mess. + * In eager mode this must be invoked before printing a proof of the empty + * clause. In lazy mode the behavior is ??? + * TODO(aozdemir) clean this up. + */ + void finalizeConflicts(std::vector& conflicts); + + void startBVConflict(CVC4::BVMinisat::Solver::TCRef cr); + void startBVConflict(CVC4::BVMinisat::Solver::TLit lit); + void endBVConflict(const BVMinisat::Solver::TLitVec& confl); + + void markAssumptionConflict() { d_isAssumptionConflict = true; } + bool isAssumptionConflict() const { return d_isAssumptionConflict; } + + virtual void printResolutionProof(std::ostream& os, + std::ostream& paren, + ProofLetMap& letMap) = 0; + + void initCnfProof(prop::CnfStream* cnfStream, context::Context* cnf) override; + + protected: + // The CNF formula that results from bit-blasting will need a proof. + // This is that proof. + std::unique_ptr d_resolutionProof; + + bool d_isAssumptionConflict; + + theory::TheoryId getTheoryId() override; + context::Context d_fakeContext; +}; + +class LFSCBitVectorProof : public ResolutionBitVectorProof +{ + public: + LFSCBitVectorProof(theory::bv::TheoryBV* bv, TheoryProofEngine* proofEngine) + : ResolutionBitVectorProof(bv, proofEngine) + { + } + void printTheoryLemmaProof(std::vector& lemma, + std::ostream& os, + std::ostream& paren, + const ProofLetMap& map) override; + void printResolutionProof(std::ostream& os, + std::ostream& paren, + ProofLetMap& letMap) override; + void calculateAtomsInBitblastingProof() override; +}; + +} // namespace proof + +} // namespace CVC4 + +#endif /* __CVC4__PROOF__RESOLUTIONBITVECTORPROOF_H */ diff --git a/src/proof/theory_proof.cpp b/src/proof/theory_proof.cpp index cfad0a068..ee06fbfa0 100644 --- a/src/proof/theory_proof.cpp +++ b/src/proof/theory_proof.cpp @@ -22,12 +22,12 @@ #include "options/proof_options.h" #include "proof/arith_proof.h" #include "proof/array_proof.h" -#include "proof/bitvector_proof.h" #include "proof/clause_id.h" #include "proof/cnf_proof.h" #include "proof/proof_manager.h" #include "proof/proof_output_channel.h" #include "proof/proof_utils.h" +#include "proof/resolution_bitvector_proof.h" #include "proof/sat_proof.h" #include "proof/simplify_boolean_node.h" #include "proof/uf_proof.h" @@ -46,6 +46,9 @@ namespace CVC4 { +using proof::LFSCBitVectorProof; +using proof::ResolutionBitVectorProof; + unsigned CVC4::ProofLetCount::counter = 0; static unsigned LET_COUNT = 1; @@ -77,7 +80,8 @@ void TheoryProofEngine::registerTheory(theory::Theory* th) { } if (id == theory::THEORY_BV) { - BitVectorProof * bvp = new LFSCBitVectorProof((theory::bv::TheoryBV*)th, this); + auto bv_theory = static_cast(th); + ResolutionBitVectorProof* bvp = new LFSCBitVectorProof(bv_theory, this); d_theoryProofTable[id] = bvp; return; } @@ -102,9 +106,9 @@ void TheoryProofEngine::finishRegisterTheory(theory::Theory* th) { theory::TheoryId id = th->getId(); if (id == theory::THEORY_BV) { Assert(d_theoryProofTable.find(id) != d_theoryProofTable.end()); - - BitVectorProof *bvp = (BitVectorProof *)d_theoryProofTable[id]; - ((theory::bv::TheoryBV*)th)->setProofLog( bvp ); + ResolutionBitVectorProof* bvp = + (ResolutionBitVectorProof*)d_theoryProofTable[id]; + ((theory::bv::TheoryBV*)th)->setResolutionProofLog(bvp); return; } } @@ -529,7 +533,7 @@ void LFSCTheoryProofEngine::finalizeBvConflicts(const IdToSatClause& lemmas, std } } - BitVectorProof* bv = ProofManager::getBitVectorProof(); + ResolutionBitVectorProof* bv = ProofManager::getBitVectorProof(); bv->finalizeConflicts(bv_lemmas); // bv->printResolutionProof(os, paren, letMap); } diff --git a/src/prop/bv_sat_solver_notify.h b/src/prop/bv_sat_solver_notify.h new file mode 100644 index 000000000..686848829 --- /dev/null +++ b/src/prop/bv_sat_solver_notify.h @@ -0,0 +1,49 @@ +/********************* */ +/*! \file sat_solver_notify.h + ** \verbatim + ** Top contributors (to current version): + ** Liana Hadarean, Dejan Jovanovic, Morgan Deters + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2018 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief The interface for things that want to recieve notification from the + ** SAT solver + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__PROP__BVSATSOLVERNOTIFY_H +#define __CVC4__PROP__BVSATSOLVERNOTIFY_H + +#include "prop/sat_solver_types.h" + +namespace CVC4 { +namespace prop { + +class BVSatSolverNotify { +public: + + virtual ~BVSatSolverNotify() {}; + + /** + * If the notify returns false, the solver will break out of whatever it's currently doing + * with an "unknown" answer. + */ + virtual bool notify(SatLiteral lit) = 0; + + /** + * Notify about a learnt clause. + */ + virtual void notify(SatClause& clause) = 0; + virtual void spendResource(unsigned amount) = 0; + virtual void safePoint(unsigned amount) = 0; + +};/* class BVSatSolverInterface::Notify */ + +} +} + +#endif diff --git a/src/prop/bvminisat/bvminisat.cpp b/src/prop/bvminisat/bvminisat.cpp index 1eb4bce96..55710092b 100644 --- a/src/prop/bvminisat/bvminisat.cpp +++ b/src/prop/bvminisat/bvminisat.cpp @@ -51,7 +51,7 @@ void BVMinisatSatSolver::MinisatNotify::notify( d_notify->notify(satClause); } -void BVMinisatSatSolver::setNotify(Notify* notify) { +void BVMinisatSatSolver::setNotify(BVSatSolverNotify* notify) { d_minisatNotify.reset(new MinisatNotify(notify)); d_minisat->setNotify(d_minisatNotify.get()); } @@ -104,7 +104,8 @@ void BVMinisatSatSolver::popAssumption() { d_minisat->popAssumption(); } -void BVMinisatSatSolver::setProofLog( BitVectorProof * bvp ) { +void BVMinisatSatSolver::setProofLog(proof::ResolutionBitVectorProof* bvp) +{ d_minisat->setProofLog( bvp ); } diff --git a/src/prop/bvminisat/bvminisat.h b/src/prop/bvminisat/bvminisat.h index 728d26bd4..16489b172 100644 --- a/src/prop/bvminisat/bvminisat.h +++ b/src/prop/bvminisat/bvminisat.h @@ -22,8 +22,10 @@ #include "context/cdo.h" #include "proof/clause_id.h" +#include "proof/resolution_bitvector_proof.h" #include "prop/bvminisat/simp/SimpSolver.h" #include "prop/sat_solver.h" +#include "prop/bv_sat_solver_notify.h" #include "util/statistics_registry.h" namespace CVC4 { @@ -35,10 +37,10 @@ class BVMinisatSatSolver : public BVSatSolverInterface, private: class MinisatNotify : public BVMinisat::Notify { - BVSatSolverInterface::Notify* d_notify; + BVSatSolverNotify* d_notify; public: - MinisatNotify(BVSatSolverInterface::Notify* notify) : d_notify(notify) {} + MinisatNotify(BVSatSolverNotify* notify) : d_notify(notify) {} bool notify(BVMinisat::Lit lit) override { return d_notify->notify(toSatLiteral(lit)); @@ -66,7 +68,7 @@ public: BVMinisatSatSolver(StatisticsRegistry* registry, context::Context* mainSatContext, const std::string& name = ""); virtual ~BVMinisatSatSolver(); - void setNotify(Notify* notify) override; + void setNotify(BVSatSolverNotify* notify) override; ClauseId addClause(SatClause& clause, bool removable) override; @@ -117,7 +119,7 @@ public: void popAssumption() override; - void setProofLog(BitVectorProof* bvp) override; + void setProofLog(proof::ResolutionBitVectorProof* bvp) override; private: /* Disable the default constructor. */ diff --git a/src/prop/bvminisat/core/Solver.cc b/src/prop/bvminisat/core/Solver.cc index a4b0248e0..a877f20c3 100644 --- a/src/prop/bvminisat/core/Solver.cc +++ b/src/prop/bvminisat/core/Solver.cc @@ -29,9 +29,9 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA #include "base/output.h" #include "options/bv_options.h" #include "options/smt_options.h" -#include "proof/bitvector_proof.h" #include "proof/clause_id.h" #include "proof/proof_manager.h" +#include "proof/resolution_bitvector_proof.h" #include "proof/sat_proof.h" #include "proof/sat_proof_implementation.h" #include "prop/bvminisat/mtl/Sort.h" @@ -1318,7 +1318,8 @@ void Solver::explain(Lit p, std::vector& explanation) { } } -void Solver::setProofLog( BitVectorProof * bvp ) { +void Solver::setProofLog(proof::ResolutionBitVectorProof* bvp) +{ d_bvp = bvp; d_bvp->initSatProof(this); d_bvp->getSatProof()->registerTrueLit(mkLit(varTrue, false)); diff --git a/src/prop/bvminisat/core/Solver.h b/src/prop/bvminisat/core/Solver.h index da4fb4c16..eef1c4e4c 100644 --- a/src/prop/bvminisat/core/Solver.h +++ b/src/prop/bvminisat/core/Solver.h @@ -39,7 +39,10 @@ namespace BVMinisat { class Solver; } -class BitVectorProof; +// TODO (aozdemir) replace this forward declaration with an include +namespace proof { +class ResolutionBitVectorProof; +} namespace BVMinisat { @@ -212,10 +215,10 @@ public: bool only_bcp; // solving mode in which only boolean constraint propagation is done void setOnlyBCP (bool val) { only_bcp = val;} void explain(Lit l, std::vector& explanation); - - void setProofLog( CVC4::BitVectorProof * bvp ); -protected: + void setProofLog(CVC4::proof::ResolutionBitVectorProof* bvp); + + protected: // has a clause been added bool clause_added; @@ -292,7 +295,7 @@ protected: bool asynch_interrupt; //proof log - CVC4::BitVectorProof * d_bvp; + CVC4::proof::ResolutionBitVectorProof* d_bvp; // Main internal methods: // diff --git a/src/prop/sat_solver.h b/src/prop/sat_solver.h index 5222af200..49064c20f 100644 --- a/src/prop/sat_solver.h +++ b/src/prop/sat_solver.h @@ -26,13 +26,13 @@ #include "context/cdlist.h" #include "context/context.h" #include "expr/node.h" +#include "proof/resolution_bitvector_proof.h" #include "proof/clause_id.h" #include "prop/sat_solver_types.h" +#include "prop/bv_sat_solver_notify.h" #include "util/statistics_registry.h" namespace CVC4 { - -class BitVectorProof; namespace prop { @@ -96,9 +96,9 @@ public: /** Check if the solver is in an inconsistent state */ virtual bool ok() const = 0; - - virtual void setProofLog( BitVectorProof * bvp ) {} - + + virtual void setProofLog(proof::ResolutionBitVectorProof* bvp) {} + };/* class SatSolver */ @@ -107,27 +107,8 @@ public: virtual ~BVSatSolverInterface() {} /** Interface for notifications */ - class Notify { - public: - - virtual ~Notify() {}; - - /** - * If the notify returns false, the solver will break out of whatever it's currently doing - * with an "unknown" answer. - */ - virtual bool notify(SatLiteral lit) = 0; - - /** - * Notify about a learnt clause. - */ - virtual void notify(SatClause& clause) = 0; - virtual void spendResource(unsigned amount) = 0; - virtual void safePoint(unsigned amount) = 0; - - };/* class BVSatSolverInterface::Notify */ - virtual void setNotify(Notify* notify) = 0; + virtual void setNotify(BVSatSolverNotify* notify) = 0; virtual void markUnremovable(SatLiteral lit) = 0; diff --git a/src/prop/sat_solver_types.h b/src/prop/sat_solver_types.h index f041f6898..ed1c5397d 100644 --- a/src/prop/sat_solver_types.h +++ b/src/prop/sat_solver_types.h @@ -24,8 +24,9 @@ #include "cvc4_private.h" -#include #include +#include +#include namespace CVC4 { namespace prop { diff --git a/src/theory/bv/bitblast/aig_bitblaster.h b/src/theory/bv/bitblast/aig_bitblaster.h index 6d21b69e6..62e70d73d 100644 --- a/src/theory/bv/bitblast/aig_bitblaster.h +++ b/src/theory/bv/bitblast/aig_bitblaster.h @@ -20,6 +20,7 @@ #define __CVC4__THEORY__BV__BITBLAST__AIG_BITBLASTER_H #include "theory/bv/bitblast/bitblaster.h" +#include "prop/sat_solver.h" class Abc_Obj_t_; typedef Abc_Obj_t_ Abc_Obj_t; diff --git a/src/theory/bv/bitblast/bitblaster.h b/src/theory/bv/bitblast/bitblaster.h index 9e2dac2f3..73b4d19c7 100644 --- a/src/theory/bv/bitblast/bitblaster.h +++ b/src/theory/bv/bitblast/bitblaster.h @@ -24,7 +24,8 @@ #include #include "expr/node.h" -#include "prop/sat_solver.h" +#include "prop/bv_sat_solver_notify.h" +#include "prop/sat_solver_types.h" #include "theory/bv/bitblast/bitblast_strategies_template.h" #include "theory/theory_registrar.h" #include "theory/valuation.h" @@ -59,8 +60,6 @@ class TBitblaster TermDefMap d_termCache; ModelCache d_modelCache; - BitVectorProof* d_bvp; - void initAtomBBStrategies(); void initTermBBStrategies(); @@ -94,7 +93,7 @@ class TBitblaster void invalidateModelCache(); }; -class MinisatEmptyNotify : public prop::BVSatSolverInterface::Notify +class MinisatEmptyNotify : public prop::BVSatSolverNotify { public: MinisatEmptyNotify() {} @@ -172,7 +171,7 @@ void TBitblaster::initTermBBStrategies() } template -TBitblaster::TBitblaster() : d_termCache(), d_modelCache(), d_bvp(NULL) +TBitblaster::TBitblaster() : d_termCache(), d_modelCache() { initAtomBBStrategies(); initTermBBStrategies(); diff --git a/src/theory/bv/bitblast/eager_bitblaster.cpp b/src/theory/bv/bitblast/eager_bitblaster.cpp index 01437cb64..33d5a1c80 100644 --- a/src/theory/bv/bitblast/eager_bitblaster.cpp +++ b/src/theory/bv/bitblast/eager_bitblaster.cpp @@ -19,7 +19,6 @@ #include "theory/bv/bitblast/eager_bitblaster.h" #include "options/bv_options.h" -#include "proof/bitvector_proof.h" #include "prop/cnf_stream.h" #include "prop/sat_solver_factory.h" #include "smt/smt_statistics_registry.h" @@ -37,6 +36,7 @@ EagerBitblaster::EagerBitblaster(TheoryBV* theory_bv, context::Context* c) d_satSolver(), d_bitblastingRegistrar(new BitblastingRegistrar(this)), d_cnfStream(), + d_bvp(nullptr), d_bv(theory_bv), d_bbAtoms(), d_variables(), @@ -268,10 +268,11 @@ bool EagerBitblaster::collectModelInfo(TheoryModel* m, bool fullModel) return true; } -void EagerBitblaster::setProofLog(BitVectorProof* bvp) { - d_bvp = bvp; - d_satSolver->setProofLog(bvp); - bvp->initCnfProof(d_cnfStream.get(), d_nullContext.get()); +void EagerBitblaster::setResolutionProofLog( + proof::ResolutionBitVectorProof* bvp) +{ + THEORY_PROOF(d_bvp = bvp; d_satSolver->setProofLog(bvp); + bvp->initCnfProof(d_cnfStream.get(), d_nullContext.get());) } bool EagerBitblaster::isSharedTerm(TNode node) { diff --git a/src/theory/bv/bitblast/eager_bitblaster.h b/src/theory/bv/bitblast/eager_bitblaster.h index 3e6190d76..3299ffc54 100644 --- a/src/theory/bv/bitblast/eager_bitblaster.h +++ b/src/theory/bv/bitblast/eager_bitblaster.h @@ -23,6 +23,8 @@ #include "theory/bv/bitblast/bitblaster.h" +#include "proof/bitvector_proof.h" +#include "proof/resolution_bitvector_proof.h" #include "prop/cnf_stream.h" #include "prop/sat_solver.h" @@ -53,7 +55,7 @@ class EagerBitblaster : public TBitblaster bool solve(); bool solve(const std::vector& assumptions); bool collectModelInfo(TheoryModel* m, bool fullModel); - void setProofLog(BitVectorProof* bvp); + void setResolutionProofLog(proof::ResolutionBitVectorProof* bvp); private: context::Context* d_context; @@ -65,6 +67,8 @@ class EagerBitblaster : public TBitblaster std::unique_ptr d_bitblastingRegistrar; std::unique_ptr d_cnfStream; + BitVectorProof* d_bvp; + TheoryBV* d_bv; TNodeSet d_bbAtoms; TNodeSet d_variables; diff --git a/src/theory/bv/bitblast/lazy_bitblaster.cpp b/src/theory/bv/bitblast/lazy_bitblaster.cpp index a50916413..529f0373b 100644 --- a/src/theory/bv/bitblast/lazy_bitblaster.cpp +++ b/src/theory/bv/bitblast/lazy_bitblaster.cpp @@ -19,17 +19,16 @@ #include "theory/bv/bitblast/lazy_bitblaster.h" #include "options/bv_options.h" +#include "proof/proof_manager.h" #include "prop/cnf_stream.h" #include "prop/sat_solver.h" #include "prop/sat_solver_factory.h" #include "smt/smt_statistics_registry.h" #include "theory/bv/abstraction.h" #include "theory/bv/theory_bv.h" +#include "theory/bv/theory_bv_utils.h" #include "theory/rewriter.h" #include "theory/theory_model.h" -#include "proof/bitvector_proof.h" -#include "proof/proof_manager.h" -#include "theory/bv/theory_bv_utils.h" namespace CVC4 { namespace theory { @@ -65,6 +64,7 @@ TLazyBitblaster::TLazyBitblaster(context::Context* c, bool emptyNotify) : TBitblaster(), d_bv(bv), + d_bvp(nullptr), d_ctx(c), d_nullRegistrar(new prop::NullRegistrar()), d_nullContext(new context::Context()), @@ -90,8 +90,8 @@ TLazyBitblaster::TLazyBitblaster(context::Context* c, d_satSolverNotify.reset( d_emptyNotify - ? (prop::BVSatSolverInterface::Notify*)new MinisatEmptyNotify() - : (prop::BVSatSolverInterface::Notify*)new MinisatNotify( + ? (prop::BVSatSolverNotify*)new MinisatEmptyNotify() + : (prop::BVSatSolverNotify*)new MinisatNotify( d_cnfStream.get(), bv, this)); d_satSolver->setNotify(d_satSolverNotify.get()); @@ -566,7 +566,8 @@ bool TLazyBitblaster::collectModelInfo(TheoryModel* m, bool fullModel) return true; } -void TLazyBitblaster::setProofLog( BitVectorProof * bvp ){ +void TLazyBitblaster::setProofLog(proof::ResolutionBitVectorProof* bvp) +{ d_bvp = bvp; d_satSolver->setProofLog( bvp ); bvp->initCnfProof(d_cnfStream.get(), d_nullContext.get()); @@ -590,8 +591,8 @@ void TLazyBitblaster::clearSolver() { d_satSolver.get(), d_nullRegistrar.get(), d_nullContext.get())); d_satSolverNotify.reset( d_emptyNotify - ? (prop::BVSatSolverInterface::Notify*)new MinisatEmptyNotify() - : (prop::BVSatSolverInterface::Notify*)new MinisatNotify( + ? (prop::BVSatSolverNotify*)new MinisatEmptyNotify() + : (prop::BVSatSolverNotify*)new MinisatNotify( d_cnfStream.get(), d_bv, this)); d_satSolver->setNotify(d_satSolverNotify.get()); } diff --git a/src/theory/bv/bitblast/lazy_bitblaster.h b/src/theory/bv/bitblast/lazy_bitblaster.h index 5e16b743a..1195d3590 100644 --- a/src/theory/bv/bitblast/lazy_bitblaster.h +++ b/src/theory/bv/bitblast/lazy_bitblaster.h @@ -19,13 +19,14 @@ #ifndef __CVC4__THEORY__BV__BITBLAST__LAZY_BITBLASTER_H #define __CVC4__THEORY__BV__BITBLAST__LAZY_BITBLASTER_H +#include "proof/resolution_bitvector_proof.h" #include "theory/bv/bitblast/bitblaster.h" #include "context/cdhashmap.h" #include "context/cdlist.h" #include "prop/cnf_stream.h" #include "prop/registrar.h" -#include "prop/sat_solver.h" +#include "prop/bv_sat_solver_notify.h" #include "theory/bv/abstraction.h" namespace CVC4 { @@ -76,7 +77,7 @@ class TLazyBitblaster : public TBitblaster * constants to equivalence classes that don't already have them */ bool collectModelInfo(TheoryModel* m, bool fullModel); - void setProofLog(BitVectorProof* bvp); + void setProofLog(proof::ResolutionBitVectorProof* bvp); typedef TNodeSet::const_iterator vars_iterator; vars_iterator beginVars() { return d_variables.begin(); } @@ -106,7 +107,7 @@ class TLazyBitblaster : public TBitblaster prop::SatLiteralHashFunction> ExplanationMap; /** This class gets callbacks from minisat on propagations */ - class MinisatNotify : public prop::BVSatSolverInterface::Notify + class MinisatNotify : public prop::BVSatSolverNotify { prop::CnfStream* d_cnf; TheoryBV* d_bv; @@ -125,13 +126,14 @@ class TLazyBitblaster : public TBitblaster }; TheoryBV* d_bv; + proof::ResolutionBitVectorProof* d_bvp; context::Context* d_ctx; std::unique_ptr d_nullRegistrar; std::unique_ptr d_nullContext; // sat solver used for bitblasting and associated CnfStream std::unique_ptr d_satSolver; - std::unique_ptr d_satSolverNotify; + std::unique_ptr d_satSolverNotify; std::unique_ptr d_cnfStream; AssertionList* diff --git a/src/theory/bv/bv_eager_solver.cpp b/src/theory/bv/bv_eager_solver.cpp index 27a48875d..119195c4a 100644 --- a/src/theory/bv/bv_eager_solver.cpp +++ b/src/theory/bv/bv_eager_solver.cpp @@ -17,7 +17,6 @@ #include "theory/bv/bv_eager_solver.h" #include "options/bv_options.h" -#include "proof/bitvector_proof.h" #include "theory/bv/bitblast/aig_bitblaster.h" #include "theory/bv/bitblast/eager_bitblaster.h" @@ -57,7 +56,7 @@ void EagerBitblastSolver::initialize() { } else { d_bitblaster.reset(new EagerBitblaster(d_bv, d_context)); THEORY_PROOF(if (d_bvp) { - d_bitblaster->setProofLog(d_bvp); + d_bitblaster->setResolutionProofLog(d_bvp); d_bvp->setBitblaster(d_bitblaster.get()); }); } @@ -128,7 +127,11 @@ bool EagerBitblastSolver::collectModelInfo(TheoryModel* m, bool fullModel) return d_bitblaster->collectModelInfo(m, fullModel); } -void EagerBitblastSolver::setProofLog(BitVectorProof* bvp) { d_bvp = bvp; } +void EagerBitblastSolver::setResolutionProofLog( + proof::ResolutionBitVectorProof* bvp) +{ + d_bvp = bvp; +} } // namespace bv } // namespace theory diff --git a/src/theory/bv/bv_eager_solver.h b/src/theory/bv/bv_eager_solver.h index b17cd6ebc..7f688b3ae 100644 --- a/src/theory/bv/bv_eager_solver.h +++ b/src/theory/bv/bv_eager_solver.h @@ -23,6 +23,7 @@ #include #include "expr/node.h" +#include "proof/resolution_bitvector_proof.h" #include "theory/bv/theory_bv.h" #include "theory/theory_model.h" @@ -47,7 +48,7 @@ class EagerBitblastSolver { bool isInitialized(); void initialize(); bool collectModelInfo(theory::TheoryModel* m, bool fullModel); - void setProofLog(BitVectorProof* bvp); + void setResolutionProofLog(proof::ResolutionBitVectorProof* bvp); private: context::CDHashSet d_assertionSet; @@ -60,7 +61,7 @@ class EagerBitblastSolver { bool d_useAig; TheoryBV* d_bv; - BitVectorProof* d_bvp; + proof::ResolutionBitVectorProof* d_bvp; }; // class EagerBitblastSolver } // namespace bv diff --git a/src/theory/bv/bv_subtheory.h b/src/theory/bv/bv_subtheory.h index 3166401aa..31c542e0b 100644 --- a/src/theory/bv/bv_subtheory.h +++ b/src/theory/bv/bv_subtheory.h @@ -24,6 +24,11 @@ #include "theory/theory.h" namespace CVC4 { + +namespace proof { +class ResolutionBitVectorProof; +} + namespace theory { class TheoryModel; @@ -88,7 +93,7 @@ class SubtheorySolver { return res; } virtual void assertFact(TNode fact) { d_assertionQueue.push_back(fact); } - virtual void setProofLog(BitVectorProof* bvp) {} + virtual void setProofLog(proof::ResolutionBitVectorProof* bvp) {} AssertionQueue::const_iterator assertionsBegin() { return d_assertionQueue.begin(); } @@ -103,7 +108,7 @@ class SubtheorySolver { /** The bit-vector theory */ TheoryBV* d_bv; /** proof log */ - BitVectorProof* d_bvp; + proof::ResolutionBitVectorProof* d_bvp; AssertionQueue d_assertionQueue; context::CDO d_assertionIndex; }; /* class SubtheorySolver */ diff --git a/src/theory/bv/bv_subtheory_bitblast.cpp b/src/theory/bv/bv_subtheory_bitblast.cpp index ea2f8e4bf..ff9dd52c2 100644 --- a/src/theory/bv/bv_subtheory_bitblast.cpp +++ b/src/theory/bv/bv_subtheory_bitblast.cpp @@ -18,7 +18,6 @@ #include "decision/decision_attributes.h" #include "options/bv_options.h" #include "options/decision_options.h" -#include "proof/bitvector_proof.h" #include "proof/proof_manager.h" #include "smt/smt_statistics_registry.h" #include "theory/bv/abstraction.h" @@ -277,7 +276,8 @@ void BitblastSolver::setConflict(TNode conflict) { d_bv->setConflict(final_conflict); } -void BitblastSolver::setProofLog( BitVectorProof * bvp ) { +void BitblastSolver::setProofLog(proof::ResolutionBitVectorProof* bvp) +{ d_bitblaster->setProofLog( bvp ); bvp->setBitblaster(d_bitblaster.get()); } diff --git a/src/theory/bv/bv_subtheory_bitblast.h b/src/theory/bv/bv_subtheory_bitblast.h index ac0d38815..aa2c90c43 100644 --- a/src/theory/bv/bv_subtheory_bitblast.h +++ b/src/theory/bv/bv_subtheory_bitblast.h @@ -23,6 +23,11 @@ #include "theory/bv/bv_subtheory.h" namespace CVC4 { + +namespace proof { +class ResolutionBitVectorProof; +} + namespace theory { namespace bv { @@ -74,7 +79,7 @@ public: void bitblastQueue(); void setAbstraction(AbstractionModule* module); uint64_t computeAtomWeight(TNode atom); - void setProofLog(BitVectorProof* bvp) override; + void setProofLog(proof::ResolutionBitVectorProof* bvp) override; }; } /* namespace CVC4::theory::bv */ diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp index d08405ef3..e60d60456 100644 --- a/src/theory/bv/theory_bv.cpp +++ b/src/theory/bv/theory_bv.cpp @@ -986,9 +986,10 @@ bool TheoryBV::applyAbstraction(const std::vector& assertions, std::vector return changed; } -void TheoryBV::setProofLog( BitVectorProof * bvp ) { +void TheoryBV::setResolutionProofLog(proof::ResolutionBitVectorProof* bvp) +{ if( options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER ){ - d_eagerSolver->setProofLog( bvp ); + d_eagerSolver->setResolutionProofLog(bvp); }else{ for( unsigned i=0; i< d_subtheories.size(); i++ ){ d_subtheories[i]->setProofLog( bvp ); diff --git a/src/theory/bv/theory_bv.h b/src/theory/bv/theory_bv.h index d5e3ad02e..afa9f4b4f 100644 --- a/src/theory/bv/theory_bv.h +++ b/src/theory/bv/theory_bv.h @@ -104,9 +104,9 @@ public: bool applyAbstraction(const std::vector& assertions, std::vector& new_assertions); - void setProofLog( BitVectorProof * bvp ); + void setResolutionProofLog(proof::ResolutionBitVectorProof* bvp); -private: + private: class Statistics { public: -- cgit v1.2.3 From 854c414f056e306dd8678fd7459ee9dee8b8d6b3 Mon Sep 17 00:00:00 2001 From: Andrew Reynolds Date: Mon, 3 Dec 2018 17:00:58 -0600 Subject: Skip non-cardinality types in sets min card inference (#2734) --- src/theory/sets/theory_sets_private.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/theory/sets/theory_sets_private.cpp b/src/theory/sets/theory_sets_private.cpp index 83c66c2d3..1c302573e 100644 --- a/src/theory/sets/theory_sets_private.cpp +++ b/src/theory/sets/theory_sets_private.cpp @@ -1543,6 +1543,12 @@ void TheorySetsPrivate::checkMinCard( std::vector< Node >& lemmas ) { for( int i=(int)(d_set_eqc.size()-1); i>=0; i-- ){ Node eqc = d_set_eqc[i]; + TypeNode tn = eqc.getType().getSetElementType(); + if (d_t_card_enabled.find(tn) == d_t_card_enabled.end()) + { + // cardinality is not enabled for this type, skip + continue; + } //get members in class std::map< Node, std::map< Node, Node > >::iterator itm = d_pol_mems[0].find( eqc ); if( itm!=d_pol_mems[0].end() ){ -- cgit v1.2.3 From cf7a6431e7fdcbb30704343f57411154464e0c30 Mon Sep 17 00:00:00 2001 From: Andrew Reynolds Date: Tue, 4 Dec 2018 13:52:17 -0600 Subject: Enable regular expression elimination by default. (#2736) Seems to have no impact on Norn, and is helpful for a number of applications. --- src/options/strings_options.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/options/strings_options.toml b/src/options/strings_options.toml index 77056e279..3544c37fe 100644 --- a/src/options/strings_options.toml +++ b/src/options/strings_options.toml @@ -205,7 +205,7 @@ header = "options/strings_options.h" category = "regular" long = "re-elim" type = "bool" - default = "false" + default = "true" help = "elimination techniques for regular expressions" [[option]] -- cgit v1.2.3 From 33ec6ac29c55ac6db7d86a700cb5e8f06b93ab96 Mon Sep 17 00:00:00 2001 From: Andrew Reynolds Date: Tue, 4 Dec 2018 16:04:47 -0600 Subject: Apply extended rewriting on PBE static symmetry breaking. (#2735) --- src/theory/quantifiers/sygus/sygus_pbe.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/theory/quantifiers/sygus/sygus_pbe.cpp b/src/theory/quantifiers/sygus/sygus_pbe.cpp index e8aa0a7f0..7891814be 100644 --- a/src/theory/quantifiers/sygus/sygus_pbe.cpp +++ b/src/theory/quantifiers/sygus/sygus_pbe.cpp @@ -272,6 +272,10 @@ bool SygusPbe::initialize(Node n, Assert(!ag.isNull()); disj.push_back(ag.negate()); Node lem = disj.size() == 1 ? disj[0] : nm->mkNode(OR, disj); + // Apply extended rewriting on the lemma. This helps utilities like + // SygusEnumerator more easily recognize the shape of this lemma, e.g. + // ( ~is-ite(x) or ( ~is-ite(x) ^ P ) ) --> ~is-ite(x). + lem = d_tds->getExtRewriter()->extendedRewrite(lem); Trace("sygus-pbe") << " static redundant op lemma : " << lem << std::endl; // Register as a symmetry breaking lemma with the term database. -- cgit v1.2.3 From 7145d0772794013fd6eb2f145a43a30be64aa557 Mon Sep 17 00:00:00 2001 From: Andrew Reynolds Date: Thu, 6 Dec 2018 10:38:05 -0600 Subject: Take into account minimality and types for cached PBE solutions (#2738) --- src/theory/quantifiers/sygus/sygus_unif_io.cpp | 50 ++++++++++++++++++++++---- src/theory/quantifiers/sygus/sygus_unif_io.h | 9 +++-- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/theory/quantifiers/sygus/sygus_unif_io.cpp b/src/theory/quantifiers/sygus/sygus_unif_io.cpp index a6e6b54c6..0b378875c 100644 --- a/src/theory/quantifiers/sygus/sygus_unif_io.cpp +++ b/src/theory/quantifiers/sygus/sygus_unif_io.cpp @@ -1013,6 +1013,7 @@ Node SygusUnifIo::constructSol( bool retValMod = x.isReturnValueModified(); Node ret_dt; + Node cached_ret_dt; if (nrole == role_equal) { if (!retValMod) @@ -1094,14 +1095,14 @@ Node SygusUnifIo::constructSol( { bool firstTime = true; std::unordered_set intersection; - std::map>::iterator + std::map>::iterator pit; for (size_t i = 0, nvals = x.d_vals.size(); i < nvals; i++) { if (x.d_vals[i].getConst()) { - pit = d_psolutions.find(i); - if (pit == d_psolutions.end()) + pit = d_psolutions[i].find(etn); + if (pit == d_psolutions[i].end()) { // no cached solution intersection.clear(); @@ -1135,12 +1136,31 @@ Node SygusUnifIo::constructSol( } if (!intersection.empty()) { - ret_dt = *intersection.begin(); + if (d_enableMinimality) + { + // if we are enabling minimality, the minimal cached solution may + // still not be the best solution, thus we remember it and keep it if + // we don't construct a better one below + std::vector intervec; + intervec.insert( + intervec.begin(), intersection.begin(), intersection.end()); + cached_ret_dt = getMinimalTerm(intervec); + } + else + { + ret_dt = *intersection.begin(); + } if (Trace.isOn("sygus-sui-dt")) { indent("sygus-sui-dt", ind); Trace("sygus-sui-dt") << "ConstructPBE: found in cache: "; - TermDbSygus::toStreamSygus("sygus-sui-dt", ret_dt); + Node csol = ret_dt; + if (d_enableMinimality) + { + csol = cached_ret_dt; + Trace("sygus-sui-dt") << "(minimal) "; + } + TermDbSygus::toStreamSygus("sygus-sui-dt", csol); Trace("sygus-sui-dt") << std::endl; } } @@ -1455,6 +1475,24 @@ Node SygusUnifIo::constructSol( sindex++; } + // if there was a cached solution, process it now + if (!cached_ret_dt.isNull() && cached_ret_dt != ret_dt) + { + if (ret_dt.isNull()) + { + // take the cached one if it is the only one + ret_dt = cached_ret_dt; + } + else if (d_enableMinimality) + { + Assert(ret_dt.getType() == cached_ret_dt.getType()); + // take the cached one if it is smaller + std::vector retDts; + retDts.push_back(cached_ret_dt); + retDts.push_back(ret_dt); + ret_dt = getMinimalTerm(retDts); + } + } Assert(ret_dt.isNull() || ret_dt.getType() == e.getType()); if (Trace.isOn("sygus-sui-dt")) { @@ -1479,7 +1517,7 @@ Node SygusUnifIo::constructSol( TermDbSygus::toStreamSygus("sygus-sui-cache", ret_dt); Trace("sygus-sui-cache") << std::endl; } - d_psolutions[i].insert(ret_dt); + d_psolutions[i][etn].insert(ret_dt); } } } diff --git a/src/theory/quantifiers/sygus/sygus_unif_io.h b/src/theory/quantifiers/sygus/sygus_unif_io.h index f189353b0..7f48645bf 100644 --- a/src/theory/quantifiers/sygus/sygus_unif_io.h +++ b/src/theory/quantifiers/sygus/sygus_unif_io.h @@ -304,9 +304,14 @@ class SygusUnifIo : public SygusUnif unsigned d_sol_term_size; /** partial solutions * - * Maps indices for I/O points to a list of solutions for that point. + * Maps indices for I/O points to a list of solutions for that point, for each + * type. We may have more than one type for solutions, e.g. for grammar: + * A -> ite( A, B, C ) | ... + * where terms of type B and C can both act as solutions. */ - std::map> d_psolutions; + std::map>> + d_psolutions; /** * This flag is set to true if the solution construction was * non-deterministic with respect to failure/success. -- cgit v1.2.3 From 63fb4e8c33db706589fe41476c4d3358fb47164e Mon Sep 17 00:00:00 2001 From: Andres Noetzli Date: Thu, 6 Dec 2018 15:23:00 -0800 Subject: Fix use-after-free due to destruction order (#2739) A test for PR #2737 was failing even though the PR only added dead code. This PR fixes the issue by fixing two use-after-free bugs: - `ResolutionBitVectorProof` has a `Context` and a `std::unique_ptr` member. The `BVSatProof` depends on the `Context` and tries to access it (indirectly) in its constructor but because the context was declared after the proof, the context was destroyed before the proof, leading to a use-after-free in a method called from the proof's destructor. This commit reorders the two members. - `TLazyBitblaster` was destroyed before the `LFSCCnfProof` in `BitVectorProof` because `SmtEngine`'s destructor first destroyed the theory engine and then the proof manager. This lead to a use-after-free because `LFSCCnfProof` was using the `d_nullContext` of `TLazyBitblaster`, which got indirectly accessed in `LFSCCnfProof`'s destructor. This commit moves the destruction of `ProofManager` above the destruction of the theory engine. The issues were likely introduced by #2599. They went undetected because our nightlies' ASAN check does not use proofs due to known memory leaks in the proof module of CVC4. I have tested this PR up to regression level 2 with ASAN with leak detection disabled. --- src/proof/resolution_bitvector_proof.h | 3 ++- src/smt/smt_engine.cpp | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/proof/resolution_bitvector_proof.h b/src/proof/resolution_bitvector_proof.h index a54d72d3f..ccb288f6e 100644 --- a/src/proof/resolution_bitvector_proof.h +++ b/src/proof/resolution_bitvector_proof.h @@ -98,6 +98,8 @@ class ResolutionBitVectorProof : public BitVectorProof void initCnfProof(prop::CnfStream* cnfStream, context::Context* cnf) override; protected: + context::Context d_fakeContext; + // The CNF formula that results from bit-blasting will need a proof. // This is that proof. std::unique_ptr d_resolutionProof; @@ -105,7 +107,6 @@ class ResolutionBitVectorProof : public BitVectorProof bool d_isAssumptionConflict; theory::TheoryId getTheoryId() override; - context::Context d_fakeContext; }; class LFSCBitVectorProof : public ResolutionBitVectorProof diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index a0939f4db..6814ad531 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -1071,6 +1071,18 @@ SmtEngine::~SmtEngine() //destroy all passes before destroying things that they refer to d_private->cleanupPreprocessingPasses(); + // d_proofManager is always created when proofs are enabled at configure + // time. Because of this, this code should not be wrapped in PROOF() which + // additionally checks flags such as options::proof(). + // + // Note: the proof manager must be destroyed before the theory engine. + // Because the destruction of the proofs depends on contexts owned be the + // theory solvers. +#ifdef CVC4_PROOF + delete d_proofManager; + d_proofManager = NULL; +#endif + delete d_theoryEngine; d_theoryEngine = NULL; delete d_propEngine; @@ -1078,15 +1090,6 @@ SmtEngine::~SmtEngine() delete d_decisionEngine; d_decisionEngine = NULL; - -// d_proofManager is always created when proofs are enabled at configure time. -// Becuase of this, this code should not be wrapped in PROOF() which -// additionally checks flags such as options::proof(). -#ifdef CVC4_PROOF - delete d_proofManager; - d_proofManager = NULL; -#endif - delete d_stats; d_stats = NULL; delete d_statisticsRegistry; -- cgit v1.2.3 From 14fc21fc1101587810e64b0ed78ce03622e2939d Mon Sep 17 00:00:00 2001 From: Alex Ozdemir Date: Thu, 6 Dec 2018 18:56:56 -0800 Subject: Enable BV proofs when using an eager bitblaster (#2733) * Enable BV proofs when using and eager bitblaster Specifically: * Removed assertions that blocked them. * Made sure that only bitvectors were stored in the BV const let-map * Prevented true/false from being bit-blasted by the eager bitblaster Also: * uncommented "no-check-proofs" from relevant tests * Option handler logic for BV proofs BV eager proofs only work when minisat is the sat solver being used by the BV theory. Added logic to the --proof hanlder to verify this or throw an option exception. * Bugfix for proof options handler I forgot that proofEnabledBuild runs even if the --proof option is negated. In my handler I now check that proofs are enabled. * Clang-format --- src/options/options_handler.cpp | 9 ++++++++- src/proof/bitvector_proof.cpp | 6 +++--- src/proof/proof_manager.cpp | 3 +-- src/theory/bv/bitblast/eager_bitblaster.cpp | 4 ++-- test/regress/regress0/bv/ackermann1.smt2 | 2 +- test/regress/regress0/bv/ackermann2.smt2 | 2 +- test/regress/regress1/bv/bug787.smt2 | 2 +- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/options/options_handler.cpp b/src/options/options_handler.cpp index bd5b00728..a808ecd3c 100644 --- a/src/options/options_handler.cpp +++ b/src/options/options_handler.cpp @@ -1632,7 +1632,14 @@ void OptionsHandler::setProduceAssertions(std::string option, bool value) void OptionsHandler::proofEnabledBuild(std::string option, bool value) { -#ifndef CVC4_PROOF +#ifdef CVC4_PROOF + if (value && options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER + && options::bvSatSolver() != theory::bv::SAT_SOLVER_MINISAT) + { + throw OptionException( + "Eager BV proofs only supported when minisat is used"); + } +#else if(value) { std::stringstream ss; ss << "option `" << option << "' requires a proofs-enabled build of CVC4; this binary was not built with proof support"; diff --git a/src/proof/bitvector_proof.cpp b/src/proof/bitvector_proof.cpp index c9e98d170..9eb39e2e2 100644 --- a/src/proof/bitvector_proof.cpp +++ b/src/proof/bitvector_proof.cpp @@ -81,7 +81,9 @@ void BitVectorProof::registerTerm(Expr term) { Debug("pf::bv") << "BitVectorProof::registerTerm( " << term << " )" << std::endl; - if (options::lfscLetification() && term.isConst()) { + if (options::lfscLetification() && term.isConst() + && term.getType().isBitVector()) + { if (d_constantLetMap.find(term) == d_constantLetMap.end()) { std::ostringstream name; name << "letBvc" << d_constantLetMap.size(); @@ -624,8 +626,6 @@ void BitVectorProof::printBitblasting(std::ostream& os, std::ostream& paren) std::vector::const_iterator it = d_bbTerms.begin(); std::vector::const_iterator end = d_bbTerms.end(); - Assert(options::bitblastMode() != theory::bv::BITBLAST_MODE_EAGER); - for (; it != end; ++it) { if (d_usedBB.find(*it) == d_usedBB.end()) { Debug("pf::bv") << "\t" << *it << "\t(UNUSED)" << std::endl; diff --git a/src/proof/proof_manager.cpp b/src/proof/proof_manager.cpp index 5b26432dd..9878972bf 100644 --- a/src/proof/proof_manager.cpp +++ b/src/proof/proof_manager.cpp @@ -559,8 +559,6 @@ void LFSCProof::toStream(std::ostream& out, const ProofLetMap& map) const void LFSCProof::toStream(std::ostream& out) const { - Assert(options::bitblastMode() != theory::bv::BITBLAST_MODE_EAGER); - Assert(!d_satProof->proofConstructed()); d_satProof->constructProof(); @@ -730,6 +728,7 @@ void LFSCProof::toStream(std::ostream& out) const d_theoryProof->printTheoryLemmas(used_lemmas, out, paren, globalLetMap); Debug("pf::pm") << "Proof manager: printing theory lemmas DONE!" << std::endl; + out << ";; Printing final unsat proof \n"; if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER && ProofManager::getBitVectorProof()) { proof::LFSCProofPrinter::printResolutionEmptyClause( ProofManager::getBitVectorProof()->getSatProof(), out, paren); diff --git a/src/theory/bv/bitblast/eager_bitblaster.cpp b/src/theory/bv/bitblast/eager_bitblaster.cpp index 33d5a1c80..019918c2f 100644 --- a/src/theory/bv/bitblast/eager_bitblaster.cpp +++ b/src/theory/bv/bitblast/eager_bitblaster.cpp @@ -99,8 +99,8 @@ void EagerBitblaster::bbFormula(TNode node) void EagerBitblaster::bbAtom(TNode node) { node = node.getKind() == kind::NOT ? node[0] : node; - if (node.getKind() == kind::BITVECTOR_BITOF) return; - if (hasBBAtom(node)) + if (node.getKind() == kind::BITVECTOR_BITOF + || node.getKind() == kind::CONST_BOOLEAN || hasBBAtom(node)) { return; } diff --git a/test/regress/regress0/bv/ackermann1.smt2 b/test/regress/regress0/bv/ackermann1.smt2 index 9b96b38c4..218fd746b 100644 --- a/test/regress/regress0/bv/ackermann1.smt2 +++ b/test/regress/regress0/bv/ackermann1.smt2 @@ -1,4 +1,4 @@ -; COMMAND-LINE: --bitblast=eager --no-check-models --no-check-proofs --no-check-unsat-cores +; COMMAND-LINE: --bitblast=eager --no-check-models --no-check-unsat-cores ; EXPECT: sat (set-logic QF_UFBV) (set-info :smt-lib-version 2.0) diff --git a/test/regress/regress0/bv/ackermann2.smt2 b/test/regress/regress0/bv/ackermann2.smt2 index eeca505fe..b1aaa7d64 100644 --- a/test/regress/regress0/bv/ackermann2.smt2 +++ b/test/regress/regress0/bv/ackermann2.smt2 @@ -1,4 +1,4 @@ -; COMMAND-LINE: --bitblast=eager --no-check-models --no-check-proofs --no-check-unsat-cores +; COMMAND-LINE: --bitblast=eager --no-check-models --no-check-unsat-cores ; EXPECT: unsat (set-logic QF_UFBV) (set-info :smt-lib-version 2.0) diff --git a/test/regress/regress1/bv/bug787.smt2 b/test/regress/regress1/bv/bug787.smt2 index 8e0ba0016..d732b9ff0 100644 --- a/test/regress/regress1/bv/bug787.smt2 +++ b/test/regress/regress1/bv/bug787.smt2 @@ -1,4 +1,4 @@ -; COMMAND-LINE: --bitblast=eager --no-check-proofs +; COMMAND-LINE: --bitblast=eager ; EXPECT: unsat (set-logic QF_BV) (set-info :status unsat) -- cgit v1.2.3 From 136a30c2b8cb06d607c5544a3911f120216b3663 Mon Sep 17 00:00:00 2001 From: Alex Ozdemir Date: Thu, 6 Dec 2018 20:00:03 -0800 Subject: Arith Constraint Proof Loggin (#2732) * Arith Constraint Proof Logging Also a tiny documentation update. * Debug.isOn check around iterated output * reference iteratees --- src/theory/arith/constraint.cpp | 29 ++++++++++++++++++++++++++++- src/theory/arith/constraint.h | 6 +++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/theory/arith/constraint.cpp b/src/theory/arith/constraint.cpp index ff71f6432..352ba0f36 100644 --- a/src/theory/arith/constraint.cpp +++ b/src/theory/arith/constraint.cpp @@ -602,7 +602,7 @@ void ConstraintRule::print(std::ostream& out) const { bool Constraint::wellFormedFarkasProof() const { Assert(hasProof()); - + const ConstraintRule& cr = getConstraintRule(); if(cr.d_constraint != this){ return false; } if(cr.d_proofType != FarkasAP){ return false; } @@ -1071,6 +1071,7 @@ ConstraintP ConstraintDatabase::lookup(TNode literal) const{ } void Constraint::setAssumption(bool nowInConflict){ + Debug("constraints::pf") << "setAssumption(" << this << ")" << std::endl; Assert(!hasProof()); Assert(negationHasProof() == nowInConflict); Assert(hasLiteral()); @@ -1113,6 +1114,7 @@ void Constraint::propagate(){ * 1*(x <= a) + (-1)*(x > b) => (0 <= a-b) */ void Constraint::impliedByUnate(ConstraintCP imp, bool nowInConflict){ + Debug("constraints::pf") << "impliedByUnate(" << this << ", " << *imp << ")" << std::endl; Assert(!hasProof()); Assert(imp->hasProof()); Assert(negationHasProof() == nowInConflict); @@ -1152,6 +1154,8 @@ void Constraint::impliedByUnate(ConstraintCP imp, bool nowInConflict){ } void Constraint::impliedByTrichotomy(ConstraintCP a, ConstraintCP b, bool nowInConflict){ + Debug("constraints::pf") << "impliedByTrichotomy(" << this << ", " << *a << ", "; + Debug("constraints::pf") << *b << ")" << std::endl; Assert(!hasProof()); Assert(negationHasProof() == nowInConflict); Assert(a->hasProof()); @@ -1180,6 +1184,7 @@ bool Constraint::allHaveProof(const ConstraintCPVec& b){ } void Constraint::impliedByIntHole(ConstraintCP a, bool nowInConflict){ + Debug("constraints::pf") << "impliedByIntHole(" << this << ", " << *a << ")" << std::endl; Assert(!hasProof()); Assert(negationHasProof() == nowInConflict); Assert(a->hasProof()); @@ -1196,6 +1201,15 @@ void Constraint::impliedByIntHole(ConstraintCP a, bool nowInConflict){ } void Constraint::impliedByIntHole(const ConstraintCPVec& b, bool nowInConflict){ + Debug("constraints::pf") << "impliedByIntHole(" << this; + if (Debug.isOn("constraints::pf")) { + for (const ConstraintCP& p : b) + { + Debug("constraints::pf") << ", " << p; + } + } + Debug("constraints::pf") << ")" << std::endl; + Assert(!hasProof()); Assert(negationHasProof() == nowInConflict); Assert(allHaveProof(b)); @@ -1224,6 +1238,15 @@ void Constraint::impliedByIntHole(const ConstraintCPVec& b, bool nowInConflict){ * coeff.back() corresponds to the current constraint. */ void Constraint::impliedByFarkas(const ConstraintCPVec& a, RationalVectorCP coeffs, bool nowInConflict){ + Debug("constraints::pf") << "impliedByFarkas(" << this; + if (Debug.isOn("constraints::pf")) { + for (const ConstraintCP& p : a) + { + Debug("constraints::pf") << ", " << p; + } + } + Debug("constraints::pf") << ", "; + Debug("constraints::pf") << ")" << std::endl; Assert(!hasProof()); Assert(negationHasProof() == nowInConflict); Assert(allHaveProof(a)); @@ -1263,6 +1286,8 @@ void Constraint::impliedByFarkas(const ConstraintCPVec& a, RationalVectorCP coef void Constraint::setInternalAssumption(bool nowInConflict){ + Debug("constraints::pf") << "setInternalAssumption(" << this; + Debug("constraints::pf") << ")" << std::endl; Assert(!hasProof()); Assert(negationHasProof() == nowInConflict); Assert(!assertedToTheTheory()); @@ -1277,6 +1302,8 @@ void Constraint::setInternalAssumption(bool nowInConflict){ void Constraint::setEqualityEngineProof(){ + Debug("constraints::pf") << "setEqualityEngineProof(" << this; + Debug("constraints::pf") << ")" << std::endl; Assert(truthIsUnknown()); Assert(hasLiteral()); d_database->pushConstraintRule(ConstraintRule(this, EqualityEngineAP)); diff --git a/src/theory/arith/constraint.h b/src/theory/arith/constraint.h index 5eef9663e..d411f2d34 100644 --- a/src/theory/arith/constraint.h +++ b/src/theory/arith/constraint.h @@ -928,7 +928,11 @@ private: /** Return true if every element in b has a proof. */ static bool allHaveProof(const ConstraintCPVec& b); - /** Precondition: hasFarkasProof() */ + /** Precondition: hasFarkasProof() + * Computes the combination implied by the farkas coefficients. Sees if it is + * a contradiction. + */ + bool wellFormedFarkasProof() const; }; /* class ConstraintValue */ -- cgit v1.2.3 From 7270b2a800c45fa87ef4cdcad8fc353ccb8cd471 Mon Sep 17 00:00:00 2001 From: Andres Noetzli Date: Fri, 7 Dec 2018 07:48:38 -0800 Subject: Strings: Make EXTF_d inference more conservative (#2740) --- src/theory/strings/theory_strings.cpp | 6 +++--- test/regress/CMakeLists.txt | 1 + test/regress/regress2/strings/extf_d_perf.smt2 | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 test/regress/regress2/strings/extf_d_perf.smt2 diff --git a/src/theory/strings/theory_strings.cpp b/src/theory/strings/theory_strings.cpp index 9da6fd277..5179ddab3 100644 --- a/src/theory/strings/theory_strings.cpp +++ b/src/theory/strings/theory_strings.cpp @@ -1662,9 +1662,9 @@ void TheoryStrings::checkExtfEval( int effort ) { << ", const = " << einfo.d_const << std::endl; for (const Node& nrcc : nrc) { - sendInference(einfo.d_exp, - einfo.d_const == d_false ? nrcc.negate() : nrcc, - effort == 0 ? "EXTF_d" : "EXTF_d-N"); + sendInternalInference(einfo.d_exp, + einfo.d_const == d_false ? nrcc.negate() : nrcc, + effort == 0 ? "EXTF_d" : "EXTF_d-N"); } }else{ to_reduce = nrc; diff --git a/test/regress/CMakeLists.txt b/test/regress/CMakeLists.txt index e0e57acf9..f22796929 100644 --- a/test/regress/CMakeLists.txt +++ b/test/regress/CMakeLists.txt @@ -1729,6 +1729,7 @@ set(regress_2_tests regress2/strings/cmu-disagree-0707-dd.smt2 regress2/strings/cmu-prereg-fmf.smt2 regress2/strings/cmu-repl-len-nterm.smt2 + regress2/strings/extf_d_perf.smt2 regress2/strings/issue918.smt2 regress2/strings/non_termination_regular_expression6.smt2 regress2/strings/norn-dis-0707-3.smt2 diff --git a/test/regress/regress2/strings/extf_d_perf.smt2 b/test/regress/regress2/strings/extf_d_perf.smt2 new file mode 100644 index 000000000..7ad094dcb --- /dev/null +++ b/test/regress/regress2/strings/extf_d_perf.smt2 @@ -0,0 +1,19 @@ +; COMMAND-LINE: --strings-exp --strings-fmf +; EXPECT: sat +(set-logic ALL) +(declare-fun _substvar_140_ () String) +(declare-fun _substvar_195_ () Int) +(declare-fun _substvar_201_ () Int) +(assert (let ((_let_0 (str.substr _substvar_140_ 10 (+ 0 (str.len _substvar_140_))))) (let ((_let_3 (str.substr _let_0 0 (str.indexof _let_0 "/" 0)))) (let ((_let_4 (str.substr _let_3 0 7))) (let ((_let_5 (str.substr _let_3 8 (+ _substvar_201_ (str.len _let_3))))) + (and + (str.contains _substvar_140_ "://") + (str.contains _let_3 "@") + (str.contains _let_5 ",") + (not (= (str.len (str.substr _let_0 (+ 1 (str.indexof _let_0 "/" 0)) _substvar_195_)) 0)) + (not (= (str.len _let_4) 0)) + (not (str.contains _let_0 ".sock")) + (not (str.contains _let_4 "@")) + (not (= (str.len _let_5) 0)) + (= "mongodb://" (str.substr _substvar_140_ 0 10)))))))) +(check-sat) + -- cgit v1.2.3 From e1dc39321cd4ab29b436025badfb05714f5649b3 Mon Sep 17 00:00:00 2001 From: makaimann Date: Mon, 10 Dec 2018 08:37:11 -0800 Subject: BoolToBV modes (off, ite, all) (#2530) --- src/options/CMakeLists.txt | 2 + src/options/bool_to_bv_mode.cpp | 42 ++++ src/options/bool_to_bv_mode.h | 57 +++++ src/options/bv_options.toml | 17 +- src/options/options_handler.cpp | 44 ++++ src/options/options_handler.h | 4 + src/preprocessing/passes/bool_to_bv.cpp | 326 ++++++++++++++++----------- src/preprocessing/passes/bool_to_bv.h | 37 +-- src/smt/smt_engine.cpp | 38 +++- src/theory/bv/theory_bv.cpp | 2 +- test/regress/CMakeLists.txt | 3 +- test/regress/regress0/bv/bool-to-bv-all.smt2 | 19 ++ test/regress/regress0/bv/bool-to-bv-ite.smt2 | 13 ++ test/regress/regress0/bv/bool-to-bv.smt2 | 19 -- 14 files changed, 447 insertions(+), 176 deletions(-) create mode 100644 src/options/bool_to_bv_mode.cpp create mode 100644 src/options/bool_to_bv_mode.h create mode 100644 test/regress/regress0/bv/bool-to-bv-all.smt2 create mode 100644 test/regress/regress0/bv/bool-to-bv-ite.smt2 delete mode 100644 test/regress/regress0/bv/bool-to-bv.smt2 diff --git a/src/options/CMakeLists.txt b/src/options/CMakeLists.txt index c711567ab..b86db8d00 100644 --- a/src/options/CMakeLists.txt +++ b/src/options/CMakeLists.txt @@ -9,6 +9,8 @@ libcvc4_add_sources( arith_unate_lemma_mode.cpp arith_unate_lemma_mode.h base_handlers.h + bool_to_bv_mode.cpp + bool_to_bv_mode.h bv_bitblast_mode.cpp bv_bitblast_mode.h datatypes_modes.h diff --git a/src/options/bool_to_bv_mode.cpp b/src/options/bool_to_bv_mode.cpp new file mode 100644 index 000000000..670e15419 --- /dev/null +++ b/src/options/bool_to_bv_mode.cpp @@ -0,0 +1,42 @@ +/********************* */ +/*! \file bool_to_bv_mode.cpp +** \verbatim +** Top contributors (to current version): +** Makai Mann +** This file is part of the CVC4 project. +** Copyright (c) 2009-2018 by the authors listed in the file AUTHORS +** in the top-level source directory) and their institutional affiliations. +** All rights reserved. See the file COPYING in the top-level source +** directory for licensing information.\endverbatim +** +** \brief Modes for bool-to-bv preprocessing pass +** +** Modes for bool-to-bv preprocessing pass which tries to lower booleans +** to bit-vectors of width 1 at various levels of aggressiveness. +**/ + +#include "options/bool_to_bv_mode.h" + +#include + + +namespace CVC4 +{ + std::ostream& operator<<(std::ostream& out, preprocessing::passes::BoolToBVMode mode) { + switch(mode) { + case preprocessing::passes::BOOL_TO_BV_OFF: + out << "BOOL_TO_BV_OFF"; + break; + case preprocessing::passes::BOOL_TO_BV_ITE: + out << "BOOL_TO_BV_ITE"; + break; + case preprocessing::passes::BOOL_TO_BV_ALL: + out << "BOOL_TO_BV_ALL"; + break; + default: + out << "BoolToBVMode:UNKNOWN![" << unsigned(mode) << "]"; + } + + return out; + } +} // namespace CVC4 diff --git a/src/options/bool_to_bv_mode.h b/src/options/bool_to_bv_mode.h new file mode 100644 index 000000000..f2911c339 --- /dev/null +++ b/src/options/bool_to_bv_mode.h @@ -0,0 +1,57 @@ +/********************* */ +/*! \file bool_to_bv_mode.h +** \verbatim +** Top contributors (to current version): +** Makai Mann +** This file is part of the CVC4 project. +** Copyright (c) 2009-2018 by the authors listed in the file AUTHORS +** in the top-level source directory) and their institutional affiliations. +** All rights reserved. See the file COPYING in the top-level source +** directory for licensing information.\endverbatim +** +** \brief Modes for bool-to-bv preprocessing pass +** +** Modes for bool-to-bv preprocessing pass which tries to lower booleans +** to bit-vectors of width 1 at various levels of aggressiveness. +**/ + +#include "cvc4_private.h" + +#ifndef __CVC4__PREPROCESSING__PASSES__BOOL_TO_BV_MODE_H +#define __CVC4__PREPROCESSING__PASSES__BOOL_TO_BV_MODE_H + +#include + +namespace CVC4 { +namespace preprocessing { +namespace passes { + +/** Enumeration of bool-to-bv modes */ +enum BoolToBVMode +{ + /** + * No bool-to-bv pass + */ + BOOL_TO_BV_OFF, + + /** + * Only lower bools in condition of ITEs + * Tries to give more info to bit-vector solver + * by using bit-vector-ITEs when possible + */ + BOOL_TO_BV_ITE, + + /** + * Lower every bool beneath the top layer to be a + * bit-vector + */ + BOOL_TO_BV_ALL +}; +} +} + +std::ostream& operator<<(std::ostream& out, preprocessing::passes::BoolToBVMode mode); + +} + +#endif /* __CVC4__PREPROCESSING__PASSES__BOOL_TO_BV_MODE_H */ diff --git a/src/options/bv_options.toml b/src/options/bv_options.toml index 15a9047c7..00290da7d 100644 --- a/src/options/bv_options.toml +++ b/src/options/bv_options.toml @@ -105,11 +105,22 @@ header = "options/bv_options.h" [[option]] name = "boolToBitvector" + smt_name = "bool-to-bv" category = "regular" - long = "bool-to-bv" + long = "bool-to-bv=MODE" + type = "CVC4::preprocessing::passes::BoolToBVMode" + default = "CVC4::preprocessing::passes::BOOL_TO_BV_OFF" + handler = "stringToBoolToBVMode" + includes = ["options/bool_to_bv_mode.h"] + help = "convert booleans to bit-vectors of size 1 at various levels of aggressiveness, see --bool-to-bv=help" + +[[option]] + name = "bitwiseEq" + category = "regular" + long = "bitwise-eq" type = "bool" - default = "false" - help = "convert booleans to bit-vectors of size 1 when possible" + default = "true" + help = "lift equivalence with one-bit bit-vectors to be boolean operations" [[option]] name = "bitvectorDivByZeroConst" diff --git a/src/options/options_handler.cpp b/src/options/options_handler.cpp index a808ecd3c..420396452 100644 --- a/src/options/options_handler.cpp +++ b/src/options/options_handler.cpp @@ -1301,6 +1301,50 @@ theory::bv::BvSlicerMode OptionsHandler::stringToBvSlicerMode( } } +const std::string OptionsHandler::s_boolToBVModeHelp = + "\ +BoolToBV pass modes supported by the --bool-to-bv option:\n\ +\n\ +off (default)\n\ ++ Don't push any booleans to width one bit-vectors\n\ +\n\ +ite\n\ ++ Try to turn ITEs into BITVECTOR_ITE when possible. It can fail per-formula \n\ + if not all sub-formulas can be turned to bit-vectors\n\ +\n\ +all\n\ ++ Force all booleans to be bit-vectors of width one except at the top level.\n\ + Most aggressive mode\n\ +"; + +preprocessing::passes::BoolToBVMode OptionsHandler::stringToBoolToBVMode( + std::string option, std::string optarg) +{ + if (optarg == "off") + { + return preprocessing::passes::BOOL_TO_BV_OFF; + } + else if (optarg == "ite") + { + return preprocessing::passes::BOOL_TO_BV_ITE; + } + else if (optarg == "all") + { + return preprocessing::passes::BOOL_TO_BV_ALL; + } + else if (optarg == "help") + { + puts(s_boolToBVModeHelp.c_str()); + exit(1); + } + else + { + throw OptionException(std::string("unknown option for --bool-to-bv: `") + + optarg + + "'. Try --bool-to-bv=help"); + } +} + void OptionsHandler::setBitblastAig(std::string option, bool arg) { if(arg) { diff --git a/src/options/options_handler.h b/src/options/options_handler.h index 53e317895..f96632696 100644 --- a/src/options/options_handler.h +++ b/src/options/options_handler.h @@ -27,6 +27,7 @@ #include "options/arith_propagation_mode.h" #include "options/arith_unate_lemma_mode.h" #include "options/base_handlers.h" +#include "options/bool_to_bv_mode.h" #include "options/bv_bitblast_mode.h" #include "options/datatypes_modes.h" #include "options/decision_mode.h" @@ -137,6 +138,8 @@ public: std::string optarg); theory::bv::BvSlicerMode stringToBvSlicerMode(std::string option, std::string optarg); + preprocessing::passes::BoolToBVMode stringToBoolToBVMode(std::string option, + std::string optarg); void setBitblastAig(std::string option, bool arg); theory::bv::SatSolverMode stringToSatSolver(std::string option, @@ -229,6 +232,7 @@ public: static const std::string s_bvSatSolverHelp; static const std::string s_booleanTermConversionModeHelp; static const std::string s_bvSlicerModeHelp; + static const std::string s_boolToBVModeHelp; static const std::string s_cegqiFairModeHelp; static const std::string s_decisionModeHelp; static const std::string s_instFormatHelp ; diff --git a/src/preprocessing/passes/bool_to_bv.cpp b/src/preprocessing/passes/bool_to_bv.cpp index c8a59bdc4..252ab941c 100644 --- a/src/preprocessing/passes/bool_to_bv.cpp +++ b/src/preprocessing/passes/bool_to_bv.cpp @@ -9,17 +9,17 @@ ** All rights reserved. See the file COPYING in the top-level source ** directory for licensing information.\endverbatim ** - ** \brief The BoolToBv preprocessing pass + ** \brief The BoolToBV preprocessing pass ** **/ #include "preprocessing/passes/bool_to_bv.h" #include -#include -#include +#include "base/map_util.h" #include "expr/node.h" +#include "options/bv_options.h" #include "smt/smt_statistics_registry.h" #include "theory/rewriter.h" #include "theory/theory.h" @@ -30,183 +30,253 @@ namespace passes { using namespace CVC4::theory; BoolToBV::BoolToBV(PreprocessingPassContext* preprocContext) - : PreprocessingPass(preprocContext, "bool-to-bv"), - d_lowerCache(), - d_one(bv::utils::mkOne(1)), - d_zero(bv::utils::mkZero(1)), - d_statistics(){}; + : PreprocessingPass(preprocContext, "bool-to-bv"), d_statistics(){}; PreprocessingPassResult BoolToBV::applyInternal( AssertionPipeline* assertionsToPreprocess) { NodeManager::currentResourceManager()->spendResource( options::preprocessStep()); - std::vector new_assertions; - lowerBoolToBv(assertionsToPreprocess->ref(), new_assertions); - for (unsigned i = 0; i < assertionsToPreprocess->size(); ++i) + + unsigned size = assertionsToPreprocess->size(); + for (unsigned i = 0; i < size; ++i) { - assertionsToPreprocess->replace(i, Rewriter::rewrite(new_assertions[i])); + assertionsToPreprocess->replace( + i, Rewriter::rewrite(lowerAssertion((*assertionsToPreprocess)[i]))); } - return PreprocessingPassResult::NO_CONFLICT; -} -void BoolToBV::addToLowerCache(TNode term, Node new_term) -{ - Assert(new_term != Node()); - Assert(!hasLowerCache(term)); - d_lowerCache[term] = new_term; + return PreprocessingPassResult::NO_CONFLICT; } -Node BoolToBV::getLowerCache(TNode term) const +Node BoolToBV::fromCache(TNode n) const { - Assert(hasLowerCache(term)); - return d_lowerCache.find(term)->second; + if (d_lowerCache.find(n) != d_lowerCache.end()) + { + return d_lowerCache.find(n)->second; + } + return n; } -bool BoolToBV::hasLowerCache(TNode term) const +bool BoolToBV::needToRebuild(TNode n) const { - return d_lowerCache.find(term) != d_lowerCache.end(); + // check if any children were rebuilt + for (const Node& nn : n) + { + if (ContainsKey(d_lowerCache, nn)) + { + return true; + } + } + return false; } -Node BoolToBV::lowerNode(TNode current, bool topLevel) +Node BoolToBV::lowerAssertion(const TNode& a) { - Node result; + bool optionITE = options::boolToBitvector() == BOOL_TO_BV_ITE; NodeManager* nm = NodeManager::currentNM(); - if (hasLowerCache(current)) - { - result = getLowerCache(current); - } - else + std::vector visit; + visit.push_back(a); + std::unordered_set visited; + // for ite mode, keeps track of whether you're in an ite condition + // for all mode, unused + std::unordered_set ite_cond; + + while (!visit.empty()) { - if (current.getNumChildren() == 0) + TNode n = visit.back(); + visit.pop_back(); + + int numChildren = n.getNumChildren(); + Kind k = n.getKind(); + Debug("bool-to-bv") << "BoolToBV::lowerAssertion Post-traversal with " << n + << " and visited = " << ContainsKey(visited, n) + << std::endl; + + // Mark as visited + /* Optimization: if it's a leaf, don't need to wait to do the work */ + if (!ContainsKey(visited, n) && (numChildren > 0)) { - if (current.getKind() == kind::CONST_BOOLEAN) + visited.insert(n); + visit.push_back(n); + + // insert children in reverse order so that they're processed in order + // important for rewriting which sorts by node id + for (int i = numChildren - 1; i >= 0; --i) { - result = (current == bv::utils::mkTrue()) ? d_one : d_zero; + visit.push_back(n[i]); } - else + + if (optionITE) { - result = current; + // check for ite-conditions + if (k == kind::ITE) + { + ite_cond.insert(n[0]); + } + else if (ContainsKey(ite_cond, n)) + { + // being part of an ite condition is inherited from the parent + ite_cond.insert(n.begin(), n.end()); + } } } + /* Optimization for ite mode */ + else if (optionITE && !ContainsKey(ite_cond, n) && !needToRebuild(n)) + { + Debug("bool-to-bv") + << "BoolToBV::lowerAssertion Skipping because don't need to rebuild: " + << n << std::endl; + // in ite mode, if you've already visited the node but it's not + // in an ite condition and doesn't need to be rebuilt, then + // don't need to do anything + continue; + } else { - Kind kind = current.getKind(); - Kind new_kind = kind; - switch (kind) - { - case kind::EQUAL: - if (current[0].getType().isBitVector() - || current[0].getType().isBoolean()) - { - new_kind = kind::BITVECTOR_COMP; - } - break; - case kind::AND: new_kind = kind::BITVECTOR_AND; break; - case kind::OR: new_kind = kind::BITVECTOR_OR; break; - case kind::NOT: new_kind = kind::BITVECTOR_NOT; break; - case kind::XOR: new_kind = kind::BITVECTOR_XOR; break; - case kind::IMPLIES: new_kind = kind::BITVECTOR_OR; break; - case kind::ITE: - if (current.getType().isBitVector() || current.getType().isBoolean()) - { - new_kind = kind::BITVECTOR_ITE; - } - break; - case kind::BITVECTOR_ULT: new_kind = kind::BITVECTOR_ULTBV; break; - case kind::BITVECTOR_SLT: new_kind = kind::BITVECTOR_SLTBV; break; - case kind::BITVECTOR_ULE: - case kind::BITVECTOR_UGT: - case kind::BITVECTOR_UGE: - case kind::BITVECTOR_SLE: - case kind::BITVECTOR_SGT: - case kind::BITVECTOR_SGE: - // Should have been removed by rewriting. - Unreachable(); - default: break; - } - NodeBuilder<> builder(new_kind); - if (kind != new_kind) - { - ++(d_statistics.d_numTermsLowered); - } - if (current.getMetaKind() == kind::metakind::PARAMETERIZED) - { - builder << current.getOperator(); - } - Node converted; - if (new_kind == kind::ITE) + lowerNode(n); + } + } + + if (fromCache(a).getType().isBitVector()) + { + return nm->mkNode(kind::EQUAL, fromCache(a), bv::utils::mkOne(1)); + } + else + { + Assert(a == fromCache(a)); + return a; + } +} + +void BoolToBV::lowerNode(const TNode& n) +{ + NodeManager* nm = NodeManager::currentNM(); + Kind k = n.getKind(); + + bool all_bv = true; + // check if it was able to convert all children to bitvectors + for (const Node& nn : n) + { + all_bv = all_bv && fromCache(nn).getType().isBitVector(); + if (!all_bv) + { + break; + } + } + + if (!all_bv || (n.getNumChildren() == 0)) + { + if ((options::boolToBitvector() == BOOL_TO_BV_ALL) + && n.getType().isBoolean()) + { + if (k == kind::CONST_BOOLEAN) { - // Special-case ITE because need condition to be Boolean. - converted = lowerNode(current[0], true); - builder << converted; - converted = lowerNode(current[1]); - builder << converted; - converted = lowerNode(current[2]); - builder << converted; - } - else if (kind == kind::IMPLIES) { - // Special-case IMPLIES because needs to be rewritten. - converted = lowerNode(current[0]); - builder << nm->mkNode(kind::BITVECTOR_NOT, converted); - converted = lowerNode(current[1]); - builder << converted; + d_lowerCache[n] = (n == bv::utils::mkTrue()) ? bv::utils::mkOne(1) + : bv::utils::mkZero(1); } else { - for (unsigned i = 0; i < current.getNumChildren(); ++i) - { - converted = lowerNode(current[i]); - builder << converted; - } + d_lowerCache[n] = + nm->mkNode(kind::ITE, n, bv::utils::mkOne(1), bv::utils::mkZero(1)); } - result = builder; + + Debug("bool-to-bv") << "BoolToBV::lowerNode " << n << " =>\n" + << fromCache(n) << std::endl; + ++(d_statistics.d_numTermsForcedLowered); + return; } - if (result.getType().isBoolean()) + else { - ++(d_statistics.d_numTermsForcedLowered); - result = nm->mkNode(kind::ITE, result, d_one, d_zero); + // invariant + // either one of the children is not a bit-vector or bool + // i.e. something that can't be 'forced' to a bitvector + // or it's in 'ite' mode which will give up on bools that + // can't be converted easily + + Debug("bool-to-bv") << "BoolToBV::lowerNode skipping: " << n << std::endl; + return; } - addToLowerCache(current, result); } - if (topLevel) + + Kind new_kind = k; + switch (k) { - result = nm->mkNode(kind::EQUAL, result, d_one); + case kind::EQUAL: new_kind = kind::BITVECTOR_COMP; break; + case kind::AND: new_kind = kind::BITVECTOR_AND; break; + case kind::OR: new_kind = kind::BITVECTOR_OR; break; + case kind::NOT: new_kind = kind::BITVECTOR_NOT; break; + case kind::XOR: new_kind = kind::BITVECTOR_XOR; break; + case kind::IMPLIES: new_kind = kind::BITVECTOR_OR; break; + case kind::ITE: new_kind = kind::BITVECTOR_ITE; break; + case kind::BITVECTOR_ULT: new_kind = kind::BITVECTOR_ULTBV; break; + case kind::BITVECTOR_SLT: new_kind = kind::BITVECTOR_SLTBV; break; + case kind::BITVECTOR_ULE: + case kind::BITVECTOR_UGT: + case kind::BITVECTOR_UGE: + case kind::BITVECTOR_SLE: + case kind::BITVECTOR_SGT: + case kind::BITVECTOR_SGE: + // Should have been removed by rewriting. + Unreachable(); + default: break; } - Assert(result != Node()); - Debug("bool-to-bv") << "BoolToBV::lowerNode " << current << " => \n" - << result << "\n"; - return result; -} -void BoolToBV::lowerBoolToBv(const std::vector& assertions, - std::vector& new_assertions) -{ - for (unsigned i = 0; i < assertions.size(); ++i) + NodeBuilder<> builder(new_kind); + if ((options::boolToBitvector() == BOOL_TO_BV_ALL) && (new_kind != k)) + { + ++(d_statistics.d_numTermsLowered); + } + + if (n.getMetaKind() == kind::metakind::PARAMETERIZED) + { + builder << n.getOperator(); + } + + // special case IMPLIES because needs to be rewritten + if (k == kind::IMPLIES) + { + builder << nm->mkNode(kind::BITVECTOR_NOT, fromCache(n[0])); + builder << fromCache(n[1]); + } + else { - Node new_assertion = lowerNode(assertions[i], true); - new_assertions.push_back(new_assertion); - Trace("bool-to-bv") << " " << assertions[i] << " => " << new_assertions[i] - << "\n"; + for (const Node& nn : n) + { + builder << fromCache(nn); + } } + + Debug("bool-to-bv") << "BoolToBV::lowerNode " << n << " =>\n" + << builder << std::endl; + + d_lowerCache[n] = builder.constructNode(); } BoolToBV::Statistics::Statistics() - : d_numTermsLowered("preprocessing::passes::BoolToBV::NumTermsLowered", 0), - d_numAtomsLowered("preprocessing::passes::BoolToBV::NumAtomsLowered", 0), + : d_numIteToBvite("preprocessing::passes::BoolToBV::NumIteToBvite", 0), + d_numTermsLowered("preprocessing::passes:BoolToBV::NumTermsLowered", 0), d_numTermsForcedLowered( "preprocessing::passes::BoolToBV::NumTermsForcedLowered", 0) { - smtStatisticsRegistry()->registerStat(&d_numTermsLowered); - smtStatisticsRegistry()->registerStat(&d_numAtomsLowered); - smtStatisticsRegistry()->registerStat(&d_numTermsForcedLowered); + smtStatisticsRegistry()->registerStat(&d_numIteToBvite); + if (options::boolToBitvector() == BOOL_TO_BV_ALL) + { + // these statistics wouldn't be correct in the ITE mode, + // because it might discard rebuilt nodes if it fails to + // convert a bool to width-one bit-vector (never forces) + smtStatisticsRegistry()->registerStat(&d_numTermsLowered); + smtStatisticsRegistry()->registerStat(&d_numTermsForcedLowered); + } } BoolToBV::Statistics::~Statistics() { - smtStatisticsRegistry()->unregisterStat(&d_numTermsLowered); - smtStatisticsRegistry()->unregisterStat(&d_numAtomsLowered); - smtStatisticsRegistry()->unregisterStat(&d_numTermsForcedLowered); + smtStatisticsRegistry()->unregisterStat(&d_numIteToBvite); + if (options::boolToBitvector() == BOOL_TO_BV_ALL) + { + smtStatisticsRegistry()->unregisterStat(&d_numTermsLowered); + smtStatisticsRegistry()->unregisterStat(&d_numTermsForcedLowered); + } } diff --git a/src/preprocessing/passes/bool_to_bv.h b/src/preprocessing/passes/bool_to_bv.h index 49c9dc944..da99d3c84 100644 --- a/src/preprocessing/passes/bool_to_bv.h +++ b/src/preprocessing/passes/bool_to_bv.h @@ -2,14 +2,14 @@ /*! \file bool_to_bv.h ** \verbatim ** Top contributors (to current version): - ** Yoni Zohar + ** Makai Mann, Yoni Zohar ** This file is part of the CVC4 project. ** Copyright (c) 2009-2018 by the authors listed in the file AUTHORS ** in the top-level source directory) and their institutional affiliations. ** All rights reserved. See the file COPYING in the top-level source ** directory for licensing information.\endverbatim ** - ** \brief The BoolToBv preprocessing pass + ** \brief The BoolToBV preprocessing pass ** **/ @@ -18,9 +18,9 @@ #ifndef __CVC4__PREPROCESSING__PASSES__BOOL_TO_BV_H #define __CVC4__PREPROCESSING__PASSES__BOOL_TO_BV_H -#include "preprocessing/passes/bv_to_bool.h" #include "preprocessing/preprocessing_pass.h" #include "preprocessing/preprocessing_pass_context.h" +#include "theory/bv/theory_bv_utils.h" #include "util/statistics_registry.h" namespace CVC4 { @@ -39,24 +39,33 @@ class BoolToBV : public PreprocessingPass private: struct Statistics { + IntStat d_numIteToBvite; IntStat d_numTermsLowered; - IntStat d_numAtomsLowered; IntStat d_numTermsForcedLowered; Statistics(); ~Statistics(); }; - void lowerBoolToBv(const std::vector& assertions, - std::vector& new_assertions); - void addToLowerCache(TNode term, Node new_term); - Node getLowerCache(TNode term) const; - bool hasLowerCache(TNode term) const; - Node lowerNode(TNode current, bool topLevel = false); - NodeNodeMap d_lowerCache; - Node d_one; - Node d_zero; + /* Takes an assertion and tries to create more bit-vector structure */ + Node lowerAssertion(const TNode& a); + + /* Tries to lower one node to a width-one bit-vector */ + void lowerNode(const TNode& n); + + /* Returns cached node if it exists, otherwise returns the node */ + Node fromCache(TNode n) const; + + /** Checks if any of the nodes children were rebuilt, + * in which case n needs to be rebuilt as well + */ + bool needToRebuild(TNode n) const; + Statistics d_statistics; -}; // class + + /* Keeps track of lowered nodes */ + std::unordered_map d_lowerCache; +}; // class BoolToBV + } // namespace passes } // namespace preprocessing } // namespace CVC4 diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index 6814ad531..7abfd8273 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -1379,17 +1379,17 @@ void SmtEngine::setDefaults() { options::bitvectorToBool.set(false); } - if (options::boolToBitvector()) + if (options::boolToBitvector() != preprocessing::passes::BOOL_TO_BV_OFF) { if (options::boolToBitvector.wasSetByUser()) { throw OptionException( - "bool-to-bv not supported with unsat cores/proofs"); + "bool-to-bv != off not supported with unsat cores/proofs"); } - Notice() << "SmtEngine: turning off bool-to-bitvector to support unsat " + Notice() << "SmtEngine: turning off bool-to-bv to support unsat " "cores/proofs" << endl; - options::boolToBitvector.set(false); + setOption("boolToBitvector", SExpr("off")); } if (options::bvIntroducePow2()) @@ -1449,13 +1449,18 @@ void SmtEngine::setDefaults() { if (options::cbqiBv() && d_logic.isQuantified()) { - if(options::boolToBitvector.wasSetByUser()) { - throw OptionException( - "bool-to-bv not supported with CBQI BV for quantified logics"); + if (options::boolToBitvector() != preprocessing::passes::BOOL_TO_BV_OFF) + { + if (options::boolToBitvector.wasSetByUser()) + { + throw OptionException( + "bool-to-bv != off not supported with CBQI BV for quantified " + "logics"); + } + Notice() << "SmtEngine: turning off bool-to-bitvector to support CBQI BV" + << endl; + setOption("boolToBitvector", SExpr("off")); } - Notice() << "SmtEngine: turning off bool-to-bitvector to support CBQI BV" - << endl; - options::boolToBitvector.set(false); } // cases where we need produce models @@ -1617,6 +1622,19 @@ void SmtEngine::setDefaults() { } } + if (options::boolToBitvector() == preprocessing::passes::BOOL_TO_BV_ALL + && !d_logic.isTheoryEnabled(THEORY_BV)) + { + if (options::boolToBitvector.wasSetByUser()) + { + throw OptionException( + "bool-to-bv=all not supported for non-bitvector logics."); + } + Notice() << "SmtEngine: turning off bool-to-bv for non-bv logic: " + << d_logic.getLogicString() << std::endl; + setOption("boolToBitvector", SExpr("off")); + } + if (! options::bvEagerExplanations.wasSetByUser() && d_logic.isTheoryEnabled(THEORY_ARRAYS) && d_logic.isTheoryEnabled(THEORY_BV)) { diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp index e60d60456..949a3d738 100644 --- a/src/theory/bv/theory_bv.cpp +++ b/src/theory/bv/theory_bv.cpp @@ -740,7 +740,7 @@ Node TheoryBV::ppRewrite(TNode t) { Debug("bv-pp-rewrite") << "TheoryBV::ppRewrite " << t << "\n"; Node res = t; - if (RewriteRule::applies(t)) { + if (options::bitwiseEq() && RewriteRule::applies(t)) { Node result = RewriteRule::run(t); res = Rewriter::rewrite(result); } else if (d_isCoreTheory && t.getKind() == kind::EQUAL) { diff --git a/test/regress/CMakeLists.txt b/test/regress/CMakeLists.txt index f22796929..9e942aae1 100644 --- a/test/regress/CMakeLists.txt +++ b/test/regress/CMakeLists.txt @@ -159,7 +159,8 @@ set(regress_0_tests regress0/bv/ackermann2.smt2 regress0/bv/ackermann3.smt2 regress0/bv/ackermann4.smt2 - regress0/bv/bool-to-bv.smt2 + regress0/bv/bool-to-bv-all.smt2 + regress0/bv/bool-to-bv-ite.smt2 regress0/bv/bug260a.smt regress0/bv/bug260b.smt regress0/bv/bug440.smt diff --git a/test/regress/regress0/bv/bool-to-bv-all.smt2 b/test/regress/regress0/bv/bool-to-bv-all.smt2 new file mode 100644 index 000000000..5947699d9 --- /dev/null +++ b/test/regress/regress0/bv/bool-to-bv-all.smt2 @@ -0,0 +1,19 @@ +; COMMAND-LINE: --bool-to-bv=all +; EXPECT: sat +(set-logic QF_BV) +(declare-fun x2 () (_ BitVec 3)) +(declare-fun x1 () (_ BitVec 3)) +(declare-fun x0 () (_ BitVec 3)) +(declare-fun b1 () Bool) +(declare-fun b2 () Bool) +(declare-fun b3 () Bool) +(assert (not (bvult (bvudiv (bvudiv (bvudiv x0 x0) x1) x2) x1))) +(assert (not (bvslt (bvudiv (bvudiv (bvudiv x0 x0) x1) x2) x1))) +(assert (= #b000 x2)) +(assert (=> b1 b2)) +(assert (and b1 b2)) +(assert (or b1 b2)) +(assert (xor b1 b3)) +(assert (not (xor b2 b2))) +(assert (ite b2 b2 b1)) +(check-sat) diff --git a/test/regress/regress0/bv/bool-to-bv-ite.smt2 b/test/regress/regress0/bv/bool-to-bv-ite.smt2 new file mode 100644 index 000000000..e1be3ea10 --- /dev/null +++ b/test/regress/regress0/bv/bool-to-bv-ite.smt2 @@ -0,0 +1,13 @@ +; COMMAND-LINE: --bool-to-bv=ite +; EXPECT: sat +(set-logic QF_BV) +(declare-fun x2 () (_ BitVec 3)) +(declare-fun x1 () (_ BitVec 3)) +(declare-fun x0 () (_ BitVec 3)) +(declare-fun b1 () Bool) +(declare-fun b2 () Bool) +(assert (not (bvult (bvudiv (bvudiv (bvudiv x0 x0) x1) x2) x1))) +(assert (= #b000 x2)) +(assert (=> b1 b2)) +(assert (= x2 (ite (bvugt x0 x1) (bvadd x0 (_ bv1 3)) (bvadd x1 (_ bv1 3))))) +(check-sat) diff --git a/test/regress/regress0/bv/bool-to-bv.smt2 b/test/regress/regress0/bv/bool-to-bv.smt2 deleted file mode 100644 index 8706c51a8..000000000 --- a/test/regress/regress0/bv/bool-to-bv.smt2 +++ /dev/null @@ -1,19 +0,0 @@ -; COMMAND-LINE: --bool-to-bv -; EXPECT: sat -(set-logic QF_BV) -(declare-fun x2 () (_ BitVec 3)) -(declare-fun x1 () (_ BitVec 3)) -(declare-fun x0 () (_ BitVec 3)) -(declare-fun b1 () Bool) -(declare-fun b2 () Bool) -(declare-fun b3 () Bool) -(assert (not (bvult (bvudiv (bvudiv (bvudiv x0 x0) x1) x2) x1))) -(assert (not (bvslt (bvudiv (bvudiv (bvudiv x0 x0) x1) x2) x1))) -(assert (= #b000 x2)) -(assert (=> b1 b2)) -(assert (and b1 b2)) -(assert (or b1 b2)) -(assert (xor b1 b3)) -(assert (not (xor b2 b2))) -(assert (ite b2 b2 b1)) -(check-sat) -- cgit v1.2.3 From 1c114dc487d94d72ebf3453611c42b28777d6482 Mon Sep 17 00:00:00 2001 From: Alex Ozdemir Date: Tue, 11 Dec 2018 11:46:38 -0800 Subject: LRAT signature (#2731) * LRAT signature Added an LRAT signature. It is almost entirely side-conditions, but it works. There is also a collection of tests for it. You can run them by invoking ``` lfscc smt.plf sat.plf lrat.plf lrat_test.plf ``` * Update proofs/signatures/lrat.plf per Yoni's suggestion. Co-Authored-By: alex-ozdemir * Responding to Yoni's comments. * Removed unused varaibles Some tests declared `var`s which were unused. Now they don't. --- proofs/signatures/lrat.plf | 566 +++++++++++++++++++++++++++++ proofs/signatures/lrat_test.plf | 786 ++++++++++++++++++++++++++++++++++++++++ proofs/signatures/sat.plf | 6 +- proofs/signatures/smt.plf | 16 + proofs/signatures/th_bv.plf | 14 - 5 files changed, 1371 insertions(+), 17 deletions(-) create mode 100644 proofs/signatures/lrat.plf create mode 100644 proofs/signatures/lrat_test.plf diff --git a/proofs/signatures/lrat.plf b/proofs/signatures/lrat.plf new file mode 100644 index 000000000..f5af891bc --- /dev/null +++ b/proofs/signatures/lrat.plf @@ -0,0 +1,566 @@ +; LRAT Proof signature +; LRAT format detailed in "Efficient Certified RAT Verification" +; Author: aozdemir +; Depends On: sat.plf, smt.plf + + +; A general note about the design of the side conditions: +; Some side-conditions make use of a _global assignment_ encoded in +; 0 (true) / 1 (false) marks on variables. + +; A list of clauses, CNF if interpretted as a formula, +; but also sometimes just a list +(declare cnf type) +(declare cnfn cnf) +(declare cnfc (! h clause (! t cnf cnf))) + +; Unit (https://en.wikipedia.org/wiki/Unit_type) +; For functions that don't return anything +(declare Unit type) ; The type with only one value (like `void` in C) +(declare unit Unit) ; That value + +; Boolean operator (not short-circuiting) +(program bool_or ((l bool) (r bool)) bool (match l (ff r) (tt tt))) +(program bool_and ((l bool) (r bool)) bool (match l (tt r) (ff ff))) +(program bool_not ((b bool)) bool (match b (tt ff) (ff tt))) + +; =================== ; +; Working CNF formula ; +; =================== ; + +; Represents a CNF formula as a map from clause indices to clauses +; Should be sorted ascending, always! +; Here, and for all collections, the suffix "n" denotes the empty collection and +; the suffix "c" denotes the constructor for the collection in the style of lisp's +; "cons cells" +(declare CMap type) +(declare CMapn CMap) +(declare CMapc (! i mpz (! c clause (! r CMap CMap)))) + +; ================= ; +; LRAT Proof Format ; +; ================= ; + +; CI lists are lists of clause indices. +; They represent clauses to delete. +; They must be sorted. +(declare CIList type) +(declare CIListn CIList) +(declare CIListc (! z mpz (! zs CIList CIList))) + +; Traces are a list of clause indices into the working CNF formula +; They represent the clauses that will be unit in a unit propegation to bottom +; Thus their elements are *not* in value order. +(declare Trace type) +(declare Tracen Trace) +(declare Tracec (! z mpz (! zs Trace Trace))) + +; RAT Hint list +; Each hint is +; * An index indicating a clause in the working CNF formula to resolve with +; * A trace indicating how UP should be done after that resolution +(declare RATHints type) +(declare RATHintsn RATHints) +(declare RATHintsc + (! target mpz + (! trace Trace + (! rest RATHints + RATHints)))) + +; LRAT proof +(declare LRATProof type) +(declare LRATProofn LRATProof) +; Deletion (includes a list of clause indices to delete) +(declare LRATProofd (! cis CIList (! rest LRATProof LRATProof))) +; Addition: a clause index, a clause, RUP trace for that clause, and hints for +; what resolutions should happen then, and how those resolutions imply bottom +; via UP. +; If the list of hints is empty, then bottom is already implied. +(declare LRATProofa + (! ci mpz + (! c clause + (! t Trace + (! h RATHints + (! rest LRATProof + LRATProof)))))) + +; ========================================== ; +; Functional programs for manipulating types ; +; ========================================== ; + +; Flip the polarity of the literal +(program lit_flip ((l lit)) lit + (match l + ((pos v) (neg v)) + ((neg v) (pos v)))) + +; Are two literal equal? +(program lit_eq ((l1 lit) (l2 lit)) bool + (match l1 + ((pos v1) (match l2 + ((pos v2) (ifequal v1 v2 tt ff)) + ((neg v2) ff))) + ((neg v1) (match l2 + ((pos v2) ff) + ((neg v2) (ifequal v1 v2 tt ff)))))) + +; Remove **all** occurences of a literal from clause +(program clause_remove_all ((l lit) (c clause)) clause + (match c + (cln cln) + ((clc l' c') + (let rest_res (clause_remove_all l c') + (match (lit_eq l l') + (tt rest_res) + (ff (clc l' rest_res))))))) + +; Return the clause's first literal +; fails on an empty clause +(program clause_head ((c clause)) lit + (match c + (cln (fail lit)) + ((clc l c') l))) + +; Does a clause contain some literal? +(program clause_contains_lit ((c clause) (l lit)) bool + (match c + ((clc l' c') (match (lit_eq l l') + (tt tt) + (ff (clause_contains_lit c' l)))) + (cln ff))) + +; Append two traces +(program Trace_concat ((t1 Trace) (t2 Trace)) Trace + (match t1 + (Tracen t2) + ((Tracec h1 r1) (Tracec h1 (Trace_concat r1 t2))))) + +; Return whether a list of RAT hits is empty +(program RATHints_is_empty ((h RATHints)) bool + (match h + (RATHintsn tt) + ((RATHintsc a b c) ff))) + +; Insert into a CMap, preserving order +(program CMap_insert ((i mpz) (c clause) (cs CMap)) CMap + (match cs + (CMapn (CMapc i c CMapn)) + ((CMapc i' c' r) + (mp_ifneg (mpz_sub i i') + (CMapc i c cs) + (CMapc i' c' (CMap_insert i c r)))))) + +; Get from a CMap +(program CMap_get ((i mpz) (cs CMap)) clause + (match cs + (CMapn (fail clause)) + ((CMapc i' c r) + (mp_ifzero (mpz_sub i i') + c + (CMap_get i r))))) + +; Remove from CMap. Only removes one element. +(program CMap_remove ((i mpz) (cs CMap)) CMap + (match cs + (CMapn CMapn) + ((CMapc i' c r) + (mp_ifzero (mpz_sub i i') + r + (CMapc i' c (CMap_remove i r)))))) + +; Remove many indices from a CMap. Asuumes the input list is sorted. +(program CMap_remove_many ((is CIList) (cs CMap)) CMap + (match + is + (CIListn cs) + ((CIListc i is') + (match + cs + (CMapn (fail CMap)) ; All deletion indices must be valid! + ((CMapc ci c cs') + (mp_ifzero (mpz_sub i ci) + (CMap_remove_many is' cs') + (CMapc ci c (CMap_remove_many is cs')))))))) + +; Given a map of clauses and a literal, return all indices in the map +; corresponsing to clauses that could resolve against that literal. i.e. for x, +; return the indices of all clauses containing x. +(program collect_resolution_targets_w_lit ((cs CMap) (l lit)) CIList + (match cs + (CMapn CIListn) + ((CMapc i c cs') + (let rest_solution (collect_resolution_targets_w_lit cs' l) + (match (clause_contains_lit c l) + (tt (CIListc i rest_solution)) + (ff rest_solution)))))) + +; Given a clause and a maps of clauses, return all indices in the map +; corresponding to clauses which could resolve with this one on its first +; literal +(program collect_resolution_targets ((cs CMap) (c clause)) CIList + (collect_resolution_targets_w_lit cs (lit_flip (clause_head c)))) + +; Is this clause a tautology? +; Internally uses mark 5 to flag variables that occur (+) +; and mark 6 to flag variables that occur (-) +(program is_t ((c clause)) bool + (match + c + (cln ff) + ((clc l c') (match + l + ((pos v) + (ifmarked5 + v + (is_t c') + (ifmarked6 + v + tt + (do + (markvar5 v) + (let r (is_t c') (do (markvar5 v) r)))))) + ((neg v) + (ifmarked6 + v + (is_t c') + (ifmarked5 + v + tt + (do + (markvar6 v) + (let r (is_t c') (do (markvar6 v) r)))))))))) + +; ===================================================================== ; +; Programs for manipulating and querying the global variable assignment ; +; ===================================================================== ; + +; This assignment marks values of type `var`. +; It marks a variable with 1 if that variable is true +; It marks a variable with 2 if that variable is false +; A variable should not be marked with both! +; A variable may be marked with neither, indicating that variable is presently +; unassigned, which we call "floating". + +; Mark the variable within to satisfy this literal. +; fails if the literal is already UNSAT +(program lit_mk_sat ((l lit)) Unit + (match l + ((pos v) (ifmarked2 v + (fail Unit) + (ifmarked1 v unit (do (markvar1 v) unit)))) + ((neg v) (ifmarked1 v + (fail Unit) + (ifmarked2 v unit (do (markvar2 v) unit)))))) + +; Mark the variable within to falsify this literal. +; fails is the literal is already SAT +(program lit_mk_unsat ((l lit)) Unit + (match l + ((neg v) (ifmarked2 v + (fail Unit) + (ifmarked1 v unit (do (markvar1 v) unit)))) + ((pos v) (ifmarked1 v + (fail Unit) + (ifmarked2 v unit (do (markvar2 v) unit)))))) + +; Unmarks the variable within a satified literal to render it neither satified nor falsified +; fails if the literal is not already satisfied +(program lit_un_mk_sat ((l lit)) Unit + (match l + ((pos v) (ifmarked1 v (do (markvar1 v) unit) (fail Unit))) + ((neg v) (ifmarked2 v (do (markvar2 v) unit) (fail Unit))))) + +; Unmarks the variable within a falsified literal to render it neither satified nor falsified +; fails if the literal is not already falsified +(program lit_un_mk_unsat ((l lit)) Unit + (match l + ((pos v) (ifmarked2 v (do (markvar2 v) unit) (fail Unit))) + ((neg v) (ifmarked1 v (do (markvar1 v) unit) (fail Unit))))) + +; Is a literal presently satisfied? +(program lit_is_sat ((l lit)) bool + (match l + ((pos v) (ifmarked1 v tt ff)) + ((neg v) (ifmarked2 v tt ff)))) + +; Is a literal presently falsified? +(program lit_is_unsat ((l lit)) bool + (match l + ((pos v) (ifmarked2 v tt ff)) + ((neg v) (ifmarked1 v tt ff)))) + +; Is a literal presently neither satisfied nor falsified? +(program lit_is_floating ((l lit)) bool + (bool_not (bool_or (lit_is_sat l) (lit_is_unsat l)))) + +; Does this clause contain a floating literal? +(program clause_has_floating ((c clause)) bool + (match c + (cln ff) + ((clc l c') (match (lit_is_floating l) + (tt tt) + (ff (clause_has_floating c')))))) + +; Is this clause falsified? i.e. are all its clauses falsified? +(program clause_is_unsat ((c clause)) bool + (match c + (cln tt) + ((clc l c') (match (lit_is_unsat l) + (tt (clause_is_unsat c')) + (ff ff))))) + +; Is this clause presently satisfied? +(program clause_is_sat ((c clause)) bool + (match c + (cln ff) + ((clc l c') (match (lit_is_sat l) + (tt tt) + (ff (clause_is_sat c')))))) + +; Falsify **all** contained literals. +; Fails on a tautological clause +(program clause_mk_all_unsat ((c clause)) Unit + (match c + (cln unit) + ((clc l c') (do + (lit_mk_unsat l) + (clause_mk_all_unsat c'))))) + +; Unfalsifies **all** contained literals +; Fails on a clause with duplicate literals +(program clause_un_mk_all_unsat ((c clause)) Unit + (match c + (cln unit) + ((clc l c') (do + (lit_un_mk_unsat l) + (clause_un_mk_all_unsat c'))))) + +; Get the first floating literal out of this clause. +; fails if there are no floating literals +(program clause_first_floating ((c clause)) lit + (match c + (cln (fail lit)) + ((clc l c') (match (lit_is_floating l) + (tt l) + (ff (clause_first_floating c')))))) + +; ===================================== ; +; High-Level Programs for LRAT Checking ; +; ===================================== ; + +; The return type for verifying that a clause is unit and modifying the global +; assignment to satisfy it +(declare MarkResult type) +; The clause is unit, and this is the (previoiusly floating) literal that is now satified. +(declare MRUnit (! l lit MarkResult)) +; The clause was unsat! +(declare MRUnsat MarkResult) +; The clauss was already satisfied. +(declare MRSat MarkResult) +; The clause had multiple floating literals. +(declare MRNotUnit MarkResult) + +; Determine wether this clause is sat, unsat, unit, or not unit, and if it is +; unit, it modifies the global assignment to satisfy the clause, and returns +; the literal that was made SAT by the new mark. +; +; Fails if `c` is a TAUT +(program clause_check_unit_and_maybe_mark ((c clause)) MarkResult + (match (clause_is_sat c) + (tt MRSat) + (ff (match (clause_is_unsat c) + (tt MRUnsat) + (ff (match (is_t c) + (tt (fail MarkResult)) + (ff ; Dedent + (match (clause_has_floating c) + (tt (let first (clause_first_floating c) + (do (lit_mk_sat first) + (match (clause_has_floating c) + (tt (do (lit_un_mk_sat first) MRNotUnit)) + (ff (MRUnit first)))))) + ; Unreachable. If clause is not floating it must have been SAT or UNSAT. + (ff (fail MarkResult)) + )))))))) + +; The return type for the process of Trace-guided unit propegation +(declare UPResult type) +; The trace guided unit propegation correctly, but that unit propegation did not end in an empty clause +(declare UPR_Ok UPResult) +; The trace guided unit propegation correctly to an empty clause +(declare UPR_Bottom UPResult) +; The trace was malformed, +;; i.e. at some point indicates that a non-unit, non-empty clause should be examined +(declare UPR_Broken UPResult) + +; Execute the unit propegation indicated by the trace. Report whether that +; unit propegation succeeds and produces bottom, fails, or succeeds but does +; not produce bottom. +; +; If the trace tries to propegate through a TAUT clause, fails. +(program do_up ((cs CMap) (t Trace)) UPResult + (match + t + (Tracen UPR_Ok) + ((Tracec i r) (match (clause_check_unit_and_maybe_mark (CMap_get i cs)) + ((MRUnit l) + (let res (do_up cs r) + (do (lit_un_mk_sat l) res))) + (MRUnsat UPR_Bottom) + (MRSat UPR_Broken) + (MRNotUnit UPR_Broken))))) + + +; Determine whether a list of indices agrees with the list of indices latent in +; a list of hints. Both lists should be sorted. +(program resolution_targets_match ( + (computed CIList) + (given RATHints)) bool + (match given + (RATHintsn + (match computed + (CIListn tt) + ((CIListc a b) ff))) + ((RATHintsc hint_idx t given') + (match computed + ((CIListc comp_idx computed') + (mp_ifzero (mpz_sub hint_idx comp_idx) + (resolution_targets_match computed' given') + (ff))) + (CIListn ff))))) + + +; Determines whether `t` is a witness that `c` is an Assymetric Tautology in `cs`. +; +; Does unit propegation in the formula `cs`, beginning by falsifying +; all literals in `c`, and then looking at the clauses indicated by `t`. +; Assumes no marks, and cleans up marks afterwards. +; +; Fails if `c` has duplicates +(program is_at_trace ((cs CMap) (c clause) (t Trace)) UPResult + (match (is_t c) + (ff + (do + (clause_mk_all_unsat c) + (let result (do_up cs t) + (do (clause_un_mk_all_unsat c) result)))) + (tt + UPR_Bottom))) + + + +; List of (clause, trace) pairs +(declare CTPairs type) +(declare CTPn CTPairs) +(declare CTPc (! c clause (! t Trace (! rest CTPairs CTPairs)))) + +; For each RAT hint, construct the pseudo-resolvant for that hint, and the net +; trace for that hint. Return a list of these. +; +; Pseudo resolvant: if l v C is the clause, and D is another clause containing +; ~l, then l v C v (D \ ~l) is the pseudo-resolvant, which is the actual +; resolant, plut l, which would be implied by UP. +; +; The net trace is the global trace (`t`), plut the trace for that specific +; resolvant. +(program construct_ct_pairs ( + (cs CMap) + (c clause) + (t Trace) + (hints RATHints) + ) CTPairs + (match hints + (RATHintsn CTPn) + ((RATHintsc i ht hints') + (CTPc + (clause_append c + (clause_remove_all (lit_flip (clause_head c)) + (CMap_get i cs))) + (Trace_concat t ht) + (construct_ct_pairs cs c t hints'))))) + +; Goes through a list of clause, trace pairs and verifies that each clause is +; an AT via that trace. +; Fails if any putative AT is a TAUT or contains duplicates +(program are_all_at ( + (cs CMap) + (l CTPairs) + ) UPResult + (match l + (CTPn UPR_Bottom) + ((CTPc c t l') + (match (is_at_trace cs c t) + (UPR_Ok UPR_Ok) + (UPR_Broken UPR_Broken) + (UPR_Bottom (are_all_at cs l')))))) + +; Is this trace, and list of hints, proof that `c` is an Resolution Assymeytic +; Tautology? +; Fails is the hints are empty (i.e. `c` should be AT, and c is TAUT or contains duplicates) +; Also fails if any of the pseudo-resolvants are TAUT or contain duplicates. +(program is_rat_trace ((cs CMap) (c clause) (t Trace) (hints RATHints)) UPResult + (match + (RATHints_is_empty hints) + (tt ; Empty RAT hints -- the clause must be AT + (is_at_trace cs c t)) + (ff ; Ew -- we must verify this is a RAT + (match (resolution_targets_match + (collect_resolution_targets cs c) + hints) + (ff ; Res targets are bad + UPR_Broken) + (tt + (are_all_at cs (construct_ct_pairs cs c t hints))))))) + +; Is this proof an LRAT proof of bottom? +; Fails if any added AT is a TAUT or contains duplicates OR if any added RAT +; produces pseudo-resolvants which are TAUT or contain duplicates +(program is_lrat_proof_of_bottom ((f CMap) (proof LRATProof)) bool + (match proof + ((LRATProofd indices rest) + (is_lrat_proof_of_bottom + (CMap_remove_many indices f) + rest)) + ((LRATProofa idx c trace hints rest) + (match (is_rat_trace f c trace hints) + (UPR_Bottom + (match + c + (cln tt) + ((clc a b) + (is_lrat_proof_of_bottom (CMap_insert idx c f) rest)))) + (UPR_Ok ff) + (UPR_Broken ff))) + (LRATProofn ff)) + ) + +; Proof of a CMap from clause proofs. +; The idx is unelidable b/c it is unspecified. +(declare CMap_holds (! c CMap type)) +(declare CMapn_proof (CMap_holds CMapn)) +(declare CMapc_proof + (! idx mpz ; Not elidable! + (! c clause + (! rest CMap + (! proof_c (holds c) + ( ! proof_rest (CMap_holds rest) + (CMap_holds (CMapc idx c rest)))))))) + +(define bottom (holds cln)) +(declare lrat_proof_of_bottom + (! cm CMap + (! proof_cm (CMap_holds cm) + (! proof LRATProof + (! sc (^ (is_lrat_proof_of_bottom cm proof) tt) + bottom))))) + + +; TODO(aozdemir) Reducing the amount of checking that resides in side-conditions. +; Steps +; 1. Unroll the traversal of is_lrat_proof_of_bottom into a serialized +; sequence of axiom applications. +; The axioms would likely correspond to DELETE, IS T, IS AT, IS RAT. +; They would manipulate a CMap by way of side-conditions. +; 2. Unroll AT checks by manifesting the assignment in data rather than marks, +; and having axioms like IS_UNSAT, IS_UNIT_ON_LITERAL. +; 3. Unroll RAT checks in a similar fashion, although more painfully. diff --git a/proofs/signatures/lrat_test.plf b/proofs/signatures/lrat_test.plf new file mode 100644 index 000000000..3ba785507 --- /dev/null +++ b/proofs/signatures/lrat_test.plf @@ -0,0 +1,786 @@ +(declare test_clause_append + (! c1 clause + (! c2 clause + (! cr clause + (! sc (^ (clause_append c1 c2) cr) type))))) + +; Test passes if the (test_clause_append ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (test_clause_append + (clc (pos v1) (clc (neg v2) cln)) + (clc (pos v3) (clc (neg v2) cln)) + (clc (pos v1) (clc (neg v2) (clc (pos v3) (clc (neg v2) cln)))) + ) + ))) +) + +; Test passes if the (test_clause_append ...) application is well-typed. +(check + (% v2 var + (% v3 var + (test_clause_append + cln + (clc (pos v3) (clc (neg v2) cln)) + (clc (pos v3) (clc (neg v2) cln)) + ) + )) +) + +; Test passes if the (test_clause_append ...) application is well-typed. +(check + (% v2 var + (% v3 var + (test_clause_append + (clc (pos v3) (clc (neg v2) cln)) + cln + (clc (pos v3) (clc (neg v2) cln)) + ) + )) +) + +(declare test_CMap_remove_many + (! is CIList + (! cs CMap + (! csr CMap + (! sc (^ (CMap_remove_many is cs) csr) type))))) + +; Test passes if the (test_CMap_remove_many ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (@ c1 (clc (pos v4) (clc (neg v2) cln)) + (@ c2 (clc (neg v3) (clc (neg v1) cln)) + (@ c3 (clc (neg v2) (clc (pos v3) cln)) + (@ c4 (clc (neg v3) (clc (neg v4) cln)) + (@ cs_in (CMapc 0 c1 (CMapc 3 c2 (CMapc 4 c3 (CMapc 5 c3 (CMapc 6 c4 CMapn))))) + (@ cs_out (CMapc 3 c2 (CMapc 5 c3 CMapn)) + (@ is_in (CIListc 0 (CIListc 4 (CIListc 6 CIListn))) + (test_CMap_remove_many + is_in + cs_in + cs_out + ) + ))) + )))) + )))) +) + +; Test passes if the (test_CMap_remove_many ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (@ c1 (clc (pos v4) (clc (neg v2) cln)) + (@ c2 (clc (neg v3) (clc (neg v1) cln)) + (@ c3 (clc (neg v2) (clc (pos v3) cln)) + (@ c4 (clc (neg v3) (clc (neg v4) cln)) + (@ cs_in (CMapc 0 c1 (CMapc 3 c2 (CMapc 4 c3 (CMapc 5 c3 (CMapc 6 c4 CMapn))))) + (@ cs_out (CMapc 0 c1 (CMapc 3 c2 (CMapc 5 c3 (CMapc 6 c4 CMapn)))) + (@ is_in (CIListc 4 CIListn) + (test_CMap_remove_many + is_in + cs_in + cs_out + ) + ))) + )))) + )))) +) + +(declare test_clause_remove_all + (! l lit + (! c clause + (! c' clause + (! sc (^ (clause_remove_all l c) c') type))))) + +; Test passes if the (test_clause_remove_all ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (@ c1 (clc (pos v4) (clc (neg v2) (clc (neg v2) (clc (pos v2) (clc (pos v1) cln))))) + (@ c2 (clc (pos v4) (clc (pos v2) (clc (pos v1) cln))) + (test_clause_remove_all + (neg v2) + c1 + c2 + ) + )) + )))) +) + +(declare test_collect_resolution_targets + (! cs CMap + (! c clause + (! is CIList + (! sc (^ (collect_resolution_targets cs c) is) type))))) + +; Test passes if the (test_collect_resolution_targets ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (@ c1 (clc (pos v3) (clc (neg v2) cln)) + (@ c2 (clc (neg v3) (clc (neg v1) cln)) + (@ c3 (clc (neg v2) (clc (pos v3) cln)) + (@ c4 (clc (neg v3) (clc (pos v3) cln)) + (@ ct (clc (neg v3) (clc (neg v4) cln)) + (@ cs_in (CMapc 0 c1 (CMapc 3 c2 (CMapc 4 c3 (CMapc 5 c3 (CMapc 6 c4 CMapn))))) + (@ is_out (CIListc 0 (CIListc 4 (CIListc 5 (CIListc 6 CIListn)))) + (test_collect_resolution_targets + cs_in + ct + is_out + ) + ))) + )))) + )))) +) + +(declare test_resolution_targets_match + (! c CIList + (! g RATHints + (! ans bool + (! sc (^ (resolution_targets_match c g) ans) type))))) + +; Test passes if the (test_resolution_targets_match ...) application is well-typed. +(check + (@ idxs_in (CIListc 0 (CIListc 4 (CIListc 5 (CIListc 6 CIListn)))) + (@ hints_in + (RATHintsc 0 Tracen + (RATHintsc 4 Tracen + (RATHintsc 5 Tracen + (RATHintsc 6 Tracen + RATHintsn)))) + (test_resolution_targets_match + idxs_in + hints_in + tt + ) + )) +) + +; Test passes if the (test_resolution_targets_match ...) application is well-typed. +(check + (@ idxs_in (CIListc 0 (CIListc 2 (CIListc 5 (CIListc 6 CIListn)))) + (@ hints_in + (RATHintsc 0 Tracen + (RATHintsc 4 Tracen + (RATHintsc 5 Tracen + (RATHintsc 6 Tracen + RATHintsn)))) + (test_resolution_targets_match + idxs_in + hints_in + ff + ) + )) +) + +(declare test_is_at_trace + (! cs CMap + (! c clause + (! t Trace + (! r UPResult + (! sc (^ (is_at_trace cs c t) r) type)))))) + +; Test passes if the (test_is_at_trace ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (@ c1 (clc (pos v3) (clc (neg v2) cln)) + (@ c2 (clc (neg v3) (clc (neg v1) cln)) + (@ c3 (clc (neg v2) (clc (pos v1) cln)) + (@ c4 (clc (neg v3) (clc (pos v2) cln)) + (@ cs (CMapc 0 c1 (CMapc 3 c2 (CMapc 5 c3 (CMapc 6 c4 CMapn)))) + (@ c (clc (neg v3) cln) + (@ t (Tracec 3 (Tracec 5 (Tracec 6 Tracen))) + (test_is_at_trace + cs + c + t + UPR_Bottom + ) + ))) + )))) + )))) +) + +; Test passes if the (test_is_at_trace ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (@ c1 (clc (pos v3) (clc (neg v2) cln)) + (@ c2 (clc (neg v3) (clc (neg v1) cln)) + (@ c3 (clc (neg v2) (clc (pos v1) cln)) + (@ c4 (clc (neg v3) (clc (pos v2) cln)) + (@ cs (CMapc 0 c1 (CMapc 3 c2 (CMapc 5 c3 (CMapc 6 c4 CMapn)))) + (@ c (clc (neg v3) cln) + (@ t (Tracec 3 (Tracec 5 Tracen)) + (test_is_at_trace + cs + c + t + UPR_Ok + ) + ))) + )))) + )))) +) + +; Test passes if the (test_is_at_trace ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (@ c1 (clc (pos v3) (clc (neg v2) cln)) + (@ c2 (clc (neg v3) (clc (neg v1) cln)) + (@ c3 (clc (neg v2) (clc (pos v1) cln)) + (@ c4 (clc (neg v3) (clc (pos v2) cln)) + (@ cs (CMapc 1 c1 (CMapc 2 c2 (CMapc 3 c3 (CMapc 4 c4 CMapn)))) + (@ c (clc (neg v3) cln) + (@ t (Tracec 2 (Tracec 1 Tracen)) + (test_is_at_trace + cs + c + t + UPR_Broken + ) + ))) + )))) + )))) +) + +(declare test_is_rat_trace (! cs CMap + (! c clause + (! t Trace + (! h RATHints + (! r UPResult + (! sc (^ (is_rat_trace cs c t h) r) type))))))) + +; Test passes if the (test_is_rat_trace ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (@ c1 (clc (pos v1) (clc (pos v2) (clc (neg v3) cln))) + (@ c2 (clc (neg v1) (clc (neg v2) (clc (pos v3) cln))) + (@ c3 (clc (pos v2) (clc (pos v3) (clc (neg v4) cln))) + (@ c4 (clc (neg v2) (clc (neg v3) (clc (pos v4) cln))) + (@ c5 (clc (neg v1) (clc (neg v3) (clc (neg v4) cln))) + (@ c6 (clc (pos v1) (clc (pos v3) (clc (pos v4) cln))) + (@ c7 (clc (neg v1) (clc (pos v2) (clc (pos v4) cln))) + (@ c8 (clc (pos v1) (clc (neg v2) (clc (neg v4) cln))) + (@ cs (CMapc 1 c1 + (CMapc 2 c2 + (CMapc 3 c3 + (CMapc 4 c4 + (CMapc 5 c5 + (CMapc 6 c6 + (CMapc 7 c7 + (CMapc 8 c8 CMapn)))))))) + (@ c (clc (pos v1) cln) + (@ t Tracen + (@ h (RATHintsc 2 (Tracec 6 (Tracec 8 Tracen)) + (RATHintsc 5 (Tracec 1 (Tracec 8 Tracen)) + (RATHintsc 7 (Tracec 6 (Tracec 1 Tracen)) + RATHintsn))) + (test_is_rat_trace + cs + c + t + h + UPR_Bottom + ) + )))) + )))))))) + )))) +) + +; Test passes if the (test_is_rat_trace ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (@ c1 (clc (pos v1) (clc (pos v2) (clc (neg v3) cln))) + (@ c2 (clc (neg v1) (clc (neg v2) (clc (pos v3) cln))) + (@ c3 (clc (pos v2) (clc (pos v3) (clc (neg v4) cln))) + (@ c4 (clc (neg v2) (clc (neg v3) (clc (pos v4) cln))) + (@ c5 (clc (neg v1) (clc (neg v3) (clc (neg v4) cln))) + (@ c6 (clc (pos v1) (clc (pos v3) (clc (pos v4) cln))) + (@ c7 (clc (neg v1) (clc (pos v2) (clc (pos v4) cln))) + (@ c8 (clc (pos v1) (clc (neg v2) (clc (neg v4) cln))) + (@ cs (CMapc 1 c1 + (CMapc 2 c2 + (CMapc 3 c3 + (CMapc 4 c4 + (CMapc 5 c5 + (CMapc 6 c6 + (CMapc 7 c7 + (CMapc 8 c8 CMapn)))))))) + (@ c (clc (pos v1) cln) + (@ t Tracen + (@ h (RATHintsc 2 (Tracec 6 (Tracec 8 Tracen)) + (RATHintsc 5 (Tracec 1 (Tracec 8 Tracen)) + RATHintsn)) + (test_is_rat_trace + cs + c + t + h + UPR_Broken + ) + )))) + )))))))) + )))) +) + +; Test passes if the (test_is_rat_trace ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (@ c1 (clc (pos v1) (clc (pos v2) (clc (neg v3) cln))) + (@ c2 (clc (neg v1) (clc (neg v2) (clc (pos v3) cln))) + (@ c3 (clc (pos v2) (clc (pos v3) (clc (neg v4) cln))) + (@ c4 (clc (neg v2) (clc (neg v3) (clc (pos v4) cln))) + (@ c5 (clc (neg v1) (clc (neg v3) (clc (neg v4) cln))) + (@ c6 (clc (pos v1) (clc (pos v3) (clc (pos v4) cln))) + (@ c7 (clc (neg v1) (clc (pos v2) (clc (pos v4) cln))) + (@ c8 (clc (pos v1) (clc (neg v2) (clc (neg v4) cln))) + (@ cs (CMapc 1 c1 + (CMapc 2 c2 + (CMapc 3 c3 + (CMapc 4 c4 + (CMapc 5 c5 + (CMapc 6 c6 + (CMapc 7 c7 + (CMapc 8 c8 CMapn)))))))) + (@ c (clc (pos v1) cln) + (@ t Tracen + (@ h (RATHintsc 2 (Tracec 6 (Tracec 8 Tracen)) + (RATHintsc 5 (Tracec 1 (Tracec 8 Tracen)) + (RATHintsc 7 (Tracec 3 (Tracec 1 Tracen)) + RATHintsn))) + (test_is_rat_trace + cs + c + t + h + UPR_Broken + ) + )))) + )))))))) + )))) +) + +; Test passes if the (test_is_rat_trace ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (@ c1 (clc (pos v1) (clc (pos v2) (clc (neg v3) cln))) + (@ c2 (clc (neg v1) (clc (neg v2) (clc (pos v3) cln))) + (@ c3 (clc (pos v2) (clc (pos v3) (clc (neg v4) cln))) + (@ c4 (clc (neg v2) (clc (neg v3) (clc (pos v4) cln))) + (@ c5 (clc (neg v1) (clc (neg v3) (clc (neg v4) cln))) + (@ c6 (clc (pos v1) (clc (pos v3) (clc (pos v4) cln))) + (@ c7 (clc (neg v1) (clc (pos v2) (clc (pos v4) cln))) + (@ c8 (clc (pos v1) (clc (neg v2) (clc (neg v4) cln))) + (@ cs (CMapc 1 c1 + (CMapc 2 c2 + (CMapc 3 c3 + (CMapc 4 c4 + (CMapc 5 c5 + (CMapc 6 c6 + (CMapc 7 c7 + (CMapc 8 c8 CMapn)))))))) + (@ c (clc (pos v1) cln) + (@ t Tracen + (@ h (RATHintsc 2 (Tracec 6 (Tracec 8 Tracen)) + (RATHintsc 5 (Tracec 1 (Tracec 8 Tracen)) + (RATHintsc 7 (Tracec 3 Tracen) + RATHintsn))) + (test_is_rat_trace + cs + c + t + h + UPR_Broken + ) + )))) + )))))))) + )))) +) + +; Test passes if the (test_is_rat_trace ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (@ c1 (clc (pos v1) (clc (pos v2) (clc (neg v3) cln))) + (@ c2 (clc (neg v1) (clc (neg v2) (clc (pos v3) cln))) + (@ c3 (clc (pos v2) (clc (pos v3) (clc (neg v4) cln))) + (@ c4 (clc (neg v2) (clc (neg v3) (clc (pos v4) cln))) + (@ c5 (clc (neg v1) (clc (neg v3) (clc (neg v4) cln))) + (@ c6 (clc (pos v1) (clc (pos v3) (clc (pos v4) cln))) + (@ c7 (clc (neg v1) (clc (pos v2) (clc (pos v4) cln))) + (@ c8 (clc (pos v1) (clc (neg v2) (clc (neg v4) cln))) + (@ c9 (clc (pos v1) cln) + (@ cs (CMapc 1 c1 + (CMapc 2 c2 + (CMapc 3 c3 + (CMapc 4 c4 + (CMapc 5 c5 + (CMapc 6 c6 + (CMapc 7 c7 + (CMapc 8 c8 + (CMapc 9 c9 + CMapn))))))))) + (@ c (clc (pos v2) cln) + (@ t (Tracec 9 (Tracec 7 (Tracec 5 (Tracec 3 Tracen)))) + (@ h RATHintsn + (test_is_rat_trace + cs + c + t + h + UPR_Bottom + ) + )))) + ))))))))) + )))) +) + +; Test passes if the (test_is_rat_trace ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (@ c1 (clc (pos v1) (clc (pos v2) (clc (neg v3) cln))) + (@ c2 (clc (neg v1) (clc (neg v2) (clc (pos v3) cln))) + (@ c3 (clc (pos v2) (clc (pos v3) (clc (neg v4) cln))) + (@ c4 (clc (neg v2) (clc (neg v3) (clc (pos v4) cln))) + (@ c5 (clc (neg v1) (clc (neg v3) (clc (neg v4) cln))) + (@ c6 (clc (pos v1) (clc (pos v3) (clc (pos v4) cln))) + (@ c7 (clc (neg v1) (clc (pos v2) (clc (pos v4) cln))) + (@ c8 (clc (pos v1) (clc (neg v2) (clc (neg v4) cln))) + (@ c9 (clc (pos v1) cln) + (@ c10 (clc (pos v2) cln) + (@ cs (CMapc 1 c1 + (CMapc 2 c2 + (CMapc 3 c3 + (CMapc 4 c4 + (CMapc 5 c5 + (CMapc 6 c6 + (CMapc 7 c7 + (CMapc 8 c8 + (CMapc 9 c9 + (CMapc 10 c10 + CMapn)))))))))) + (@ c cln + (@ t (Tracec 9 (Tracec 10 (Tracec 2 (Tracec 4 (Tracec 5 Tracen))))) + (@ h RATHintsn + (test_is_rat_trace + cs + c + t + h + UPR_Bottom + ) + )))) + )))))))))) + )))) +) + +(declare test_is_lrat_proof_of_bottom + (! f CMap + (! p LRATProof + (! r bool + (! sc (^ (is_lrat_proof_of_bottom f p) r) type))))) + +; Test passes if the (test_is_lrat_proof_of_bottom ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (@ c1 (clc (pos v1) (clc (pos v2) (clc (neg v3) cln))) + (@ c2 (clc (neg v1) (clc (neg v2) (clc (pos v3) cln))) + (@ c3 (clc (pos v2) (clc (pos v3) (clc (neg v4) cln))) + (@ c4 (clc (neg v2) (clc (neg v3) (clc (pos v4) cln))) + (@ c5 (clc (neg v1) (clc (neg v3) (clc (neg v4) cln))) + (@ c6 (clc (pos v1) (clc (pos v3) (clc (pos v4) cln))) + (@ c7 (clc (neg v1) (clc (pos v2) (clc (pos v4) cln))) + (@ c8 (clc (pos v1) (clc (neg v2) (clc (neg v4) cln))) + (@ cs (CMapc 1 c1 + (CMapc 2 c2 + (CMapc 3 c3 + (CMapc 4 c4 + (CMapc 5 c5 + (CMapc 6 c6 + (CMapc 7 c7 + (CMapc 8 c8 + CMapn)))))))) + (@ p + (LRATProofa 9 + (clc (pos v1) cln) + Tracen + (RATHintsc 2 (Tracec 6 (Tracec 8 Tracen)) + (RATHintsc 5 (Tracec 1 (Tracec 8 Tracen)) + (RATHintsc 7 (Tracec 6 (Tracec 1 Tracen)) + RATHintsn))) + (LRATProofd (CIListc 1 (CIListc 6 (CIListc 8 CIListn))) + (LRATProofa 10 + (clc (pos v2) cln) + (Tracec 9 (Tracec 7 (Tracec 5 (Tracec 3 Tracen)))) + RATHintsn + (LRATProofd (CIListc 3 (CIListc 7 CIListn)) + (LRATProofa 11 + cln + (Tracec 9 (Tracec 10 (Tracec 2 (Tracec 4 (Tracec 5 Tracen))))) + RATHintsn + LRATProofn))))) + (test_is_lrat_proof_of_bottom + cs + p + tt + ) + )) + )))))))) + )))) +) + +; Test passes if the (test_is_lrat_proof_of_bottom ...) application is well-typed. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (@ c1 (clc (pos v1) (clc (pos v2) (clc (neg v3) cln))) + (@ c2 (clc (neg v1) (clc (neg v2) (clc (pos v3) cln))) + (@ c3 (clc (pos v2) (clc (pos v3) (clc (neg v4) cln))) + (@ c4 (clc (neg v2) (clc (neg v3) (clc (pos v4) cln))) + (@ c5 (clc (neg v1) (clc (neg v3) (clc (neg v4) cln))) + (@ c6 (clc (pos v1) (clc (pos v3) (clc (pos v4) cln))) + (@ c7 (clc (neg v1) (clc (pos v2) (clc (pos v4) cln))) + (@ c8 (clc (pos v1) (clc (neg v2) (clc (neg v4) cln))) + (@ cs (CMapc 1 c1 + (CMapc 2 c2 + (CMapc 3 c3 + (CMapc 4 c4 + (CMapc 5 c5 + (CMapc 6 c6 + (CMapc 7 c7 + (CMapc 8 c8 + CMapn)))))))) + (@ p + (LRATProofa 9 + (clc (pos v1) cln) + Tracen + (RATHintsc 2 (Tracec 6 (Tracec 8 Tracen)) + (RATHintsc 5 (Tracec 1 (Tracec 8 Tracen)) + (RATHintsc 7 (Tracec 6 (Tracec 1 Tracen)) + RATHintsn))) + (LRATProofd (CIListc 1 (CIListc 6 (CIListc 8 CIListn))) + (LRATProofa 10 + (clc (pos v2) cln) + (Tracec 9 (Tracec 7 (Tracec 5 (Tracec 3 Tracen)))) + RATHintsn + (LRATProofd (CIListc 3 (CIListc 7 CIListn)) + (LRATProofa 11 + cln + (Tracec 9 (Tracec 10 (Tracec 2 (Tracec 4 Tracen)))) + RATHintsn + LRATProofn))))) + (test_is_lrat_proof_of_bottom + cs + p + ff + ) + )) + )))))))) + )))) +) + +; Proof from Figure 2 of "Efficient Certified RAT Verification" +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (% pf_c1 (holds (clc (pos v1) (clc (pos v2) (clc (neg v3) cln)))) + (% pf_c2 (holds (clc (neg v1) (clc (neg v2) (clc (pos v3) cln)))) + (% pf_c3 (holds (clc (pos v2) (clc (pos v3) (clc (neg v4) cln)))) + (% pf_c4 (holds (clc (neg v2) (clc (neg v3) (clc (pos v4) cln)))) + (% pf_c5 (holds (clc (neg v1) (clc (neg v3) (clc (neg v4) cln)))) + (% pf_c6 (holds (clc (pos v1) (clc (pos v3) (clc (pos v4) cln)))) + (% pf_c7 (holds (clc (neg v1) (clc (pos v2) (clc (pos v4) cln)))) + (% pf_c8 (holds (clc (pos v1) (clc (neg v2) (clc (neg v4) cln)))) + (@ pf_cmap + (CMapc_proof 1 _ _ pf_c1 + (CMapc_proof 2 _ _ pf_c2 + (CMapc_proof 3 _ _ pf_c3 + (CMapc_proof 4 _ _ pf_c4 + (CMapc_proof 5 _ _ pf_c5 + (CMapc_proof 6 _ _ pf_c6 + (CMapc_proof 7 _ _ pf_c7 + (CMapc_proof 8 _ _ pf_c8 + CMapn_proof)))))))) + (@ lrat_proof_witness + (LRATProofa 9 + (clc (pos v1) cln) + Tracen + (RATHintsc 2 (Tracec 6 (Tracec 8 Tracen)) + (RATHintsc 5 (Tracec 1 (Tracec 8 Tracen)) + (RATHintsc 7 (Tracec 6 (Tracec 1 Tracen)) + RATHintsn))) + (LRATProofd (CIListc 1 (CIListc 6 (CIListc 8 CIListn))) + (LRATProofa 10 + (clc (pos v2) cln) + (Tracec 9 (Tracec 7 (Tracec 5 (Tracec 3 Tracen)))) + RATHintsn + (LRATProofd (CIListc 3 (CIListc 7 CIListn)) + (LRATProofa 11 + cln + (Tracec 9 (Tracec 10 (Tracec 2 (Tracec 4 (Tracec 5 Tracen))))) + RATHintsn + LRATProofn))))) + (: + (holds cln) + (lrat_proof_of_bottom _ pf_cmap lrat_proof_witness)) + )) + )))))))) + )))) +) + +; Clauses 1 and 9 are identical. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (% pf_c1 (holds (clc (pos v1) (clc (pos v2) (clc (neg v3) cln)))) + (% pf_c2 (holds (clc (neg v1) (clc (neg v2) (clc (pos v3) cln)))) + (% pf_c3 (holds (clc (pos v2) (clc (pos v3) (clc (neg v4) cln)))) + (% pf_c4 (holds (clc (neg v2) (clc (neg v3) (clc (pos v4) cln)))) + (% pf_c5 (holds (clc (neg v1) (clc (neg v3) (clc (neg v4) cln)))) + (% pf_c6 (holds (clc (pos v1) (clc (pos v3) (clc (pos v4) cln)))) + (% pf_c7 (holds (clc (neg v1) (clc (pos v2) (clc (pos v4) cln)))) + (% pf_c8 (holds (clc (pos v1) (clc (neg v2) (clc (neg v4) cln)))) + (% pf_c9 (holds (clc (pos v1) (clc (pos v2) (clc (neg v3) cln)))) + (@ pf_cmap + (CMapc_proof 1 _ _ pf_c1 + (CMapc_proof 2 _ _ pf_c2 + (CMapc_proof 3 _ _ pf_c3 + (CMapc_proof 4 _ _ pf_c4 + (CMapc_proof 5 _ _ pf_c5 + (CMapc_proof 6 _ _ pf_c6 + (CMapc_proof 7 _ _ pf_c7 + (CMapc_proof 8 _ _ pf_c8 + (CMapc_proof 9 _ _ pf_c9 + CMapn_proof))))))))) + (@ lrat_proof_witness + (LRATProofa 10 + (clc (pos v1) cln) + Tracen + (RATHintsc 2 (Tracec 6 (Tracec 8 Tracen)) + (RATHintsc 5 (Tracec 1 (Tracec 8 Tracen)) + (RATHintsc 7 (Tracec 6 (Tracec 9 Tracen)) + RATHintsn))) + (LRATProofd (CIListc 1 (CIListc 6 (CIListc 8 (CIListc 9 CIListn)))) + (LRATProofa 11 + (clc (pos v2) cln) + (Tracec 10 (Tracec 7 (Tracec 5 (Tracec 3 Tracen)))) + RATHintsn + (LRATProofd (CIListc 3 (CIListc 7 CIListn)) + (LRATProofa 12 + cln + (Tracec 10 (Tracec 11 (Tracec 2 (Tracec 4 (Tracec 5 Tracen))))) + RATHintsn + LRATProofn))))) + (: + (holds cln) + (lrat_proof_of_bottom _ pf_cmap lrat_proof_witness)) + )) + ))))))))) + )))) +) + +; Proof from Figure 1 of "Efficient Certified RAT Verification" +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (% pf_c1 (holds (clc (pos v1) (clc (pos v2) (clc (neg v3) cln)))) + (% pf_c2 (holds (clc (neg v1) (clc (neg v2) (clc (pos v3) cln)))) + (% pf_c3 (holds (clc (pos v2) (clc (pos v3) (clc (neg v4) cln)))) + (% pf_c4 (holds (clc (neg v2) (clc (neg v3) (clc (pos v4) cln)))) + (% pf_c5 (holds (clc (neg v1) (clc (neg v3) (clc (neg v4) cln)))) + (% pf_c6 (holds (clc (pos v1) (clc (pos v3) (clc (pos v4) cln)))) + (% pf_c7 (holds (clc (neg v1) (clc (pos v2) (clc (pos v4) cln)))) + (% pf_c8 (holds (clc (pos v1) (clc (neg v2) (clc (neg v4) cln)))) + (@ pf_cmap + (CMapc_proof 1 _ _ pf_c1 + (CMapc_proof 2 _ _ pf_c2 + (CMapc_proof 3 _ _ pf_c3 + (CMapc_proof 4 _ _ pf_c4 + (CMapc_proof 5 _ _ pf_c5 + (CMapc_proof 6 _ _ pf_c6 + (CMapc_proof 7 _ _ pf_c7 + (CMapc_proof 8 _ _ pf_c8 + CMapn_proof)))))))) + (@ lrat_proof_witness + (LRATProofa 9 + (clc (pos v1) (clc (pos v2) cln)) + (Tracec 1 (Tracec 6 (Tracec 3 Tracen))) + RATHintsn + (LRATProofd (CIListc 1 CIListn) + (LRATProofa 10 + (clc (pos v1) (clc (pos v3) cln)) + (Tracec 9 (Tracec 8 (Tracec 6 Tracen))) + RATHintsn + (LRATProofd (CIListc 6 CIListn) + (LRATProofa 11 + (clc (pos v1) cln) + (Tracec 10 (Tracec 9 (Tracec 4 (Tracec 8 Tracen)))) + RATHintsn + (LRATProofd (CIListc 8 (CIListc 9 (CIListc 10 CIListn))) + (LRATProofa 12 + (clc (pos v2) cln) + (Tracec 11 (Tracec 7 (Tracec 5 (Tracec 3 Tracen)))) + RATHintsn + (LRATProofd (CIListc 3 (CIListc 7 CIListn)) + (LRATProofa 13 + cln + (Tracec 11 (Tracec 12 (Tracec 2 (Tracec 4 (Tracec 5 Tracen))))) + RATHintsn + LRATProofn + ))))))))) + (: + (holds cln) + (lrat_proof_of_bottom _ pf_cmap lrat_proof_witness)) + )) + )))))))) + )))) +) diff --git a/proofs/signatures/sat.plf b/proofs/signatures/sat.plf index b95caa8fd..8f40aa8bf 100644 --- a/proofs/signatures/sat.plf +++ b/proofs/signatures/sat.plf @@ -19,8 +19,8 @@ ; code to check resolutions -(program append ((c1 clause) (c2 clause)) clause - (match c1 (cln c2) ((clc l c1') (clc l (append c1' c2))))) +(program clause_append ((c1 clause) (c2 clause)) clause + (match c1 (cln c2) ((clc l c1') (clc l (clause_append c1' c2))))) ; we use marks as follows: ; -- mark 1 to record if we are supposed to remove a positive occurrence of the variable. @@ -49,7 +49,7 @@ (match m (tt (do (ifmarked4 v v (markvar4 v)) c')) (ff (do (ifmarked4 v (markvar4 v) v) (markvar2 v) (clc l c'))))))))) - ((concat_cl c1 c2) (append (simplify_clause c1) (simplify_clause c2))) + ((concat_cl c1 c2) (clause_append (simplify_clause c1) (simplify_clause c2))) ((clr l c1) (match l ; set mark 1 to indicate we should remove v, and fail if diff --git a/proofs/signatures/smt.plf b/proofs/signatures/smt.plf index 06dc16153..57dc5bd1e 100644 --- a/proofs/signatures/smt.plf +++ b/proofs/signatures/smt.plf @@ -439,6 +439,22 @@ (holds C)) (holds (clc (neg v) C)))))))))) +;; Numeric primitives + +(program mpz_sub ((x mpz) (y mpz)) mpz + (mp_add x (mp_mul (~1) y))) + +(program mp_ispos ((x mpz)) formula + (mp_ifneg x false true)) + +(program mpz_eq ((x mpz) (y mpz)) formula + (mp_ifzero (mpz_sub x y) true false)) + +(program mpz_lt ((x mpz) (y mpz)) formula + (mp_ifneg (mpz_sub x y) true false)) + +(program mpz_lte ((x mpz) (y mpz)) formula + (mp_ifneg (mpz_sub x y) true (mpz_eq x y))) ;; Example: ;; diff --git a/proofs/signatures/th_bv.plf b/proofs/signatures/th_bv.plf index 6012e052a..934951a86 100644 --- a/proofs/signatures/th_bv.plf +++ b/proofs/signatures/th_bv.plf @@ -3,20 +3,6 @@ (declare trust-bad (th_holds false)) ; helper stuff -(program mpz_sub ((x mpz) (y mpz)) mpz - (mp_add x (mp_mul (~1) y))) - -(program mp_ispos ((x mpz)) formula - (mp_ifneg x false true)) - -(program mpz_eq ((x mpz) (y mpz)) formula - (mp_ifzero (mpz_sub x y) true false)) - -(program mpz_lt ((x mpz) (y mpz)) formula - (mp_ifneg (mpz_sub x y) true false)) - -(program mpz_lte ((x mpz) (y mpz)) formula - (mp_ifneg (mpz_sub x y) true (mpz_eq x y))) (program mpz_ ((x mpz) (y mpz)) formula (mp_ifzero (mpz_sub x y) true false)) -- cgit v1.2.3 From 147fd723e6c13eb3dd44a43073be03a64ea3fe66 Mon Sep 17 00:00:00 2001 From: Andrew Reynolds Date: Tue, 11 Dec 2018 16:38:00 -0600 Subject: Remove alternate versions of mbqi (#2742) --- src/CMakeLists.txt | 2 - src/options/options_handler.cpp | 18 +- src/options/quantifiers_modes.cpp | 6 - src/options/quantifiers_modes.h | 4 - src/smt/smt_engine.cpp | 17 - src/theory/quantifiers/first_order_model.cpp | 590 ------------- src/theory/quantifiers/first_order_model.h | 121 +-- src/theory/quantifiers/fmf/ambqi_builder.cpp | 971 --------------------- src/theory/quantifiers/fmf/ambqi_builder.h | 105 --- src/theory/quantifiers/fmf/model_builder.cpp | 684 --------------- src/theory/quantifiers/fmf/model_builder.h | 157 ---- src/theory/quantifiers/fmf/model_engine.cpp | 1 - src/theory/quantifiers_engine.cpp | 13 +- src/theory/uf/theory_uf_model.cpp | 84 -- src/theory/uf/theory_uf_model.h | 39 - .../regress0/fmf/Arrow_Order-smtlib.778341.smt | 2 +- test/regress/regress0/fmf/QEpres-uf.855035.smt | 2 +- test/regress/regress1/fmf/nlp042+1.smt2 | 2 +- 18 files changed, 14 insertions(+), 2804 deletions(-) delete mode 100644 src/theory/quantifiers/fmf/ambqi_builder.cpp delete mode 100644 src/theory/quantifiers/fmf/ambqi_builder.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9e93bd953..91c06ddd9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -475,8 +475,6 @@ libcvc4_add_sources( theory/quantifiers/extended_rewrite.h theory/quantifiers/first_order_model.cpp theory/quantifiers/first_order_model.h - theory/quantifiers/fmf/ambqi_builder.cpp - theory/quantifiers/fmf/ambqi_builder.h theory/quantifiers/fmf/bounded_integers.cpp theory/quantifiers/fmf/bounded_integers.h theory/quantifiers/fmf/full_model_check.cpp diff --git a/src/options/options_handler.cpp b/src/options/options_handler.cpp index 420396452..36144e70e 100644 --- a/src/options/options_handler.cpp +++ b/src/options/options_handler.cpp @@ -267,7 +267,8 @@ agg \n\ \n\ "; -const std::string OptionsHandler::s_mbqiModeHelp = "\ +const std::string OptionsHandler::s_mbqiModeHelp = + "\ Model-based quantifier instantiation modes currently supported by the --mbqi option:\n\ \n\ default \n\ @@ -277,12 +278,8 @@ default \n\ none \n\ + Disable model-based quantifier instantiation.\n\ \n\ -gen-ev \n\ -+ Use model-based quantifier instantiation algorithm from CADE 24 finite\n\ - model finding paper based on generalizing evaluations.\n\ -\n\ -abs \n\ -+ Use abstract MBQI algorithm (uses disjoint sets). \n\ +trust \n\ ++ Do not instantiate quantified formulas (incomplete technique).\n\ \n\ "; @@ -660,14 +657,11 @@ void OptionsHandler::checkLiteralMatchMode( theory::quantifiers::MbqiMode OptionsHandler::stringToMbqiMode( std::string option, std::string optarg) { - if(optarg == "gen-ev") { - return theory::quantifiers::MBQI_GEN_EVAL; - } else if(optarg == "none") { + if (optarg == "none") + { return theory::quantifiers::MBQI_NONE; } else if(optarg == "default" || optarg == "fmc") { return theory::quantifiers::MBQI_FMC; - } else if(optarg == "abs") { - return theory::quantifiers::MBQI_ABS; } else if(optarg == "trust") { return theory::quantifiers::MBQI_TRUST; } else if(optarg == "help") { diff --git a/src/options/quantifiers_modes.cpp b/src/options/quantifiers_modes.cpp index 1814a363d..b08f71c2e 100644 --- a/src/options/quantifiers_modes.cpp +++ b/src/options/quantifiers_modes.cpp @@ -64,18 +64,12 @@ std::ostream& operator<<(std::ostream& out, theory::quantifiers::LiteralMatchMod std::ostream& operator<<(std::ostream& out, theory::quantifiers::MbqiMode mode) { switch(mode) { - case theory::quantifiers::MBQI_GEN_EVAL: - out << "MBQI_GEN_EVAL"; - break; case theory::quantifiers::MBQI_NONE: out << "MBQI_NONE"; break; case theory::quantifiers::MBQI_FMC: out << "MBQI_FMC"; break; - case theory::quantifiers::MBQI_ABS: - out << "MBQI_ABS"; - break; case theory::quantifiers::MBQI_TRUST: out << "MBQI_TRUST"; break; diff --git a/src/options/quantifiers_modes.h b/src/options/quantifiers_modes.h index 41378d2cd..eea043865 100644 --- a/src/options/quantifiers_modes.h +++ b/src/options/quantifiers_modes.h @@ -53,14 +53,10 @@ enum LiteralMatchMode { }; enum MbqiMode { - /** mbqi from CADE 24 paper */ - MBQI_GEN_EVAL, /** no mbqi */ MBQI_NONE, /** default, mbqi from Section 5.4.2 of AJR thesis */ MBQI_FMC, - /** abstract mbqi algorithm */ - MBQI_ABS, /** mbqi trust (produce no instantiations) */ MBQI_TRUST, }; diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index 7abfd8273..ae20fa156 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -1809,12 +1809,6 @@ void SmtEngine::setDefaults() { options::mbqiMode.set( quantifiers::MBQI_NONE ); } } - if( options::mbqiMode()==quantifiers::MBQI_ABS ){ - if( !d_logic.isPure(THEORY_UF) ){ - //MBQI_ABS is only supported in pure quantified UF - options::mbqiMode.set( quantifiers::MBQI_FMC ); - } - } if( options::fmfFunWellDefinedRelevant() ){ if( !options::fmfFunWellDefined.wasSetByUser() ){ options::fmfFunWellDefined.set( true ); @@ -1852,17 +1846,6 @@ void SmtEngine::setDefaults() { options::instWhenMode.set( quantifiers::INST_WHEN_LAST_CALL ); } } - if( options::mbqiMode()==quantifiers::MBQI_ABS ){ - if( !options::preSkolemQuant.wasSetByUser() ){ - options::preSkolemQuant.set( true ); - } - if( !options::preSkolemQuantNested.wasSetByUser() ){ - options::preSkolemQuantNested.set( true ); - } - if( !options::fmfOneInstPerRound.wasSetByUser() ){ - options::fmfOneInstPerRound.set( true ); - } - } } //apply counterexample guided instantiation options diff --git a/src/theory/quantifiers/first_order_model.cpp b/src/theory/quantifiers/first_order_model.cpp index 0eec40de2..5eb65ed21 100644 --- a/src/theory/quantifiers/first_order_model.cpp +++ b/src/theory/quantifiers/first_order_model.cpp @@ -15,7 +15,6 @@ #include "theory/quantifiers/first_order_model.h" #include "options/base_options.h" #include "options/quantifiers_options.h" -#include "theory/quantifiers/fmf/ambqi_builder.h" #include "theory/quantifiers/fmf/bounded_integers.h" #include "theory/quantifiers/fmf/full_model_check.h" #include "theory/quantifiers/fmf/model_engine.h" @@ -414,473 +413,6 @@ unsigned FirstOrderModel::getModelBasisArg(Node n) return n.getAttribute(ModelBasisArgAttribute()); } -Node FirstOrderModelIG::UfModelTreeGenerator::getIntersection(TheoryModel* m, - Node n1, - Node n2, - bool& isGround) -{ - isGround = true; - std::vector children; - children.push_back(n1.getOperator()); - for (unsigned i = 0, size = n1.getNumChildren(); i < size; i++) - { - if (n1[i] == n2[i]) - { - if (n1[i].getAttribute(ModelBasisAttribute())) - { - isGround = false; - } - children.push_back(n1[i]); - } - else if (n1[i].getAttribute(ModelBasisAttribute())) - { - children.push_back(n2[i]); - } - else if (n2[i].getAttribute(ModelBasisAttribute())) - { - children.push_back(n1[i]); - } - else if (m->areEqual(n1[i], n2[i])) - { - children.push_back(n1[i]); - } - else - { - return Node::null(); - } - } - return NodeManager::currentNM()->mkNode(APPLY_UF, children); -} - -void FirstOrderModelIG::UfModelTreeGenerator::setValue( - TheoryModel* m, Node n, Node v, bool ground, bool isReq) -{ - Assert(!n.isNull()); - Assert(!v.isNull()); - d_set_values[isReq ? 1 : 0][ground ? 1 : 0][n] = v; - if (!ground) - { - for (unsigned i = 0, defSize = d_defaults.size(); i < defSize; i++) - { - // for correctness, to allow variable order-independent function - // interpretations, we must ensure that the intersection of all default - // terms is also defined. - // for example, if we have that f( e, a ) = ..., and f( b, e ) = ..., - // then we must define f( b, a ). - bool isGround; - Node ni = getIntersection(m, n, d_defaults[i], isGround); - if (!ni.isNull()) - { - // if the intersection exists, and is not already defined - if (d_set_values[0][isGround ? 1 : 0].find(ni) - == d_set_values[0][isGround ? 1 : 0].end() - && d_set_values[1][isGround ? 1 : 0].find(ni) - == d_set_values[1][isGround ? 1 : 0].end()) - { - // use the current value - setValue(m, ni, v, isGround, false); - } - } - } - d_defaults.push_back(n); - } - if (isReq - && d_set_values[0][ground ? 1 : 0].find(n) - != d_set_values[0][ground ? 1 : 0].end()) - { - d_set_values[0][ground ? 1 : 0].erase(n); - } -} - -void FirstOrderModelIG::UfModelTreeGenerator::makeModel(TheoryModel* m, - uf::UfModelTree& tree) -{ - for (int j = 0; j < 2; j++) - { - for (int k = 0; k < 2; k++) - { - for (std::map::iterator it = d_set_values[j][k].begin(); - it != d_set_values[j][k].end(); - ++it) - { - tree.setValue(m, it->first, it->second, k == 1); - } - } - } - if (!d_default_value.isNull()) - { - tree.setDefaultValue(m, d_default_value); - } - tree.simplify(); -} - -void FirstOrderModelIG::UfModelTreeGenerator::clear() -{ - d_default_value = Node::null(); - for (int j = 0; j < 2; j++) - { - for (int k = 0; k < 2; k++) - { - d_set_values[j][k].clear(); - } - } - d_defaults.clear(); -} - -FirstOrderModelIG::FirstOrderModelIG(QuantifiersEngine * qe, context::Context* c, std::string name) : -FirstOrderModel(qe, c,name) { - -} - -void FirstOrderModelIG::processInitialize( bool ispre ){ - if( ispre ){ - //rebuild models - d_uf_model_tree.clear(); - d_uf_model_gen.clear(); - } -} - -void FirstOrderModelIG::processInitializeModelForTerm( Node n ){ - if( n.getKind()==APPLY_UF ){ - Node op = n.getOperator(); - if( d_uf_model_tree.find( op )==d_uf_model_tree.end() ){ - TypeNode tn = op.getType(); - tn = tn[ (int)tn.getNumChildren()-1 ]; - //only generate models for predicates and functions with uninterpreted range types - //if( tn==NodeManager::currentNM()->booleanType() || tn.isSort() ){ - d_uf_model_tree[ op ] = uf::UfModelTree( op ); - d_uf_model_gen[ op ].clear(); - //} - } - } - /* - if( n.getType().isArray() ){ - while( n.getKind()==STORE ){ - n = n[0]; - } - Node nn = getRepresentative( n ); - if( d_array_model.find( nn )==d_array_model.end() ){ - d_array_model[nn] = arrays::ArrayModel( nn, this ); - } - } - */ -} - -//for evaluation of quantifier bodies - -void FirstOrderModelIG::resetEvaluate(){ - d_eval_uf_use_default.clear(); - d_eval_uf_model.clear(); - d_eval_term_index_order.clear(); -} - -//if evaluate( n ) = eVal, -// let n' = ri * n be the formula n instantiated with the current values in r_iter -// if eVal = 1, then n' is true, if eVal = -1, then n' is false, -// if eVal = 0, then n' cannot be proven to be equal to phaseReq -// if eVal is not 0, then -// each n{ri->d_index[0]/x_0...ri->d_index[depIndex]/x_depIndex, */x_(depIndex+1) ... */x_n } is equivalent in the current model -int FirstOrderModelIG::evaluate( Node n, int& depIndex, RepSetIterator* ri ){ - Debug("fmf-eval-debug2") << "Evaluate " << n << std::endl; - //Notice() << "Eval " << n << std::endl; - if( n.getKind()==NOT ){ - int val = evaluate( n[0], depIndex, ri ); - return val==1 ? -1 : ( val==-1 ? 1 : 0 ); - }else if( n.getKind()==OR || n.getKind()==AND ){ - int baseVal = n.getKind()==AND ? 1 : -1; - int eVal = baseVal; - int posDepIndex = ri->getNumTerms(); - int negDepIndex = -1; - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - //evaluate subterm - int childDepIndex; - Node nn = n[i]; - int eValT = evaluate( nn, childDepIndex, ri ); - if( eValT==baseVal ){ - if( eVal==baseVal ){ - if( childDepIndex>negDepIndex ){ - negDepIndex = childDepIndex; - } - } - }else if( eValT==-baseVal ){ - eVal = -baseVal; - if( childDepIndexdepIndex2 ? depIndex1 : depIndex2; - return eVal==eVal2 ? 1 : -1; - } - } - return 0; - }else if( n.getKind()==ITE ){ - int depIndex1, depIndex2; - int eVal = evaluate( n[0], depIndex1, ri ); - if( eVal==0 ){ - //evaluate children to see if they are the same value - int eval1 = evaluate( n[1], depIndex1, ri ); - if( eval1!=0 ){ - int eval2 = evaluate( n[1], depIndex2, ri ); - if( eval1==eval2 ){ - depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; - return eval1; - } - } - }else{ - int eValT = evaluate( n[eVal==1 ? 1 : 2], depIndex2, ri ); - depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; - return eValT; - } - return 0; - }else if( n.getKind()==FORALL ){ - return 0; - }else{ - //Debug("fmf-eval-debug") << "Evaluate literal " << n << std::endl; - int retVal = 0; - depIndex = ri->getNumTerms()-1; - Node val = evaluateTerm( n, depIndex, ri ); - if( !val.isNull() ){ - if( areEqual( val, d_true ) ){ - retVal = 1; - }else if( areEqual( val, d_false ) ){ - retVal = -1; - }else{ - if( val.getKind()==EQUAL ){ - if( areEqual( val[0], val[1] ) ){ - retVal = 1; - }else if( areDisequal( val[0], val[1] ) ){ - retVal = -1; - } - } - } - } - if( retVal!=0 ){ - Debug("fmf-eval-debug") << "Evaluate literal: return " << retVal << ", depIndex = " << depIndex << std::endl; - }else{ - Trace("fmf-eval-amb") << "Neither true nor false : " << n << std::endl; - Trace("fmf-eval-amb") << " value : " << val << std::endl; - } - return retVal; - } -} - -Node FirstOrderModelIG::evaluateTerm( Node n, int& depIndex, RepSetIterator* ri ){ - //Message() << "Eval term " << n << std::endl; - Node val; - depIndex = ri->getNumTerms()-1; - //check the type of n - if( n.getKind()==INST_CONSTANT ){ - int v = n.getAttribute(InstVarNumAttribute()); - depIndex = ri->getIndexOrder( v ); - val = ri->getCurrentTerm( v ); - }else if( n.getKind()==ITE ){ - int depIndex1, depIndex2; - int eval = evaluate( n[0], depIndex1, ri ); - if( eval==0 ){ - //evaluate children to see if they are the same - Node val1 = evaluateTerm( n[ 1 ], depIndex1, ri ); - Node val2 = evaluateTerm( n[ 2 ], depIndex2, ri ); - if( val1==val2 ){ - val = val1; - depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; - }else{ - return Node::null(); - } - }else{ - val = evaluateTerm( n[ eval==1 ? 1 : 2 ], depIndex2, ri ); - depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; - } - }else{ - std::vector< int > children_depIndex; - //default term evaluate : evaluate all children, recreate the value - val = evaluateTermDefault( n, depIndex, children_depIndex, ri ); - Trace("fmf-eval-debug") << "Evaluate term, value from " << n << " is " << val << std::endl; - if( !val.isNull() ){ - bool setVal = false; - //custom ways of evaluating terms - if( n.getKind()==APPLY_UF ){ - Node op = n.getOperator(); - //Debug("fmf-eval-debug") << "Evaluate term " << n << " (" << gn << ")" << std::endl; - //if it is a defined UF, then consult the interpretation - if( d_uf_model_tree.find( op )!=d_uf_model_tree.end() ){ - int argDepIndex = 0; - //make the term model specifically for n - makeEvalUfModel( n ); - //now, consult the model - if( d_eval_uf_use_default[n] ){ - Trace("fmf-eval-debug") << "get default" << std::endl; - val = d_uf_model_tree[ op ].getValue( this, val, argDepIndex ); - }else{ - Trace("fmf-eval-debug") << "get uf model" << std::endl; - val = d_eval_uf_model[ n ].getValue( this, val, argDepIndex ); - } - //Debug("fmf-eval-debug") << "Evaluate term " << n << " (" << gn << ")" << std::endl; - //d_eval_uf_model[ n ].debugPrint("fmf-eval-debug", d_qe ); - Assert( !val.isNull() ); - //recalculate the depIndex - depIndex = -1; - for( int i=0; idepIndex ){ - depIndex = children_depIndex[index]; - } - } - setVal = true; - }else{ - Trace("fmf-eval-debug") << "No model." << std::endl; - } - } - //if not set already, rewrite and consult model for interpretation - if( !setVal ){ - val = Rewriter::rewrite( val ); - if( !val.isConst() ){ - return Node::null(); - } - } - Trace("fmf-eval-debug") << "Evaluate term " << n << " = "; - Trace("fmf-eval-debug") << getRepresentative(val); - Trace("fmf-eval-debug") << " (term " << val << "), depIndex = " << depIndex << std::endl; - } - } - return val; -} - -Node FirstOrderModelIG::evaluateTermDefault( Node n, int& depIndex, std::vector< int >& childDepIndex, RepSetIterator* ri ){ - depIndex = -1; - if( n.getNumChildren()==0 ){ - return n; - }else{ - bool isInterp = n.getKind()!=APPLY_UF; - //first we must evaluate the arguments - std::vector< Node > children; - if( n.getMetaKind()==kind::metakind::PARAMETERIZED ){ - children.push_back( n.getOperator() ); - } - //for each argument, calculate its value, and the variables its value depends upon - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - childDepIndex.push_back( -1 ); - Node nn = evaluateTerm( n[i], childDepIndex[i], ri ); - if( nn.isNull() ){ - depIndex = ri->getNumTerms()-1; - return nn; - }else{ - if( childDepIndex[i]>depIndex ){ - depIndex = childDepIndex[i]; - } - if( isInterp ){ - if( !nn.isConst() ) { - nn = getRepresentative( nn ); - } - } - children.push_back( nn ); - } - } - //recreate the value - Node val = NodeManager::currentNM()->mkNode( n.getKind(), children ); - return val; - } -} - -void FirstOrderModelIG::makeEvalUfModel( Node n ){ - if( d_eval_uf_model.find( n )==d_eval_uf_model.end() ){ - makeEvalUfIndexOrder( n ); - if( !d_eval_uf_use_default[n] ){ - Node op = n.getOperator(); - d_eval_uf_model[n] = uf::UfModelTree( op, d_eval_term_index_order[n] ); - d_uf_model_gen[op].makeModel( this, d_eval_uf_model[n] ); - //Debug("fmf-index-order") << "Make model for " << n << " : " << std::endl; - //d_eval_uf_model[n].debugPrint( std::cout, d_qe->getModel(), 2 ); - } - } -} - -struct sortGetMaxVariableNum { - std::map< Node, int > d_max_var_num; - int computeMaxVariableNum( Node n ){ - if( n.getKind()==INST_CONSTANT ){ - return n.getAttribute(InstVarNumAttribute()); - }else if( TermUtil::hasInstConstAttr(n) ){ - int maxVal = -1; - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - int val = getMaxVariableNum( n[i] ); - if( val>maxVal ){ - maxVal = val; - } - } - return maxVal; - }else{ - return -1; - } - } - int getMaxVariableNum( Node n ){ - std::map< Node, int >::iterator it = d_max_var_num.find( n ); - if( it==d_max_var_num.end() ){ - int num = computeMaxVariableNum( n ); - d_max_var_num[n] = num; - return num; - }else{ - return it->second; - } - } - bool operator() (Node i,Node j) { return (getMaxVariableNum(i) > argIndex; - std::vector< Node > args; - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - if( argIndex.find( n[i] )==argIndex.end() ){ - args.push_back( n[i] ); - } - argIndex[n[i]].push_back( i ); - } - sortGetMaxVariableNum sgmvn; - std::sort( args.begin(), args.end(), sgmvn ); - for( int i=0; i<(int)args.size(); i++ ){ - for( int j=0; j<(int)argIndex[ args[i] ].size(); j++ ){ - d_eval_term_index_order[n].push_back( argIndex[ args[i] ][j] ); - } - } - bool useDefault = true; - for( int i=0; i<(int)d_eval_term_index_order[n].size(); i++ ){ - if( i!=d_eval_term_index_order[n][i] ){ - useDefault = false; - break; - } - } - d_eval_uf_use_default[n] = useDefault; - Debug("fmf-index-order") << "Will consider the following index ordering for " << n << " : "; - for( int i=0; i<(int)d_eval_term_index_order[n].size(); i++ ){ - Debug("fmf-index-order") << d_eval_term_index_order[n][i] << " "; - } - Debug("fmf-index-order") << std::endl; - } -} - FirstOrderModelFmc::FirstOrderModelFmc(QuantifiersEngine * qe, context::Context* c, std::string name) : FirstOrderModel(qe, c, name){ @@ -989,128 +521,6 @@ Node FirstOrderModelFmc::getFunctionValue(Node op, const char* argPrefix ) { return NodeManager::currentNM()->mkNode(kind::LAMBDA, boundVarList, curr); } -FirstOrderModelAbs::FirstOrderModelAbs(QuantifiersEngine * qe, context::Context* c, std::string name) : -FirstOrderModel(qe, c, name) { - -} - -FirstOrderModelAbs::~FirstOrderModelAbs() -{ - for(std::map::iterator i = d_models.begin(); i != d_models.end(); ++i) { - delete (*i).second; - } -} - -void FirstOrderModelAbs::processInitialize( bool ispre ) { - if( !ispre ){ - Trace("ambqi-debug") << "Process initialize" << std::endl; - for( std::map::iterator it = d_models.begin(); it != d_models.end(); ++it ) { - Node op = it->first; - TypeNode tno = op.getType(); - Trace("ambqi-debug") << " Init " << op << " " << tno << std::endl; - for( unsigned i=0; i::iterator it = d_rep_id.find( r ); - if( it!=d_rep_id.end() ){ - return it->second; - }else{ - return 0; - } -} - -TNode FirstOrderModelAbs::getUsedRepresentative( TNode n ) { - if( hasTerm( n ) ){ - if( n.getType().isBoolean() ){ - return areEqual(n, d_true) ? d_true : d_false; - }else{ - return getRepresentative( n ); - } - }else{ - Trace("qint-debug") << "Get rep " << n << " " << n.getType() << std::endl; - Assert( d_rep_set.hasType( n.getType() ) && !d_rep_set.d_type_reps[n.getType()].empty() ); - return d_rep_set.d_type_reps[n.getType()][0]; - } -} - -Node FirstOrderModelAbs::getFunctionValue(Node op, const char* argPrefix ) { - if( d_models_valid[op] ){ - Trace("ambqi-debug") << "Get function value for " << op << std::endl; - TypeNode type = op.getType(); - std::vector< Node > vars; - for( size_t i=0; imkBoundVar( ss.str(), type[i] ); - vars.push_back( b ); - } - Node boundVarList = NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, vars); - Node curr = d_models[op]->getFunctionValue( this, op, vars ); - Node fv = NodeManager::currentNM()->mkNode(kind::LAMBDA, boundVarList, curr); - Trace("ambqi-debug") << "Return " << fv << std::endl; - return fv; - }else{ - - } - return Node::null(); -} - -void FirstOrderModelAbs::processInitializeModelForTerm( Node n ) { - if( n.getKind()==APPLY_UF || n.getKind()==VARIABLE || n.getKind()==SKOLEM ){ - Node op = n.getKind()==APPLY_UF ? n.getOperator() : n; - if( d_models.find(op)==d_models.end()) { - Trace("abmqi-debug") << "init model for " << op << std::endl; - d_models[op] = new AbsDef; - d_models_valid[op] = false; - } - } -} - -void FirstOrderModelAbs::collectEqVars( TNode q, TNode n, std::map< int, bool >& eq_vars ) { - for( unsigned i=0; i=0 && v<(int)q[0].getNumChildren() ); - eq_vars[v] = true; - } - collectEqVars( q, n[i], eq_vars ); - } -} - -void FirstOrderModelAbs::processInitializeQuantifier( Node q ) { - if( d_var_order.find( q )==d_var_order.end() ){ - std::map< int, bool > eq_vars; - for( unsigned i=0; i::iterator it = eq_vars.begin(); it != eq_vars.end(); ++it ){ - if( it->second==(r==1) ){ - d_var_index[q][it->first] = d_var_order[q].size(); - d_var_order[q].push_back( it->first ); - } - } - } - } -} - -Node FirstOrderModelAbs::getVariable( Node q, unsigned i ) { - return q[0][d_var_order[q][i]]; -} - } /* CVC4::theory::quantifiers namespace */ } /* CVC4::theory namespace */ } /* CVC4 namespace */ diff --git a/src/theory/quantifiers/first_order_model.h b/src/theory/quantifiers/first_order_model.h index 7da5b2088..b96b42dc2 100644 --- a/src/theory/quantifiers/first_order_model.h +++ b/src/theory/quantifiers/first_order_model.h @@ -42,15 +42,10 @@ namespace quantifiers { class TermDb; -class FirstOrderModelIG; - namespace fmcheck { class FirstOrderModelFmc; }/* CVC4::theory::quantifiers::fmcheck namespace */ -class FirstOrderModelQInt; -class FirstOrderModelAbs; - struct IsStarAttributeId {}; typedef expr::Attribute IsStarAttribute; @@ -94,10 +89,7 @@ class FirstOrderModel : public TheoryModel public: FirstOrderModel(QuantifiersEngine* qe, context::Context* c, std::string name); - virtual FirstOrderModelIG* asFirstOrderModelIG() { return nullptr; } virtual fmcheck::FirstOrderModelFmc* asFirstOrderModelFmc() { return nullptr; } - virtual FirstOrderModelQInt* asFirstOrderModelQInt() { return nullptr; } - virtual FirstOrderModelAbs* asFirstOrderModelAbs() { return nullptr; } /** assert quantifier */ void assertQuantifier( Node n ); /** get number of asserted quantifiers */ @@ -172,11 +164,11 @@ class FirstOrderModel : public TheoryModel /** get variable id */ std::map > d_quant_var_id; /** process initialize model for term */ - virtual void processInitializeModelForTerm(Node n) = 0; + virtual void processInitializeModelForTerm(Node n) {} /** process initialize quantifier */ virtual void processInitializeQuantifier(Node q) {} /** process initialize */ - virtual void processInitialize(bool ispre) = 0; + virtual void processInitialize(bool ispre) {} private: // list of inactive quantified formulas @@ -193,85 +185,6 @@ class FirstOrderModel : public TheoryModel void computeModelBasisArgAttribute(Node n); };/* class FirstOrderModel */ -class FirstOrderModelIG : public FirstOrderModel -{ - public: // for Theory UF: - /** class for generating models for uninterpreted functions - * - * This implements the model construction from page 6 of Reynolds et al, - * "Quantifier Instantiation Techniques for Finite Model Finding in SMT", - * CADE 2013. - */ - class UfModelTreeGenerator - { - public: - UfModelTreeGenerator() {} - ~UfModelTreeGenerator() {} - /** set default value */ - void setDefaultValue(Node v) { d_default_value = v; } - /** set value */ - void setValue( - TheoryModel* m, Node n, Node v, bool ground = true, bool isReq = true); - /** make model */ - void makeModel(TheoryModel* m, uf::UfModelTree& tree); - /** reset */ - void clear(); - - public: - /** the overall default value */ - Node d_default_value; - /** - * Stores (required, ground) values in key, value pairs of the form - * ( P( a, b ), c ), which indicates P( a, b ) has value c in the model. - * The "non-ground" values indicate that the key has a "model-basis" - * variable, for example, ( P( _, b ), c ) indicates that P( x, b ) has the - * value b for any value of x. - */ - std::map d_set_values[2][2]; - /** stores the set of non-ground keys in the above maps */ - std::vector d_defaults; - /** - * Returns the term corresponding to the intersection of n1 and n2, if it - * exists, for example, for P( _, a ) and P( b, _ ), this method returns - * P( b, a ), where _ is the "model basis" variable. We take into account - * equality between arguments, so if a=b, then the intersection of P( a, a ) - * and P( b, _ ) is P( a, a ). - */ - Node getIntersection(TheoryModel* m, Node n1, Node n2, bool& isGround); - }; - /** models for each UF operator */ - std::map d_uf_model_tree; - /** model generators for each UF operator */ - std::map d_uf_model_gen; - - private: - //map from terms to the models used to calculate their value - std::map< Node, bool > d_eval_uf_use_default; - std::map< Node, uf::UfModelTree > d_eval_uf_model; - void makeEvalUfModel( Node n ); - //index ordering to use for each term - std::map< Node, std::vector< int > > d_eval_term_index_order; - void makeEvalUfIndexOrder( Node n ); -//the following functions are for evaluating quantifier bodies -public: - FirstOrderModelIG(QuantifiersEngine * qe, context::Context* c, std::string name); - - FirstOrderModelIG* asFirstOrderModelIG() override { return this; } - // initialize the model - void processInitialize(bool ispre) override; - //for initialize model - void processInitializeModelForTerm(Node n) override; - /** reset evaluation */ - void resetEvaluate(); - /** evaluate functions */ - int evaluate( Node n, int& depIndex, RepSetIterator* ri ); - Node evaluateTerm( Node n, int& depIndex, RepSetIterator* ri ); -private: - //default evaluate term function - Node evaluateTermDefault( Node n, int& depIndex, std::vector< int >& childDepIndex, RepSetIterator* ri ); -};/* class FirstOrderModelIG */ - - namespace fmcheck { class Def; @@ -301,36 +214,6 @@ class FirstOrderModelFmc : public FirstOrderModel }/* CVC4::theory::quantifiers::fmcheck namespace */ -class AbsDef; - -class FirstOrderModelAbs : public FirstOrderModel -{ - public: - std::map< Node, AbsDef * > d_models; - std::map< Node, bool > d_models_valid; - std::map< TNode, unsigned > d_rep_id; - std::map< TypeNode, unsigned > d_domain; - std::map< Node, std::vector< int > > d_var_order; - std::map< Node, std::map< int, int > > d_var_index; - - private: - /** get current model value */ - void processInitializeModelForTerm(Node n) override; - void processInitializeQuantifier(Node q) override; - void collectEqVars( TNode q, TNode n, std::map< int, bool >& eq_vars ); - TNode getUsedRepresentative( TNode n ); - - public: - FirstOrderModelAbs(QuantifiersEngine * qe, context::Context* c, std::string name); - ~FirstOrderModelAbs() override; - FirstOrderModelAbs* asFirstOrderModelAbs() override { return this; } - void processInitialize(bool ispre) override; - unsigned getRepresentativeId( TNode n ); - bool isValidType( TypeNode tn ) { return d_domain.find( tn )!=d_domain.end(); } - Node getFunctionValue(Node op, const char* argPrefix ); - Node getVariable( Node q, unsigned i ); -}; - }/* CVC4::theory::quantifiers namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/quantifiers/fmf/ambqi_builder.cpp b/src/theory/quantifiers/fmf/ambqi_builder.cpp deleted file mode 100644 index f2b131f21..000000000 --- a/src/theory/quantifiers/fmf/ambqi_builder.cpp +++ /dev/null @@ -1,971 +0,0 @@ -/********************* */ -/*! \file ambqi_builder.cpp - ** \verbatim - ** Top contributors (to current version): - ** Andrew Reynolds, Tim King - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2018 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Implementation of abstract MBQI builder - **/ - -#include "theory/quantifiers/fmf/ambqi_builder.h" - -#include "base/cvc4_check.h" -#include "options/quantifiers_options.h" -#include "theory/quantifiers/instantiate.h" -#include "theory/quantifiers/term_database.h" -#include "theory/quantifiers/term_util.h" - -using namespace std; -using namespace CVC4::kind; -using namespace CVC4::context; - -namespace CVC4 { -namespace theory { -namespace quantifiers { - - -void AbsDef::construct_func( FirstOrderModelAbs * m, std::vector< TNode >& fapps, unsigned depth ) { - d_def.clear(); - Assert( !fapps.empty() ); - if( depth==fapps[0].getNumChildren() ){ - //if( fapps.size()>1 ){ - // for( unsigned i=0; i " << m->getRepresentativeId( fapps[i] ) << std::endl; - // } - //} - //get representative in model for this term - d_value = m->getRepresentativeId( fapps[0] ); - Assert( d_value!=val_none ); - }else{ - TypeNode tn = fapps[0][depth].getType(); - std::map< unsigned, std::vector< TNode > > fapp_child; - - //partition based on evaluations of fapps[1][depth]....fapps[n][depth] - for( unsigned i=0; igetRepresentativeId( fapps[i][depth] ); - Assert( r < 32 ); - fapp_child[r].push_back( fapps[i] ); - } - - //do completion - std::map< unsigned, unsigned > fapp_child_index; - unsigned def = m->d_domain[ tn ]; - unsigned minSize = fapp_child.begin()->second.size(); - unsigned minSizeIndex = fapp_child.begin()->first; - for( std::map< unsigned, std::vector< TNode > >::iterator it = fapp_child.begin(); it != fapp_child.end(); ++it ){ - fapp_child_index[it->first] = ( 1 << it->first ); - def = def & ~( 1 << it->first ); - if( it->second.size()second.size(); - minSizeIndex = it->first; - } - } - fapp_child_index[minSizeIndex] |= def; - d_default = fapp_child_index[minSizeIndex]; - - //construct children - for( std::map< unsigned, std::vector< TNode > >::iterator it = fapp_child.begin(); it != fapp_child.end(); ++it ){ - Trace("abs-model-debug") << "Construct " << it->first << " : " << fapp_child_index[it->first] << " : "; - const RepSet* rs = m->getRepSet(); - debugPrintUInt("abs-model-debug", - rs->getNumRepresentatives(tn), - fapp_child_index[it->first]); - Trace("abs-model-debug") << " : " << it->second.size() << " terms." << std::endl; - d_def[fapp_child_index[it->first]].construct_func( m, it->second, depth+1 ); - } - } -} - -void AbsDef::simplify( FirstOrderModelAbs * m, TNode q, TNode n, unsigned depth ) { - if( d_value==val_none && !d_def.empty() ){ - //process the default - std::map< unsigned, AbsDef >::iterator defd = d_def.find( d_default ); - Assert( defd!=d_def.end() ); - unsigned newDef = d_default; - std::vector< unsigned > to_erase; - defd->second.simplify( m, q, n, depth+1 ); - int defVal = defd->second.d_value; - bool isConstant = ( defVal!=val_none ); - //process each child - for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ - if( it->first!=d_default ){ - it->second.simplify( m, q, n, depth+1 ); - if( it->second.d_value==defVal && it->second.d_value!=val_none ){ - newDef = newDef | it->first; - to_erase.push_back( it->first ); - }else{ - isConstant = false; - } - } - } - if( !to_erase.empty() ){ - //erase old default - int defVal = defd->second.d_value; - d_def.erase( d_default ); - //set new default - d_default = newDef; - d_def[d_default].construct_def_entry( m, q, n, defVal, depth+1 ); - //erase redundant entries - for( unsigned i=0; igetRepSet(); - unsigned dSize = rs->getNumRepresentatives(tn); - Assert( dSize<32 ); - for( std::map< unsigned, AbsDef >::const_iterator it = d_def.begin(); it != d_def.end(); ++it ){ - for( unsigned i=0; ifirst ); - if( it->first==d_default ){ - Trace(c) << "*"; - } - if( it->second.d_value!=val_none ){ - Trace(c) << " -> V[" << it->second.d_value << "]"; - } - Trace(c) << std::endl; - it->second.debugPrint( c, m, f, depth+1 ); - } - } - } -} - -bool AbsDef::addInstantiations( FirstOrderModelAbs * m, QuantifiersEngine * qe, TNode q, std::vector< Node >& terms, int& inst, unsigned depth ) { - if( inst==0 || !options::fmfOneInstPerRound() ){ - if( d_value==1 ){ - //instantiations are all true : ignore this - return true; - }else{ - if( depth==q[0].getNumChildren() ){ - if (qe->getInstantiate()->addInstantiation(q, terms, true)) - { - Trace("ambqi-inst-debug") << "-> Added instantiation." << std::endl; - inst++; - return true; - }else{ - Trace("ambqi-inst-debug") << "-> Failed to add instantiation." << std::endl; - //we are incomplete - return false; - } - }else{ - bool osuccess = true; - TypeNode tn = m->getVariable( q, depth ).getType(); - for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ - //get witness term - unsigned index = 0; - bool success; - do { - success = false; - index = getId( it->first, index ); - if( index<32 ){ - const RepSet* rs = m->getRepSet(); - Assert(index < rs->getNumRepresentatives(tn)); - terms[m->d_var_order[q][depth]] = - rs->getRepresentative(tn, index); - if( !it->second.addInstantiations( m, qe, q, terms, inst, depth+1 ) && inst==0 ){ - //if we are incomplete, and have not yet added an instantiation, keep trying - index++; - Trace("ambqi-inst-debug") << "At depth " << depth << ", failed branch, no instantiations and incomplete, increment index : " << index << std::endl; - }else{ - success = true; - } - } - }while( !qe->inConflict() && !success && index<32 ); - //mark if we are incomplete - osuccess = osuccess && success; - } - return osuccess; - } - } - }else{ - return true; - } -} - -void AbsDef::construct_entry( std::vector< unsigned >& entry, std::vector< bool >& entry_def, int v, unsigned depth ) { - if( depth==entry.size() ){ - d_value = v; - }else{ - d_def[entry[depth]].construct_entry( entry, entry_def, v, depth+1 ); - if( entry_def[depth] ){ - d_default = entry[depth]; - } - } -} - -void AbsDef::get_defs( unsigned u, std::vector< AbsDef * >& defs ) { - for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ - if( ( u & it->first )!=0 ){ - Assert( (u & it->first)==u ); - defs.push_back( &it->second ); - } - } -} - -void AbsDef::construct_normalize( FirstOrderModelAbs * m, TNode q, std::vector< AbsDef * >& defs, unsigned depth ) { - if( depth==q[0].getNumChildren() ){ - Assert( defs.size()==1 ); - d_value = defs[0]->d_value; - }else{ - TypeNode tn = m->getVariable( q, depth ).getType(); - unsigned def = m->d_domain[tn]; - for( unsigned i=0; i::iterator itd = defs[i]->d_def.begin(); itd != defs[i]->d_def.end(); ++itd ){ - if( isSimple( itd->first ) && ( def & itd->first )!=0 ){ - def &= ~( itd->first ); - //process this value - std::vector< AbsDef * > cdefs; - for( unsigned j=0; jget_defs( itd->first, cdefs ); - } - d_def[itd->first].construct_normalize( m, q, cdefs, depth+1 ); - if( def==0 ){ - d_default = itd->first; - break; - } - } - } - if( def==0 ){ - break; - } - } - if( def!=0 ){ - d_default = def; - //process the default - std::vector< AbsDef * > cdefs; - for( unsigned j=0; jget_defs( d_default, cdefs ); - } - d_def[d_default].construct_normalize( m, q, cdefs, depth+1 ); - } - } -} - -void AbsDef::construct_def_entry( FirstOrderModelAbs * m, TNode q, TNode n, int v, unsigned depth ) { - d_value = v; - if( depthgetVariable( q, depth ).getType(); - unsigned dom = m->d_domain[tn] ; - d_def[dom].construct_def_entry( m, q, n, v, depth+1 ); - d_default = dom; - } -} - -void AbsDef::apply_ucompose( FirstOrderModelAbs * m, TNode q, - std::vector< unsigned >& entry, std::vector< bool >& entry_def, - std::vector< int >& terms, std::map< unsigned, int >& vchildren, - AbsDef * a, unsigned depth ) { - if( depth==terms.size() ){ - if( Trace.isOn("ambqi-check-debug2") ){ - Trace("ambqi-check-debug2") << "Add entry ( "; - const RepSet* rs = m->getRepSet(); - for( unsigned i=0; igetNumRepresentatives(m->getVariable(q, i).getType()); - debugPrintUInt( "ambqi-check-debug2", dSize, entry[i] ); - Trace("ambqi-check-debug2") << " "; - } - Trace("ambqi-check-debug2") << ")" << std::endl; - } - a->construct_entry( entry, entry_def, d_value ); - }else{ - unsigned id; - if( terms[depth]==val_none ){ - //a variable - std::map< unsigned, int >::iterator itv = vchildren.find( depth ); - Assert( itv!=vchildren.end() ); - unsigned prev_v = entry[itv->second]; - bool prev_vd = entry_def[itv->second]; - for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ - entry[itv->second] = it->first & prev_v; - entry_def[itv->second] = ( it->first==d_default ) && prev_vd; - if( entry[itv->second]!=0 ){ - it->second.apply_ucompose( m, q, entry, entry_def, terms, vchildren, a, depth+1 ); - } - } - entry[itv->second] = prev_v; - entry_def[itv->second] = prev_vd; - }else{ - id = (unsigned)terms[depth]; - Assert( id<32 ); - unsigned fid = 1 << id; - std::map< unsigned, AbsDef >::iterator it = d_def.find( fid ); - if( it!=d_def.end() ){ - it->second.apply_ucompose( m, q, entry, entry_def, terms, vchildren, a, depth+1 ); - }else{ - d_def[d_default].apply_ucompose( m, q, entry, entry_def, terms, vchildren, a, depth+1 ); - } - } - } -} - -void AbsDef::construct_var_eq( FirstOrderModelAbs * m, TNode q, unsigned v1, unsigned v2, int curr, int currv, unsigned depth ) { - if( depth==q[0].getNumChildren() ){ - Assert( currv!=val_none ); - d_value = currv; - }else{ - TypeNode tn = m->getVariable( q, depth ).getType(); - unsigned dom = m->d_domain[tn]; - int vindex = depth==v1 ? 0 : ( depth==v2 ? 1 : val_none ); - if( vindex==val_none ){ - d_def[dom].construct_var_eq( m, q, v1, v2, curr, currv, depth+1 ); - d_default = dom; - }else{ - Assert( currv==val_none ); - if( curr==val_none ){ - unsigned numReps = m->getRepSet()->getNumRepresentatives(tn); - Assert( numReps < 32 ); - for( unsigned i=0; igetVariable( q, depth ).getType(); - if( v==depth ){ - const unsigned numReps = m->getRepSet()->getNumRepresentatives(tn); - CVC4_CHECK(numReps > 0 && numReps < 32); - for( unsigned i=0; id_domain[tn]; - d_def[dom].construct_var( m, q, v, currv, depth+1 ); - d_default = dom; - } - } -} - -void AbsDef::construct_compose( FirstOrderModelAbs * m, TNode q, TNode n, AbsDef * f, - std::map< unsigned, AbsDef * >& children, - std::map< unsigned, int >& bchildren, std::map< unsigned, int >& vchildren, - std::vector< unsigned >& entry, std::vector< bool >& entry_def ) { - const RepSet* rs = m->getRepSet(); - if( n.getKind()==OR || n.getKind()==AND ){ - // short circuiting - for( std::map< unsigned, AbsDef * >::iterator it = children.begin(); it != children.end(); ++it ){ - if( ( it->second->d_value==0 && n.getKind()==AND ) || - ( it->second->d_value==1 && n.getKind()==OR ) ){ - //std::cout << "Short circuit " << it->second->d_value << " " << entry.size() << "/" << q[0].getNumChildren() << std::endl; - unsigned count = q[0].getNumChildren() - entry.size(); - for( unsigned i=0; id_domain[m->getVariable( q, entry.size() ).getType()] ); - entry_def.push_back( true ); - } - construct_entry( entry, entry_def, it->second->d_value ); - for( unsigned i=0; i values; - values.resize( n.getNumChildren(), val_none ); - for( std::map< unsigned, AbsDef * >::iterator it = children.begin(); it != children.end(); ++it ){ - values[it->first] = it->second->d_value; - } - for( std::map< unsigned, int >::iterator it = bchildren.begin(); it != bchildren.end(); ++it ){ - values[it->first] = it->second; - } - //look up value(s) - f->apply_ucompose( m, q, entry, entry_def, values, vchildren, this ); - }else{ - bool incomplete = false; - //we are composing with an interpreted function - std::vector< TNode > values; - values.resize( n.getNumChildren(), TNode::null() ); - for( std::map< unsigned, AbsDef * >::iterator it = children.begin(); it != children.end(); ++it ){ - Trace("ambqi-check-debug2") << "composite : " << it->first << " : " << it->second->d_value; - if( it->second->d_value>=0 ){ - if (it->second->d_value - >= (int)rs->getNumRepresentatives(n[it->first].getType())) - { - std::cout << it->second->d_value << " " << n[it->first] << " " - << n[it->first].getType() << " " - << rs->getNumRepresentatives(n[it->first].getType()) - << std::endl; - } - Assert(it->second->d_value - < (int)rs->getNumRepresentatives(n[it->first].getType())); - values[it->first] = rs->getRepresentative(n[it->first].getType(), - it->second->d_value); - }else{ - incomplete = true; - } - Trace("ambqi-check-debug2") << " ->> " << values[it->first] << std::endl; - } - for( std::map< unsigned, int >::iterator it = bchildren.begin(); it != bchildren.end(); ++it ){ - Trace("ambqi-check-debug2") << " basic : " << it->first << " : " << it->second; - if( it->second>=0 ){ - Assert(it->second - < (int)rs->getNumRepresentatives(n[it->first].getType())); - values[it->first] = - rs->getRepresentative(n[it->first].getType(), it->second); - }else{ - incomplete = true; - } - Trace("ambqi-check-debug2") << " ->> " << values[it->first] << std::endl; - } - Assert( vchildren.empty() ); - if( incomplete ){ - Trace("ambqi-check-debug2") << "Construct incomplete entry." << std::endl; - - //if a child is unknown, we must return unknown - construct_entry( entry, entry_def, val_unk ); - }else{ - if( Trace.isOn("ambqi-check-debug2") ){ - for( unsigned i=0; imkNode( n.getKind(), values ); - vv = Rewriter::rewrite( vv ); - int v = m->getRepresentativeId( vv ); - construct_entry( entry, entry_def, v ); - } - } - }else{ - //take product of arguments - TypeNode tn = m->getVariable( q, entry.size() ).getType(); - Assert( m->isValidType( tn ) ); - unsigned def = m->d_domain[tn]; - if( Trace.isOn("ambqi-check-debug2") ){ - for( unsigned i=0; i::iterator it = children.begin(); it != children.end(); ++it ){ - Assert( it->second!=NULL ); - //process each child - for( std::map< unsigned, AbsDef >::iterator itd = it->second->d_def.begin(); itd != it->second->d_def.end(); ++itd ){ - if( itd->first!=it->second->d_default && ( def & itd->first )!=0 ){ - def &= ~( itd->first ); - //process this value - std::map< unsigned, AbsDef * > cchildren; - for( std::map< unsigned, AbsDef * >::iterator it2 = children.begin(); it2 != children.end(); ++it2 ){ - Assert( it2->second!=NULL ); - std::map< unsigned, AbsDef >::iterator itdf = it2->second->d_def.find( itd->first ); - if( itdf!=it2->second->d_def.end() ){ - cchildren[it2->first] = &itdf->second; - }else{ - Assert( it2->second->getDefault()!=NULL ); - cchildren[it2->first] = it2->second->getDefault(); - } - } - if( Trace.isOn("ambqi-check-debug2") ){ - for( unsigned i=0; igetNumRepresentatives(tn), - itd->first); - Trace("ambqi-check-debug2") << " " << children.size() << " " << cchildren.size() << std::endl; - } - entry.push_back( itd->first ); - entry_def.push_back( def==0 ); - construct_compose( m, q, n, f, cchildren, bchildren, vchildren, entry, entry_def ); - entry_def.pop_back(); - entry.pop_back(); - if( def==0 ){ - break; - } - } - } - if( def==0 ){ - break; - } - } - if( def!=0 ){ - if( Trace.isOn("ambqi-check-debug2") ){ - for( unsigned i=0; i cdchildren; - for( std::map< unsigned, AbsDef * >::iterator it = children.begin(); it != children.end(); ++it ){ - Assert( it->second->getDefault()!=NULL ); - cdchildren[it->first] = it->second->getDefault(); - } - if( Trace.isOn("ambqi-check-debug2") ){ - for( unsigned i=0; igetNumRepresentatives(tn), def); - Trace("ambqi-check-debug2") << " " << children.size() << " " << cdchildren.size() << std::endl; - } - entry.push_back( def ); - entry_def.push_back( true ); - construct_compose( m, q, n, f, cdchildren, bchildren, vchildren, entry, entry_def ); - entry_def.pop_back(); - entry.pop_back(); - } - } -} - -bool AbsDef::construct( FirstOrderModelAbs * m, TNode q, TNode n, AbsDef * f, - std::map< unsigned, AbsDef * >& children, - std::map< unsigned, int >& bchildren, std::map< unsigned, int >& vchildren, - int varChCount ) { - if( Trace.isOn("ambqi-check-debug3") ){ - for( unsigned i=0; i::iterator it = bchildren.begin(); it !=bchildren.end(); ++it ){ - if( ( it->second==0 && n.getKind()==AND ) || - ( it->second==1 && n.getKind()==OR ) ){ - construct_def_entry( m, q, q[0], it->second ); - return true; - } - } - } - Trace("ambqi-check-debug2") << "Construct compose..." << std::endl; - std::vector< unsigned > entry; - std::vector< bool > entry_def; - if( f && varChCount>0 ){ - AbsDef unorm; - unorm.construct_compose( m, q, n, f, children, bchildren, vchildren, entry, entry_def ); - //normalize - std::vector< AbsDef* > defs; - defs.push_back( &unorm ); - construct_normalize( m, q, defs ); - }else{ - construct_compose( m, q, n, f, children, bchildren, vchildren, entry, entry_def ); - } - Assert( is_normalized() ); - return true; - }else if( varChCount==1 && ( n.getKind()==EQUAL && !n[0].getType().isBoolean() ) ){ - Trace("ambqi-check-debug2") << "Expand variable child..." << std::endl; - //expand the variable based on its finite domain - AbsDef a; - a.construct_var( m, q, vchildren.begin()->second, val_none ); - children[vchildren.begin()->first] = &a; - vchildren.clear(); - std::vector< unsigned > entry; - std::vector< bool > entry_def; - Trace("ambqi-check-debug2") << "Construct compose with variable..." << std::endl; - construct_compose( m, q, n, f, children, bchildren, vchildren, entry, entry_def ); - return true; - }else if( varChCount==2 && ( n.getKind()==EQUAL && !n[0].getType().isBoolean() ) ){ - Trace("ambqi-check-debug2") << "Construct variable equality..." << std::endl; - //efficient expansion of the equality - construct_var_eq( m, q, vchildren[0], vchildren[1], val_none, val_none ); - return true; - }else{ - return false; - } -} - -void AbsDef::negate() { - for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ - it->second.negate(); - } - if( d_value==0 ){ - d_value = 1; - }else if( d_value==1 ){ - d_value = 0; - } -} - -Node AbsDef::getFunctionValue( FirstOrderModelAbs * m, TNode op, std::vector< Node >& vars, unsigned depth ) { - const RepSet* rs = m->getRepSet(); - if( depth==vars.size() ){ - TypeNode tn = op.getType(); - if( tn.getNumChildren()>0 ){ - tn = tn[tn.getNumChildren() - 1]; - } - if( d_value>=0 ){ - Assert(d_value < (int)rs->getNumRepresentatives(tn)); - if( tn.isBoolean() ){ - return NodeManager::currentNM()->mkConst( d_value==1 ); - }else{ - return rs->getRepresentative(tn, d_value); - } - }else{ - return Node::null(); - } - }else{ - TypeNode tn = vars[depth].getType(); - Node curr; - curr = d_def[d_default].getFunctionValue( m, op, vars, depth+1 ); - for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ - if( it->first!=d_default ){ - unsigned id = getId( it->first ); - Assert(id < rs->getNumRepresentatives(tn)); - TNode n = rs->getRepresentative(tn, id); - Node fv = it->second.getFunctionValue( m, op, vars, depth+1 ); - if( !curr.isNull() && !fv.isNull() ){ - curr = NodeManager::currentNM()->mkNode( ITE, vars[depth].eqNode( n ), fv, curr ); - }else{ - curr = Node::null(); - } - } - } - return curr; - } -} - -bool AbsDef::isSimple( unsigned n ) { - return (n & (n - 1))==0; -} - -unsigned AbsDef::getId( unsigned n, unsigned start, unsigned end ) { - Assert( n!=0 ); - while( (n & ( 1 << start )) == 0 ){ - start++; - if( start==end ){ - return start; - } - } - return start; -} - -Node AbsDef::evaluate( FirstOrderModelAbs * m, TypeNode retTyp, std::vector< Node >& args ) { - std::vector< unsigned > iargs; - for( unsigned i=0; igetRepresentativeId( args[i] ); - iargs.push_back( v ); - } - return evaluate( m, retTyp, iargs, 0 ); -} - -Node AbsDef::evaluate( FirstOrderModelAbs * m, TypeNode retTyp, std::vector< unsigned >& iargs, unsigned depth ) { - if( d_value!=val_none ){ - if( d_value==val_unk ){ - return Node::null(); - }else{ - const RepSet* rs = m->getRepSet(); - Assert(d_value >= 0 && d_value < (int)rs->getNumRepresentatives(retTyp)); - return rs->getRepresentative(retTyp, d_value); - } - }else{ - std::map< unsigned, AbsDef >::iterator it = d_def.find( iargs[depth] ); - if( it==d_def.end() ){ - return d_def[d_default].evaluate( m, retTyp, iargs, depth+1 ); - }else{ - return it->second.evaluate( m, retTyp, iargs, depth+1 ); - } - } -} - -bool AbsDef::is_normalized() { - for( std::map< unsigned, AbsDef >::iterator it1 = d_def.begin(); it1 != d_def.end(); ++it1 ){ - if( !it1->second.is_normalized() ){ - return false; - } - for( std::map< unsigned, AbsDef >::iterator it2 = d_def.begin(); it2 != d_def.end(); ++it2 ){ - if( it1->first!=it2->first && (( it1->first & it2->first )!=0) ){ - return false; - } - } - } - return true; -} - -AbsMbqiBuilder::AbsMbqiBuilder( context::Context* c, QuantifiersEngine* qe ) : -QModelBuilder( c, qe ){ - d_true = NodeManager::currentNM()->mkConst( true ); - d_false = NodeManager::currentNM()->mkConst( false ); -} - - -//------------------------model construction---------------------------- - -bool AbsMbqiBuilder::processBuildModel(TheoryModel* m) { - if (!m->areFunctionValuesEnabled()) - { - // nothing to do if no functions - return true; - } - Trace("ambqi-debug") << "process build model " << std::endl; - FirstOrderModel* f = (FirstOrderModel*)m; - FirstOrderModelAbs* fm = f->asFirstOrderModelAbs(); - RepSet* rs = m->getRepSetPtr(); - fm->initialize(); - //process representatives - fm->d_rep_id.clear(); - fm->d_domain.clear(); - - //initialize boolean sort - TypeNode b = d_true.getType(); - rs->d_type_reps[b].clear(); - rs->d_type_reps[b].push_back(d_false); - rs->d_type_reps[b].push_back(d_true); - fm->d_rep_id[d_false] = 0; - fm->d_rep_id[d_true] = 1; - - //initialize unintpreted sorts - Trace("ambqi-model") << std::endl << "Making representatives..." << std::endl; - for (std::map >::iterator it = - rs->d_type_reps.begin(); - it != rs->d_type_reps.end(); - ++it) - { - if( it->first.isSort() ){ - Assert( !it->second.empty() ); - //set the domain - fm->d_domain[it->first] = 0; - Trace("ambqi-model") << "Representatives for " << it->first << " : " << std::endl; - for( unsigned i=0; isecond.size(); i++ ){ - if( i<32 ){ - fm->d_domain[it->first] |= ( 1 << i ); - } - Trace("ambqi-model") << i << " : " << it->second[i] << std::endl; - fm->d_rep_id[it->second[i]] = i; - } - if( it->second.size()>=32 ){ - fm->d_domain.erase( it->first ); - } - } - } - - Trace("ambqi-model") << std::endl << "Making function definitions..." << std::endl; - //construct the models for functions - for( std::map::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) { - Node f = it->first; - Trace("ambqi-model-debug") << "Building Model for " << f << std::endl; - //reset the model - it->second->clear(); - //get all (non-redundant) f-applications - std::vector< TNode > fapps; - Trace("ambqi-model-debug") << "Initial terms: " << std::endl; - std::map< Node, std::vector< Node > >::iterator itut = fm->d_uf_terms.find( f ); - if( itut!=fm->d_uf_terms.end() ){ - for( size_t i=0; isecond.size(); i++ ){ - Node n = itut->second[i]; - // only consider unique up to congruence (in model equality engine)? - Trace("ambqi-model-debug") << " " << n << " -> " << fm->getRepresentativeId( n ) << std::endl; - fapps.push_back( n ); - } - } - if( fapps.empty() ){ - //choose arbitrary value - Node mbt = fm->getModelBasisOpTerm(f); - Trace("ambqi-model-debug") << "Initial terms empty, add " << mbt << std::endl; - fapps.push_back( mbt ); - } - bool fValid = true; - for( unsigned i=0; id_domain.find( fapps[0][i].getType() )==fm->d_domain.end() ){ - Trace("ambqi-model") << "Interpretation of " << f << " is not valid."; - Trace("ambqi-model") << " (domain for " << fapps[0][i].getType() << " is too large)." << std::endl; - fValid = false; - break; - } - } - fm->d_models_valid[f] = fValid; - if( fValid ){ - //construct the ambqi model - it->second->construct_func( fm, fapps ); - Trace("ambqi-model-debug") << "Interpretation of " << f << " : " << std::endl; - it->second->debugPrint("ambqi-model-debug", fm, fapps[0] ); - Trace("ambqi-model-debug") << "Simplifying " << f << "..." << std::endl; - it->second->simplify( fm, TNode::null(), fapps[0] ); - Trace("ambqi-model") << "(Simplified) interpretation of " << f << " : " << std::endl; - it->second->debugPrint("ambqi-model", fm, fapps[0] ); - -/* - if( Debug.isOn("ambqi-model-debug") ){ - for( size_t i=0; id_uf_terms[f].size(); i++ ){ - Node e = it->second->evaluate_n( fm, fm->d_uf_terms[f][i] ); - Debug("ambqi-model-debug") << fm->d_uf_terms[f][i] << " evaluates to " << e << std::endl; - Assert( fm->areEqual( e, fm->d_uf_terms[f][i] ) ); - } - } -*/ - } - } - Trace("ambqi-model") << "Construct model representation..." << std::endl; - //make function values - for( std::map::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) { - if( it->first.getType().getNumChildren()>1 ){ - Trace("ambqi-model") << "Construct for " << it->first << "..." << std::endl; - Node f_def = fm->getFunctionValue( it->first, "$x" ); - m->assignFunctionDefinition( it->first, f_def ); - } - } - Assert( d_addedLemmas==0 ); - return TheoryEngineModelBuilder::processBuildModel( m ); -} - - -//--------------------model checking--------------------------------------- - -//do exhaustive instantiation -int AbsMbqiBuilder::doExhaustiveInstantiation( FirstOrderModel * fm, Node q, int effort ) { - Trace("ambqi-check") << "Exhaustive instantiation " << q << " " << effort << std::endl; - if (effort==0) { - FirstOrderModelAbs * fma = fm->asFirstOrderModelAbs(); - bool quantValid = true; - for( unsigned i=0; iisValidType( q[0][i].getType() ) ){ - quantValid = false; - Trace("ambqi-inst") << "Interpretation of " << q << " is not valid because of type " << q[0][i].getType() << std::endl; - break; - } - } - if( quantValid ){ - Trace("ambqi-check") << "Compute interpretation..." << std::endl; - AbsDef ad; - doCheck( fma, q, ad, q[1] ); - //now process entries - Trace("ambqi-inst-debug") << "...Current : " << d_addedLemmas << std::endl; - Trace("ambqi-inst") << "Interpretation of " << q << " is : " << std::endl; - ad.debugPrint( "ambqi-inst", fma, q[0] ); - Trace("ambqi-inst") << std::endl; - Trace("ambqi-check") << "Add instantiations..." << std::endl; - int lem = 0; - quantValid = ad.addInstantiations( fma, d_qe, q, lem ); - Trace("ambqi-inst") << "...Added " << lem << " lemmas." << std::endl; - if( lem>0 ){ - //if we were incomplete but added at least one lemma, we are ok - quantValid = true; - } - d_addedLemmas += lem; - Trace("ambqi-inst-debug") << "...Total : " << d_addedLemmas << std::endl; - } - return quantValid ? 1 : 0; - }else{ - return 1; - } -} - -bool AbsMbqiBuilder::doCheck( FirstOrderModelAbs * m, TNode q, AbsDef & ad, TNode n ) { - Assert( n.getKind()!=FORALL ); - if( n.getKind()==NOT && n[0].getKind()!=FORALL ){ - doCheck( m, q, ad, n[0] ); - ad.negate(); - return true; - }else{ - std::map< unsigned, AbsDef > children; - std::map< unsigned, int > bchildren; - std::map< unsigned, int > vchildren; - int varChCount = 0; - for( unsigned i=0; id_var_index[q][ m->getVariableId( q, n[i] ) ]; - //vchildren[i] = m->getVariableId( q, n[i] ); - }else if( m->hasTerm( n[i] ) ){ - bchildren[i] = m->getRepresentativeId( n[i] ); - }else{ - if( !doCheck( m, q, children[i], n[i] ) ){ - bchildren[i] = AbsDef::val_unk; - children.erase( i ); - } - } - } - //convert to pointers - std::map< unsigned, AbsDef * > pchildren; - for( std::map< unsigned, AbsDef >::iterator it = children.begin(); it != children.end(); ++it ){ - pchildren[it->first] = &it->second; - } - //construct the interpretation - Trace("ambqi-check-debug") << "Compute Interpretation of " << n << " " << n.getKind() << std::endl; - if( n.getKind() == APPLY_UF || n.getKind() == VARIABLE || n.getKind() == SKOLEM ){ - Node op; - if( n.getKind() == APPLY_UF ){ - op = n.getOperator(); - }else{ - op = n; - } - //uninterpreted compose - if( m->d_models_valid[op] ){ - ad.construct( m, q, n, m->d_models[op], pchildren, bchildren, vchildren, varChCount ); - }else{ - Trace("ambqi-check-debug") << "** Cannot produce interpretation for " << n << " (no function model)" << std::endl; - return false; - } - }else if( !ad.construct( m, q, n, NULL, pchildren, bchildren, vchildren, varChCount ) ){ - Trace("ambqi-check-debug") << "** Cannot produce interpretation for " << n << " (variables are children of interpreted symbol)" << std::endl; - return false; - } - Trace("ambqi-check-try") << "Interpretation for " << n << " is : " << std::endl; - ad.debugPrint("ambqi-check-try", m, q[0] ); - ad.simplify( m, q, q[0] ); - Trace("ambqi-check-debug") << "(Simplified) Interpretation for " << n << " is : " << std::endl; - ad.debugPrint("ambqi-check-debug", m, q[0] ); - Trace("ambqi-check-debug") << std::endl; - return true; - } -} - -}/* namespace CVC4::theory::quantifiers */ -}/* namespace CVC4::theory */ -}/* namespace CVC4 */ diff --git a/src/theory/quantifiers/fmf/ambqi_builder.h b/src/theory/quantifiers/fmf/ambqi_builder.h deleted file mode 100644 index b052e0985..000000000 --- a/src/theory/quantifiers/fmf/ambqi_builder.h +++ /dev/null @@ -1,105 +0,0 @@ -/********************* */ -/*! \file ambqi_builder.h - ** \verbatim - ** Top contributors (to current version): - ** Andrew Reynolds, Tim King - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2018 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Abstract MBQI model builder class - **/ - -#include "cvc4_private.h" - -#ifndef ABSTRACT_MBQI_BUILDER -#define ABSTRACT_MBQI_BUILDER - -#include "theory/quantifiers/fmf/model_builder.h" -#include "theory/quantifiers/first_order_model.h" - -namespace CVC4 { -namespace theory { -namespace quantifiers { - -class FirstOrderModelAbs; - -//representiation of function and term interpretations -class AbsDef -{ -private: - bool addInstantiations( FirstOrderModelAbs * m, QuantifiersEngine * qe, TNode q, std::vector< Node >& terms, int& inst, unsigned depth ); - void construct_compose( FirstOrderModelAbs * m, TNode q, TNode n, AbsDef * f, - std::map< unsigned, AbsDef * >& children, - std::map< unsigned, int >& bchildren, std::map< unsigned, int >& vchildren, - std::vector< unsigned >& entry, std::vector< bool >& entry_def ); - void construct_entry( std::vector< unsigned >& entry, std::vector< bool >& entry_def, int v, unsigned depth = 0 ); - void construct_def_entry( FirstOrderModelAbs * m, TNode q, TNode n, int v, unsigned depth = 0 ); - void apply_ucompose( FirstOrderModelAbs * m, TNode q, - std::vector< unsigned >& entry, std::vector< bool >& entry_def, std::vector< int >& terms, - std::map< unsigned, int >& vchildren, AbsDef * a, unsigned depth = 0 ); - void construct_var_eq( FirstOrderModelAbs * m, TNode q, unsigned v1, unsigned v2, int curr, int currv, unsigned depth = 0 ); - void construct_var( FirstOrderModelAbs * m, TNode q, unsigned v, int currv, unsigned depth = 0 ); - void get_defs( unsigned u, std::vector< AbsDef * >& defs ); - void construct_normalize( FirstOrderModelAbs * m, TNode q, std::vector< AbsDef * >& defs, unsigned depth = 0 ); -public: - enum { - val_none = -1, - val_unk = -2, - }; - AbsDef() : d_default( 0 ), d_value( -1 ){} - std::map< unsigned, AbsDef > d_def; - unsigned d_default; - int d_value; - - void clear() { d_def.clear(); d_default = 0; d_value = -1; } - AbsDef * getDefault() { return &d_def[d_default]; } - void construct_func( FirstOrderModelAbs * m, std::vector< TNode >& fapps, unsigned depth = 0 ); - void debugPrintUInt( const char * c, unsigned dSize, unsigned u ) const; - void debugPrint( const char * c, FirstOrderModelAbs * m, TNode f, unsigned depth = 0 ) const; - void simplify( FirstOrderModelAbs * m, TNode q, TNode n, unsigned depth = 0 ); - int addInstantiations( FirstOrderModelAbs * m, QuantifiersEngine * qe, Node q, int& inst ){ - std::vector< Node > terms; - terms.resize( q[0].getNumChildren() ); - return addInstantiations( m, qe, q, terms, inst, 0 ); - } - bool construct( FirstOrderModelAbs * m, TNode q, TNode n, AbsDef * f, - std::map< unsigned, AbsDef * >& children, - std::map< unsigned, int >& bchildren, - std::map< unsigned, int >& vchildren, - int varChCount ); - void negate(); - Node getFunctionValue( FirstOrderModelAbs * m, TNode op, std::vector< Node >& vars, unsigned depth = 0 ); - static bool isSimple( unsigned n ); - static unsigned getId( unsigned n, unsigned start=0, unsigned end=32 ); - Node evaluate( FirstOrderModelAbs * m, TypeNode retType, std::vector< Node >& args ); - Node evaluate( FirstOrderModelAbs * m, TypeNode retType, std::vector< unsigned >& iargs, unsigned depth = 0 ); - //for debugging - bool is_normalized(); -}; - -class AbsMbqiBuilder : public QModelBuilder -{ - friend class AbsDef; -private: - Node d_true; - Node d_false; - bool doCheck( FirstOrderModelAbs * m, TNode q, AbsDef & ad, TNode n ); -public: - AbsMbqiBuilder( context::Context* c, QuantifiersEngine* qe ); - - //process build model - bool processBuildModel(TheoryModel* m) override; - //do exhaustive instantiation - int doExhaustiveInstantiation(FirstOrderModel* fm, - Node q, - int effort) override; -}; - -} -} -} - -#endif diff --git a/src/theory/quantifiers/fmf/model_builder.cpp b/src/theory/quantifiers/fmf/model_builder.cpp index c03fc7a32..8ef30fc4d 100644 --- a/src/theory/quantifiers/fmf/model_builder.cpp +++ b/src/theory/quantifiers/fmf/model_builder.cpp @@ -143,687 +143,3 @@ void QModelBuilder::debugModel( TheoryModel* m ){ } } } - -bool TermArgBasisTrie::addTerm(FirstOrderModel* fm, Node n, unsigned argIndex) -{ - if (argIndex < n.getNumChildren()) - { - Node r; - if( n[ argIndex ].getAttribute(ModelBasisAttribute()) ){ - r = n[ argIndex ]; - }else{ - r = fm->getRepresentative( n[ argIndex ] ); - } - std::map< Node, TermArgBasisTrie >::iterator it = d_data.find( r ); - if( it==d_data.end() ){ - d_data[r].addTerm(fm, n, argIndex + 1); - return true; - }else{ - return it->second.addTerm(fm, n, argIndex + 1); - } - }else{ - return false; - } -} - -void QModelBuilderIG::UfModelPreferenceData::setValuePreference(Node q, - Node r, - bool isPro) -{ - if (std::find(d_values.begin(), d_values.end(), r) == d_values.end()) - { - d_values.push_back(r); - } - int index = isPro ? 0 : 1; - if (std::find( - d_value_pro_con[index][r].begin(), d_value_pro_con[index][r].end(), q) - == d_value_pro_con[index][r].end()) - { - d_value_pro_con[index][r].push_back(q); - } -} - -Node QModelBuilderIG::UfModelPreferenceData::getBestDefaultValue( - Node defaultTerm, TheoryModel* m) -{ - Node defaultVal; - double maxScore = -1; - for (size_t i = 0, size = d_values.size(); i < size; i++) - { - Node v = d_values[i]; - double score = (1.0 + static_cast(d_value_pro_con[0][v].size())) - / (1.0 + static_cast(d_value_pro_con[1][v].size())); - Debug("fmf-model-cons-debug") << " - score( "; - Debug("fmf-model-cons-debug") << m->getRepresentative(v); - Debug("fmf-model-cons-debug") << " ) = " << score << std::endl; - if (score > maxScore) - { - defaultVal = v; - maxScore = score; - } - } - if (maxScore < 1.0) - { - // consider finding another value, if possible - Debug("fmf-model-cons-debug") - << "Poor choice for default value, score = " << maxScore << std::endl; - TypeNode tn = defaultTerm.getType(); - Node newDefaultVal = m->getRepSet()->getDomainValue(tn, d_values); - if (!newDefaultVal.isNull()) - { - defaultVal = newDefaultVal; - Debug("fmf-model-cons-debug") << "-> Change default value to "; - Debug("fmf-model-cons-debug") << m->getRepresentative(defaultVal); - Debug("fmf-model-cons-debug") << std::endl; - } - else - { - Debug("fmf-model-cons-debug") - << "-> Could not find arbitrary element of type " - << tn[tn.getNumChildren() - 1] << std::endl; - Debug("fmf-model-cons-debug") << " Excluding: " << d_values; - Debug("fmf-model-cons-debug") << std::endl; - } - } - // get the default term (this term must be defined non-ground in model) - Debug("fmf-model-cons-debug") << " Choose "; - Debug("fmf-model-cons-debug") << m->getRepresentative(defaultVal); - Debug("fmf-model-cons-debug") - << " as default value (" << defaultTerm << ")" << std::endl; - Debug("fmf-model-cons-debug") - << " # quantifiers pro = " << d_value_pro_con[0][defaultVal].size() - << std::endl; - Debug("fmf-model-cons-debug") - << " # quantifiers con = " << d_value_pro_con[1][defaultVal].size() - << std::endl; - return defaultVal; -} - -QModelBuilderIG::QModelBuilderIG(context::Context* c, QuantifiersEngine* qe) - : QModelBuilder(c, qe), - d_didInstGen(false), - d_numQuantSat(0), - d_numQuantInstGen(0), - d_numQuantNoInstGen(0), - d_numQuantNoSelForm(0), - d_instGenMatches(0) {} - -/* -Node QModelBuilderIG::getCurrentUfModelValue( FirstOrderModel* fm, Node n, std::vector< Node > & args, bool partial ) { - return n; -} -*/ - -bool QModelBuilderIG::processBuildModel( TheoryModel* m ) { - if (!m->areFunctionValuesEnabled()) - { - // nothing to do if no functions - return true; - } - FirstOrderModel* f = (FirstOrderModel*)m; - FirstOrderModelIG* fm = f->asFirstOrderModelIG(); - Trace("model-engine-debug") << "Process build model " << optUseModel() << std::endl; - d_didInstGen = false; - //reset the internal information - reset( fm ); - //only construct first order model if optUseModel() is true - if( optUseModel() ){ - Trace("model-engine-debug") << "Initializing " << fm->getNumAssertedQuantifiers() << " quantifiers..." << std::endl; - //check if any quantifiers are un-initialized - for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ - Node q = fm->getAssertedQuantifier( i ); - if( d_qe->getModel()->isQuantifierActive( q ) ){ - int lems = initializeQuantifier(q, q, f); - d_statistics.d_init_inst_gen_lemmas += lems; - d_addedLemmas += lems; - if( d_qe->inConflict() ){ - break; - } - } - } - if( d_addedLemmas>0 ){ - Trace("model-engine") << "Initialize, Added Lemmas = " << d_addedLemmas << std::endl; - return false; - }else{ - Assert( !d_qe->inConflict() ); - //initialize model - fm->initialize(); - //analyze the functions - Trace("model-engine-debug") << "Analyzing model..." << std::endl; - analyzeModel( fm ); - //analyze the quantifiers - Trace("model-engine-debug") << "Analyzing quantifiers..." << std::endl; - d_uf_prefs.clear(); - for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ - Node q = fm->getAssertedQuantifier( i ); - analyzeQuantifier( fm, q ); - } - - //if applicable, find exceptions to model via inst-gen - if( options::fmfInstGen() ){ - d_didInstGen = true; - d_instGenMatches = 0; - d_numQuantSat = 0; - d_numQuantInstGen = 0; - d_numQuantNoInstGen = 0; - d_numQuantNoSelForm = 0; - //now, see if we know that any exceptions via InstGen exist - Trace("model-engine-debug") << "Perform InstGen techniques for quantifiers..." << std::endl; - for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ - Node f = fm->getAssertedQuantifier( i ); - if( d_qe->getModel()->isQuantifierActive( f ) ){ - int lems = doInstGen( fm, f ); - d_statistics.d_inst_gen_lemmas += lems; - d_addedLemmas += lems; - //temporary - if( lems>0 ){ - d_numQuantInstGen++; - }else if( hasInstGen( f ) ){ - d_numQuantNoInstGen++; - }else{ - d_numQuantNoSelForm++; - } - if( d_qe->inConflict() || ( options::fmfInstGenOneQuantPerRound() && lems>0 ) ){ - break; - } - }else{ - d_numQuantSat++; - } - } - Trace("model-engine-debug") << "Quantifiers sat/ig/n-ig/null " << d_numQuantSat << " / " << d_numQuantInstGen << " / "; - Trace("model-engine-debug") << d_numQuantNoInstGen << " / " << d_numQuantNoSelForm << std::endl; - Trace("model-engine-debug") << "Inst-gen # matches examined = " << d_instGenMatches << std::endl; - if( Trace.isOn("model-engine") ){ - if( d_addedLemmas>0 ){ - Trace("model-engine") << "InstGen, added lemmas = " << d_addedLemmas << std::endl; - }else{ - Trace("model-engine") << "No InstGen lemmas..." << std::endl; - } - } - } - //construct the model if necessary - if( d_addedLemmas==0 ){ - //if no immediate exceptions, build the model - // this model will be an approximation that will need to be tested via exhaustive instantiation - Trace("model-engine-debug") << "Building model..." << std::endl; - //build model for UF - for( std::map< Node, uf::UfModelTree >::iterator it = fm->d_uf_model_tree.begin(); it != fm->d_uf_model_tree.end(); ++it ){ - Trace("model-engine-debug-uf") << "Building model for " << it->first << "..." << std::endl; - constructModelUf( fm, it->first ); - } - Trace("model-engine-debug") << "Done building models." << std::endl; - }else{ - return false; - } - } - } - //update models - for( std::map< Node, uf::UfModelTree >::iterator it = fm->d_uf_model_tree.begin(); it != fm->d_uf_model_tree.end(); ++it ){ - it->second.update( fm ); - Trace("model-func") << "QModelBuilder: Make function value from tree " << it->first << std::endl; - //construct function values - Node f_def = it->second.getFunctionValue( "$x" ); - fm->assignFunctionDefinition( it->first, f_def ); - } - Assert( d_addedLemmas==0 ); - return TheoryEngineModelBuilder::processBuildModel( m ); -} - -int QModelBuilderIG::initializeQuantifier(Node f, Node fp, FirstOrderModel* fm) -{ - if( d_quant_basis_match_added.find( f )==d_quant_basis_match_added.end() ){ - //create the basis match if necessary - if( d_quant_basis_match.find( f )==d_quant_basis_match.end() ){ - Trace("inst-fmf-init") << "Initialize " << f << std::endl; - //add the model basis instantiation - // This will help produce the necessary information for model completion. - // We do this by extending distinguish ground assertions (those - // containing terms with "model basis" attribute) to hold for all cases. - - ////first, check if any variables are required to be equal - //for( std::map< Node, bool >::iterator it = d_quantEngine->d_phase_reqs[f].begin(); - // it != d_quantEngine->d_phase_reqs[f].end(); ++it ){ - // Node n = it->first; - // if( n.getKind()==EQUAL && n[0].getKind()==INST_CONSTANT && n[1].getKind()==INST_CONSTANT ){ - // Notice() << "Unhandled phase req: " << n << std::endl; - // } - //} - d_quant_basis_match[f] = InstMatch( f ); - for (unsigned j = 0; j < f[0].getNumChildren(); j++) - { - Node t = fm->getModelBasisTerm(f[0][j].getType()); - //calculate the basis match for f - d_quant_basis_match[f].setValue( j, t ); - } - ++(d_statistics.d_num_quants_init); - } - //try to add it - Trace("inst-fmf-init") << "Init: try to add match " << d_quant_basis_match[f] << std::endl; - //add model basis instantiation - if (d_qe->getInstantiate()->addInstantiation(fp, d_quant_basis_match[f])) - { - d_quant_basis_match_added[f] = true; - return 1; - }else{ - //shouldn't happen usually, but will occur if x != y is a required literal for f. - //Notice() << "No model basis for " << f << std::endl; - d_quant_basis_match_added[f] = false; - } - } - return 0; -} - -void QModelBuilderIG::analyzeModel( FirstOrderModel* fm ){ - FirstOrderModelIG* fmig = fm->asFirstOrderModelIG(); - d_uf_model_constructed.clear(); - //determine if any functions are constant - for( std::map< Node, uf::UfModelTree >::iterator it = fmig->d_uf_model_tree.begin(); it != fmig->d_uf_model_tree.end(); ++it ){ - Node op = it->first; - std::map< Node, std::vector< Node > >::iterator itut = fmig->d_uf_terms.find( op ); - if( itut!=fmig->d_uf_terms.end() ){ - for( size_t i=0; isecond.size(); i++ ){ - Node n = fmig->d_uf_terms[op][i]; - //for calculating if op is constant - Node v = fmig->getRepresentative( n ); - if( i==0 ){ - d_uf_prefs[op].d_const_val = v; - }else if( v!=d_uf_prefs[op].d_const_val ){ - d_uf_prefs[op].d_const_val = Node::null(); - break; - } - } - } - if( !d_uf_prefs[op].d_const_val.isNull() ){ - fmig->d_uf_model_gen[op].setDefaultValue( d_uf_prefs[op].d_const_val ); - fmig->d_uf_model_gen[op].makeModel( fmig, it->second ); - Debug("fmf-model-cons") << "Function " << op << " is the constant function "; - Debug("fmf-model-cons") << d_uf_prefs[op].d_const_val; - Debug("fmf-model-cons") << std::endl; - d_uf_model_constructed[op] = true; - }else{ - d_uf_model_constructed[op] = false; - } - } -} - -bool QModelBuilderIG::hasConstantDefinition( Node n ){ - Node lit = n.getKind()==NOT ? n[0] : n; - if( lit.getKind()==APPLY_UF ){ - Node op = lit.getOperator(); - if( !d_uf_prefs[op].d_const_val.isNull() ){ - return true; - } - } - return false; -} - -QModelBuilderIG::Statistics::Statistics() - : d_num_quants_init("QModelBuilderIG::Number_Quantifiers", 0), - d_num_partial_quants_init("QModelBuilderIG::Number_Partial_Quantifiers", - 0), - d_init_inst_gen_lemmas("QModelBuilderIG::Initialize_Inst_Gen_Lemmas", 0), - d_inst_gen_lemmas("QModelBuilderIG::Inst_Gen_Lemmas", 0) -{ - smtStatisticsRegistry()->registerStat(&d_num_quants_init); - smtStatisticsRegistry()->registerStat(&d_num_partial_quants_init); - smtStatisticsRegistry()->registerStat(&d_init_inst_gen_lemmas); - smtStatisticsRegistry()->registerStat(&d_inst_gen_lemmas); -} - -QModelBuilderIG::Statistics::~Statistics(){ - smtStatisticsRegistry()->unregisterStat(&d_num_quants_init); - smtStatisticsRegistry()->unregisterStat(&d_num_partial_quants_init); - smtStatisticsRegistry()->unregisterStat(&d_init_inst_gen_lemmas); - smtStatisticsRegistry()->unregisterStat(&d_inst_gen_lemmas); -} - -//do exhaustive instantiation -int QModelBuilderIG::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) { - if( optUseModel() ){ - QRepBoundExt qrbe(d_qe); - RepSetIterator riter(d_qe->getModel()->getRepSet(), &qrbe); - if( riter.setQuantifier( f ) ){ - FirstOrderModelIG * fmig = (FirstOrderModelIG*)d_qe->getModel(); - Debug("inst-fmf-ei") << "Reset evaluate..." << std::endl; - fmig->resetEvaluate(); - Debug("inst-fmf-ei") << "Begin instantiation..." << std::endl; - EqualityQuery* qy = d_qe->getEqualityQuery(); - Instantiate* inst = d_qe->getInstantiate(); - TermUtil* util = d_qe->getTermUtil(); - while( !riter.isFinished() && ( d_addedLemmas==0 || !options::fmfOneInstPerRound() ) ){ - d_triedLemmas++; - if( Debug.isOn("inst-fmf-ei-debug") ){ - for( int i=0; i<(int)riter.d_index.size(); i++ ){ - Debug("inst-fmf-ei-debug") << i << " : " << riter.d_index[i] << " : " << riter.getCurrentTerm( i ) << std::endl; - } - } - int eval = 0; - int depIndex; - //see if instantiation is already true in current model - if( Debug.isOn("fmf-model-eval") ){ - Debug("fmf-model-eval") << "Evaluating "; - riter.debugPrintSmall("fmf-model-eval"); - Debug("fmf-model-eval") << "Done calculating terms." << std::endl; - } - //if evaluate(...)==1, then the instantiation is already true in the model - // depIndex is the index of the least significant variable that this evaluation relies upon - depIndex = riter.getNumTerms()-1; - Debug("fmf-model-eval") << "We will evaluate " - << util->getInstConstantBody(f) << std::endl; - eval = fmig->evaluate(util->getInstConstantBody(f), depIndex, &riter); - if( eval==1 ){ - Debug("fmf-model-eval") << " Returned success with depIndex = " << depIndex << std::endl; - }else{ - Debug("fmf-model-eval") << " Returned " << (eval==-1 ? "failure" : "unknown") << ", depIndex = " << depIndex << std::endl; - } - if( eval==1 ){ - //instantiation is already true -> skip - riter.incrementAtIndex(depIndex); - }else{ - //instantiation was not shown to be true, construct the match - InstMatch m( f ); - for (unsigned i = 0; i < riter.getNumTerms(); i++) - { - m.set(qy, i, riter.getCurrentTerm(i)); - } - Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl; - //add as instantiation - if (inst->addInstantiation(f, m, true)) - { - d_addedLemmas++; - if( d_qe->inConflict() ){ - break; - } - //if the instantiation is show to be false, and we wish to skip multiple instantiations at once - if( eval==-1 ){ - riter.incrementAtIndex(depIndex); - }else{ - riter.increment(); - } - }else{ - Debug("fmf-model-eval") << "* Failed Add instantiation " << m << std::endl; - riter.increment(); - } - } - } - //print debugging information - Trace("inst-fmf-ei") << "For " << f << ", finished: " << std::endl; - Trace("inst-fmf-ei") << " Inst Tried: " << d_triedLemmas << std::endl; - Trace("inst-fmf-ei") << " Inst Added: " << d_addedLemmas << std::endl; - if( d_addedLemmas>1000 ){ - Trace("model-engine-warn") << "WARNING: many instantiations produced for " << f << ": " << std::endl; - Trace("model-engine-warn") << " Inst Tried: " << d_triedLemmas << std::endl; - Trace("model-engine-warn") << " Inst Added: " << d_addedLemmas << std::endl; - Trace("model-engine-warn") << std::endl; - } - } - //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round - return riter.isIncomplete() ? -1 : 1; - }else{ - return 0; - } -} - - - -void QModelBuilderDefault::reset( FirstOrderModel* fm ){ - d_quant_selection_lit.clear(); - d_quant_selection_lit_candidates.clear(); - d_quant_selection_lit_terms.clear(); - d_term_selection_lit.clear(); - d_op_selection_terms.clear(); -} - - -int QModelBuilderDefault::getSelectionScore( std::vector< Node >& uf_terms ) { - /* - size_t maxChildren = 0; - for( size_t i=0; imaxChildren ){ - maxChildren = uf_terms[i].getNumChildren(); - } - } - //TODO: look at how many entries they have? - return (int)maxChildren; - */ - return 0; -} - -void QModelBuilderDefault::analyzeQuantifier( FirstOrderModel* fm, Node f ){ - if( d_qe->getModel()->isQuantifierActive( f ) ){ - FirstOrderModelIG* fmig = fm->asFirstOrderModelIG(); - Debug("fmf-model-prefs") << "Analyze quantifier " << f << std::endl; - //the pro/con preferences for this quantifier - std::vector< Node > pro_con[2]; - //the terms in the selection literal we choose - std::vector< Node > selectionLitTerms; - Trace("inst-gen-debug-quant") << "Inst-gen analyze " << f << std::endl; - //for each asserted quantifier f, - // - determine selection literals - // - check which function/predicates have good and bad definitions for satisfying f - if( d_phase_reqs.find( f )==d_phase_reqs.end() ){ - d_phase_reqs[f].initialize( d_qe->getTermUtil()->getInstConstantBody( f ), true ); - } - int selectLitScore = -1; - for( std::map< Node, bool >::iterator it = d_phase_reqs[f].d_phase_reqs.begin(); it != d_phase_reqs[f].d_phase_reqs.end(); ++it ){ - //the literal n is phase-required for quantifier f - Node n = it->first; - Node gn = fm->getModelBasis(f, n); - Debug("fmf-model-req") << " Req: " << n << " -> " << it->second << std::endl; - bool value; - //if the corresponding ground abstraction literal has a SAT value - if( d_qe->getValuation().hasSatValue( gn, value ) ){ - //collect the non-ground uf terms that this literal contains - // and compute if all of the symbols in this literal have - // constant definitions. - bool isConst = true; - std::vector< Node > uf_terms; - if( TermUtil::hasInstConstAttr(n) ){ - isConst = false; - if( gn.getKind()==APPLY_UF ){ - uf_terms.push_back( gn ); - isConst = hasConstantDefinition( gn ); - }else if( gn.getKind()==EQUAL ){ - isConst = true; - for( int j=0; j<2; j++ ){ - if( TermUtil::hasInstConstAttr(n[j]) ){ - if( n[j].getKind()==APPLY_UF && - fmig->d_uf_model_tree.find( gn[j].getOperator() )!=fmig->d_uf_model_tree.end() ){ - uf_terms.push_back( gn[j] ); - isConst = isConst && hasConstantDefinition( gn[j] ); - }else{ - isConst = false; - } - } - } - } - } - //check if the value in the SAT solver matches the preference according to the quantifier - int pref = 0; - if( value!=it->second ){ - //we have a possible selection literal - bool selectLit = d_quant_selection_lit[f].isNull(); - bool selectLitConstraints = true; - //it is a constantly defined selection literal : the quantifier is sat - if( isConst ){ - selectLit = selectLit || d_qe->getModel()->isQuantifierActive( f ); - d_qe->getModel()->setQuantifierActive( f, false ); - //check if choosing this literal would add any additional constraints to default definitions - selectLitConstraints = false; - selectLit = true; - } - //also check if it is naturally a better literal - if( !selectLit ){ - int score = getSelectionScore( uf_terms ); - //Trace("inst-gen-debug") << "Check " << score << " < " << selectLitScore << std::endl; - selectLit = scoregetModel()->isQuantifierActive( f ) ){ - Debug("fmf-model-prefs") << " It is " << ( pref==1 ? "pro" : "con" ); - Debug("fmf-model-prefs") << " the definition of " << n << std::endl; - for( int j=0; j<(int)uf_terms.size(); j++ ){ - pro_con[ pref==1 ? 0 : 1 ].push_back( uf_terms[j] ); - } - } - } - } - //process information about selection literal for f - if( !d_quant_selection_lit[f].isNull() ){ - d_quant_selection_lit_terms[f].insert( d_quant_selection_lit_terms[f].begin(), selectionLitTerms.begin(), selectionLitTerms.end() ); - for( int i=0; i<(int)selectionLitTerms.size(); i++ ){ - d_term_selection_lit[ selectionLitTerms[i] ] = d_quant_selection_lit[f]; - d_op_selection_terms[ selectionLitTerms[i].getOperator() ].push_back( selectionLitTerms[i] ); - } - }else{ - Trace("inst-gen-warn") << "WARNING: " << f << " has no selection literals" << std::endl; - } - //process information about requirements and preferences of quantifier f - if( !d_qe->getModel()->isQuantifierActive( f ) ){ - Debug("fmf-model-prefs") << " * Constant SAT due to definition of ops: "; - for( int i=0; i<(int)selectionLitTerms.size(); i++ ){ - Debug("fmf-model-prefs") << selectionLitTerms[i] << " "; - } - Debug("fmf-model-prefs") << std::endl; - }else{ - //note quantifier's value preferences to models - for( int k=0; k<2; k++ ){ - for( int j=0; j<(int)pro_con[k].size(); j++ ){ - Node op = pro_con[k][j].getOperator(); - Node r = fmig->getRepresentative( pro_con[k][j] ); - d_uf_prefs[op].setValuePreference(f, r, k == 0); - } - } - } - } -} - -int QModelBuilderDefault::doInstGen( FirstOrderModel* fm, Node f ){ - int addedLemmas = 0; - //we wish to add all known exceptions to our selection literal for f. this will help to refine our current model. - //This step is advantageous over exhaustive instantiation, since we are adding instantiations that involve model basis terms, - // effectively acting as partial instantiations instead of pointwise instantiations. - if( !d_quant_selection_lit[f].isNull() ){ - Trace("inst-gen") << "Do Inst-Gen for " << f << std::endl; - for( size_t i=0; i tr_terms; - if( lit.getKind()==APPLY_UF ){ - //only match predicates that are contrary to this one, use literal matching - Node eq = NodeManager::currentNM()->mkNode( - EQUAL, lit, NodeManager::currentNM()->mkConst(!phase)); - tr_terms.push_back( eq ); - }else if( lit.getKind()==EQUAL ){ - //collect trigger terms - for( int j=0; j<2; j++ ){ - if( TermUtil::hasInstConstAttr(lit[j]) ){ - if( lit[j].getKind()==APPLY_UF ){ - tr_terms.push_back( lit[j] ); - }else{ - tr_terms.clear(); - break; - } - } - } - if( tr_terms.size()==1 && !phase ){ - //equality between a function and a ground term, use literal matching - tr_terms.clear(); - tr_terms.push_back( lit ); - } - } - //if applicable, try to add exceptions here - if( !tr_terms.empty() ){ - //make a trigger for these terms, add instantiations - inst::Trigger* tr = inst::Trigger::mkTrigger( d_qe, f, tr_terms, true, inst::Trigger::TR_MAKE_NEW ); - //Notice() << "Trigger = " << (*tr) << std::endl; - tr->resetInstantiationRound(); - tr->reset( Node::null() ); - //d_qe->d_optInstMakeRepresentative = false; - //d_qe->d_optMatchIgnoreModelBasis = true; - addedLemmas += tr->addInstantiations(); - } - } - } - return addedLemmas; -} - -void QModelBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op ){ - FirstOrderModelIG* fmig = fm->asFirstOrderModelIG(); - if( !d_uf_model_constructed[op] ){ - //construct the model for the uninterpretted function/predicate - bool setDefaultVal = true; - Node defaultTerm = fmig->getModelBasisOpTerm(op); - Trace("fmf-model-cons") << "Construct model for " << op << "..." << std::endl; - //set the values in the model - std::map< Node, std::vector< Node > >::iterator itut = fmig->d_uf_terms.find( op ); - if( itut!=fmig->d_uf_terms.end() ){ - for( size_t i=0; isecond.size(); i++ ){ - Node n = itut->second[i]; - // only consider unique up to congruence (in model equality engine)? - Node v = fmig->getRepresentative( n ); - Trace("fmf-model-cons") << "Set term " << n << " : " - << fmig->getRepSet()->getIndexFor(v) << " " << v - << std::endl; - //if this assertion did not help the model, just consider it ground - //set n = v in the model tree - //set it as ground value - fmig->d_uf_model_gen[op].setValue( fm, n, v ); - // also set as default value if necessary - if (n.hasAttribute(ModelBasisArgAttribute()) - && n.getAttribute(ModelBasisArgAttribute()) != 0) - { - Trace("fmf-model-cons") << " Set as default." << std::endl; - fmig->d_uf_model_gen[op].setValue(fm, n, v, false); - if( n==defaultTerm ){ - //incidentally already set, we will not need to find a default value - setDefaultVal = false; - } - } - } - } - //set the overall default value if not set already (is this necessary??) - if( setDefaultVal ){ - Trace("fmf-model-cons") << " Choose default value..." << std::endl; - //chose defaultVal based on heuristic, currently the best ratio of "pro" responses - Node defaultVal = d_uf_prefs[op].getBestDefaultValue( defaultTerm, fm ); - if( defaultVal.isNull() ){ - if (!fmig->getRepSet()->hasType(defaultTerm.getType())) - { - Node mbt = fmig->getModelBasisTerm(defaultTerm.getType()); - fmig->getRepSetPtr()->d_type_reps[defaultTerm.getType()].push_back( - mbt); - } - defaultVal = - fmig->getRepSet()->getRepresentative(defaultTerm.getType(), 0); - } - Assert( !defaultVal.isNull() ); - Trace("fmf-model-cons") - << "Set default term : " << fmig->getRepSet()->getIndexFor(defaultVal) - << std::endl; - fmig->d_uf_model_gen[op].setValue( fm, defaultTerm, defaultVal, false ); - } - Debug("fmf-model-cons") << " Making model..."; - fmig->d_uf_model_gen[op].makeModel( fm, fmig->d_uf_model_tree[op] ); - d_uf_model_constructed[op] = true; - Debug("fmf-model-cons") << " Finished constructing model for " << op << "." << std::endl; - } -} diff --git a/src/theory/quantifiers/fmf/model_builder.h b/src/theory/quantifiers/fmf/model_builder.h index b34f1e580..b73716169 100644 --- a/src/theory/quantifiers/fmf/model_builder.h +++ b/src/theory/quantifiers/fmf/model_builder.h @@ -56,163 +56,6 @@ public: unsigned getNumTriedLemmas() { return d_triedLemmas; } }; -class TermArgBasisTrie { -public: - /** the data */ - std::map< Node, TermArgBasisTrie > d_data; - /** add term to the trie */ - bool addTerm(FirstOrderModel* fm, Node n, unsigned argIndex = 0); -};/* class TermArgBasisTrie */ - -/** model builder class - * This class is capable of building candidate models based on the current quantified formulas - * that are asserted. Use: - * (1) call QModelBuilder::buildModel( m, false );, where m is a FirstOrderModel - * (2) if candidate model is determined to be a real model, - then call QModelBuilder::buildModel( m, true ); - */ -class QModelBuilderIG : public QModelBuilder -{ - typedef context::CDHashMap BoolMap; - - protected: - /** - * This class stores temporary information useful to model engine for - * constructing models for uninterpreted functions. - */ - class UfModelPreferenceData - { - public: - UfModelPreferenceData() {} - virtual ~UfModelPreferenceData() {} - /** any constant value of the type */ - Node d_const_val; - /** list of possible default values */ - std::vector d_values; - /** - * Map from values to the set of quantified formulas that are (pro, con) - * that value. A quantified formula may be "pro" a particular default - * value of an uninterpreted function if that value is likely to satisfy - * many points in its domain. For example, forall x. P( f( x ) ) may be - * "pro" the default value true for P. - */ - std::map > d_value_pro_con[2]; - /** set that quantified formula q is pro/con the default value of r */ - void setValuePreference(Node q, Node r, bool isPro); - /** get best default value */ - Node getBestDefaultValue(Node defaultTerm, TheoryModel* m); - }; - /** map from operators to model preference data */ - std::map d_uf_prefs; - //built model uf - std::map< Node, bool > d_uf_model_constructed; - //whether inst gen was done - bool d_didInstGen; - /** process build model */ - bool processBuildModel(TheoryModel* m) override; - - protected: - //reset - virtual void reset( FirstOrderModel* fm ) = 0; - //initialize quantifiers, return number of lemmas produced - virtual int initializeQuantifier(Node f, Node fp, FirstOrderModel* fm); - //analyze model - virtual void analyzeModel( FirstOrderModel* fm ); - //analyze quantifiers - virtual void analyzeQuantifier( FirstOrderModel* fm, Node f ) = 0; - //do InstGen techniques for quantifier, return number of lemmas produced - virtual int doInstGen( FirstOrderModel* fm, Node f ) = 0; - //theory-specific build models - virtual void constructModelUf( FirstOrderModel* fm, Node op ) = 0; - - protected: - //map from quantifiers to if are SAT - //std::map< Node, bool > d_quant_sat; - //which quantifiers have been initialized - std::map< Node, bool > d_quant_basis_match_added; - //map from quantifiers to model basis match - std::map< Node, InstMatch > d_quant_basis_match; - - protected: // helper functions - /** term has constant definition */ - bool hasConstantDefinition( Node n ); - - public: - QModelBuilderIG( context::Context* c, QuantifiersEngine* qe ); - - public: - /** statistics class */ - class Statistics { - public: - IntStat d_num_quants_init; - IntStat d_num_partial_quants_init; - IntStat d_init_inst_gen_lemmas; - IntStat d_inst_gen_lemmas; - Statistics(); - ~Statistics(); - }; - Statistics d_statistics; - // is term selected - virtual bool isTermSelected( Node n ) { return false; } - /** quantifier has inst-gen definition */ - virtual bool hasInstGen( Node f ) = 0; - /** did inst gen this round? */ - bool didInstGen() { return d_didInstGen; } - // is quantifier active? - bool isQuantifierActive( Node f ); - //do exhaustive instantiation - int doExhaustiveInstantiation(FirstOrderModel* fm, - Node f, - int effort) override; - - //temporary stats - int d_numQuantSat; - int d_numQuantInstGen; - int d_numQuantNoInstGen; - int d_numQuantNoSelForm; - //temporary stat - int d_instGenMatches; -};/* class QModelBuilder */ - - -class QModelBuilderDefault : public QModelBuilderIG -{ - private: /// information for (old) InstGen - // map from quantifiers to their selection literals - std::map< Node, Node > d_quant_selection_lit; - std::map< Node, std::vector< Node > > d_quant_selection_lit_candidates; - //map from quantifiers to their selection literal terms - std::map< Node, std::vector< Node > > d_quant_selection_lit_terms; - //map from terms to the selection literals they exist in - std::map< Node, Node > d_term_selection_lit; - //map from operators to terms that appear in selection literals - std::map< Node, std::vector< Node > > d_op_selection_terms; - //get selection score - int getSelectionScore( std::vector< Node >& uf_terms ); - - protected: - //reset - void reset(FirstOrderModel* fm) override; - //analyze quantifier - void analyzeQuantifier(FirstOrderModel* fm, Node f) override; - //do InstGen techniques for quantifier, return number of lemmas produced - int doInstGen(FirstOrderModel* fm, Node f) override; - //theory-specific build models - void constructModelUf(FirstOrderModel* fm, Node op) override; - - protected: - std::map< Node, QuantPhaseReq > d_phase_reqs; - - public: - QModelBuilderDefault( context::Context* c, QuantifiersEngine* qe ) : QModelBuilderIG( c, qe ){} - - //has inst gen - bool hasInstGen(Node f) override - { - return !d_quant_selection_lit[f].isNull(); - } -}; - }/* CVC4::theory::quantifiers namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/quantifiers/fmf/model_engine.cpp b/src/theory/quantifiers/fmf/model_engine.cpp index 81ecf9e77..d2579b4ee 100644 --- a/src/theory/quantifiers/fmf/model_engine.cpp +++ b/src/theory/quantifiers/fmf/model_engine.cpp @@ -15,7 +15,6 @@ #include "theory/quantifiers/fmf/model_engine.h" #include "options/quantifiers_options.h" -#include "theory/quantifiers/fmf/ambqi_builder.h" #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/fmf/full_model_check.h" #include "theory/quantifiers/instantiate.h" diff --git a/src/theory/quantifiers_engine.cpp b/src/theory/quantifiers_engine.cpp index 320f50afb..433621d31 100644 --- a/src/theory/quantifiers_engine.cpp +++ b/src/theory/quantifiers_engine.cpp @@ -30,7 +30,6 @@ #include "theory/quantifiers/equality_infer.h" #include "theory/quantifiers/equality_query.h" #include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/fmf/ambqi_builder.h" #include "theory/quantifiers/fmf/bounded_integers.h" #include "theory/quantifiers/fmf/full_model_check.h" #include "theory/quantifiers/fmf/model_engine.h" @@ -249,20 +248,14 @@ QuantifiersEngine::QuantifiersEngine(context::Context* c, d_model.reset(new quantifiers::fmcheck::FirstOrderModelFmc( this, c, "FirstOrderModelFmc")); d_builder.reset(new quantifiers::fmcheck::FullModelChecker(c, this)); - }else if( options::mbqiMode()==quantifiers::MBQI_ABS ){ - Trace("quant-engine-debug") << "...make abs mbqi builder." << std::endl; - d_model.reset( - new quantifiers::FirstOrderModelAbs(this, c, "FirstOrderModelAbs")); - d_builder.reset(new quantifiers::AbsMbqiBuilder(c, this)); }else{ Trace("quant-engine-debug") << "...make default model builder." << std::endl; d_model.reset( - new quantifiers::FirstOrderModelIG(this, c, "FirstOrderModelIG")); - d_builder.reset(new quantifiers::QModelBuilderDefault(c, this)); + new quantifiers::FirstOrderModel(this, c, "FirstOrderModel")); + d_builder.reset(new quantifiers::QModelBuilder(c, this)); } }else{ - d_model.reset( - new quantifiers::FirstOrderModelIG(this, c, "FirstOrderModelIG")); + d_model.reset(new quantifiers::FirstOrderModel(this, c, "FirstOrderModel")); } } diff --git a/src/theory/uf/theory_uf_model.cpp b/src/theory/uf/theory_uf_model.cpp index a3e058569..42847dfd4 100644 --- a/src/theory/uf/theory_uf_model.cpp +++ b/src/theory/uf/theory_uf_model.cpp @@ -37,17 +37,6 @@ void UfModelTreeNode::clear(){ d_value = Node::null(); } -bool UfModelTreeNode::hasConcreteArgumentDefinition(){ - if( d_data.size()>1 ){ - return true; - }else if( d_data.empty() ){ - return false; - }else{ - Node r; - return d_data.find( r )==d_data.end(); - } -} - //set value function void UfModelTreeNode::setValue( TheoryModel* m, Node n, Node v, std::vector< int >& indexOrder, bool ground, int argIndex ){ if( d_data.empty() ){ @@ -67,75 +56,6 @@ void UfModelTreeNode::setValue( TheoryModel* m, Node n, Node v, std::vector< int } } -//get value function -Node UfModelTreeNode::getValue( TheoryModel* m, Node n, std::vector< int >& indexOrder, int& depIndex, int argIndex ){ - if( !d_value.isNull() && isTotal( n.getOperator(), argIndex ) ){ - //Notice() << "Constant, return " << d_value << ", depIndex = " << argIndex << std::endl; - depIndex = argIndex; - return d_value; - }else{ - Node val; - int childDepIndex[2] = { argIndex, argIndex }; - for( int i=0; i<2; i++ ){ - //first check the argument, then check default - Node r; - if( i==0 ){ - r = m->getRepresentative( n[ indexOrder[argIndex] ] ); - } - std::map< Node, UfModelTreeNode >::iterator it = d_data.find( r ); - if( it!=d_data.end() ){ - val = it->second.getValue( m, n, indexOrder, childDepIndex[i], argIndex+1 ); - if( !val.isNull() ){ - break; - } - }else{ - //argument is not a defined argument: thus, it depends on this argument - childDepIndex[i] = argIndex+1; - } - } - //update depIndex - depIndex = childDepIndex[0]>childDepIndex[1] ? childDepIndex[0] : childDepIndex[1]; - //Notice() << "Return " << val << ", depIndex = " << depIndex; - //Notice() << " ( " << childDepIndex[0] << ", " << childDepIndex[1] << " )" << std::endl; - return val; - } -} - -Node UfModelTreeNode::getValue( TheoryModel* m, Node n, std::vector< int >& indexOrder, std::vector< int >& depIndex, int argIndex ){ - if( argIndex==(int)indexOrder.size() ){ - return d_value; - }else{ - Node val; - bool depArg = false; - //will try concrete value first, then default - for( int i=0; i<2; i++ ){ - Node r; - if( i==0 ){ - r = m->getRepresentative( n[ indexOrder[argIndex] ] ); - } - std::map< Node, UfModelTreeNode >::iterator it = d_data.find( r ); - if( it!=d_data.end() ){ - val = it->second.getValue( m, n, indexOrder, depIndex, argIndex+1 ); - //we have found a value - if( !val.isNull() ){ - if( i==0 ){ - depArg = true; - } - break; - } - } - } - //it depends on this argument if we found it via concrete argument value, - // or if found by default/disequal from some concrete argument value(s). - if( depArg || hasConcreteArgumentDefinition() ){ - if( std::find( depIndex.begin(), depIndex.end(), indexOrder[argIndex] )==depIndex.end() ){ - depIndex.push_back( indexOrder[argIndex] ); - } - } - return val; - } -} - Node UfModelTreeNode::getFunctionValue(std::vector& args, int index, Node argDefaultValue, bool simplify) { if(!d_data.empty()) { Node defaultValue = argDefaultValue; @@ -264,10 +184,6 @@ bool UfModelTreeNode::isTotal( Node op, int argIndex ){ } } -Node UfModelTreeNode::getConstantValue( TheoryModel* m, Node n, std::vector< int >& indexOrder, int argIndex ){ - return d_value; -} - void indent( std::ostream& out, int ind ){ for( int i=0; i d_data; /** the value of this tree node (if all paths lead to same value) */ Node d_value; - /** has concrete argument defintion */ - bool hasConcreteArgumentDefinition(); public: //is this model tree empty? bool isEmpty() { return d_data.empty() && d_value.isNull(); } @@ -40,11 +38,6 @@ public: void clear(); /** setValue function */ void setValue( TheoryModel* m, Node n, Node v, std::vector< int >& indexOrder, bool ground, int argIndex ); - /** getValue function */ - Node getValue( TheoryModel* m, Node n, std::vector< int >& indexOrder, int& depIndex, int argIndex ); - Node getValue( TheoryModel* m, Node n, std::vector< int >& indexOrder, std::vector< int >& depIndex, int argIndex ); - /** getConstant Value function */ - Node getConstantValue( TheoryModel* m, Node n, std::vector< int >& indexOrder, int argIndex ); /** getFunctionValue */ Node getFunctionValue( std::vector< Node >& args, int index, Node argDefaultValue, bool simplify = true ); /** update function */ @@ -92,36 +85,6 @@ public: void setDefaultValue( TheoryModel* m, Node v ){ d_tree.setValue( m, Node::null(), v, d_index_order, false, 0 ); } - /** getValue function - * - * returns val, the value of ground term n - * Say n is f( t_0...t_n ) - * depIndex is the index for which every term of the form f( t_0 ... t_depIndex, *,... * ) is equal to val - * for example, if g( x_0, x_1, x_2 ) := lambda x_0 x_1 x_2. if( x_1==a ) b else c, - * then g( a, a, a ) would return b with depIndex = 1 - * - */ - Node getValue( TheoryModel* m, Node n, int& depIndex ){ - return d_tree.getValue( m, n, d_index_order, depIndex, 0 ); - } - /** -> implementation incomplete */ - Node getValue( TheoryModel* m, Node n, std::vector< int >& depIndex ){ - return d_tree.getValue( m, n, d_index_order, depIndex, 0 ); - } - /** getConstantValue function - * - * given term n, where n may contain "all value" arguments, aka model basis arguments - * if n is null, then every argument of n is considered "all value" - * if n is constant for the entire domain specified by n, then this function returns the value of its domain - * otherwise, it returns null - * for example, say the term e represents "all values" - * if f( x_0, x_1 ) := if( x_0 = a ) b else if( x_1 = a ) a else b, - * then f( a, e ) would return b, while f( e, a ) would return null - * -> implementation incomplete - */ - Node getConstantValue( TheoryModel* m, Node n ) { - return d_tree.getConstantValue( m, n, d_index_order, 0 ); - } /** getFunctionValue * Returns a representation of this function. */ @@ -136,8 +99,6 @@ public: void simplify() { d_tree.simplify( d_op, Node::null(), 0 ); } /** is this tree total? */ bool isTotal() { return d_tree.isTotal( d_op, 0 ); } - /** is this function constant? */ - bool isConstant( TheoryModel* m ) { return !getConstantValue( m, Node::null() ).isNull(); } /** is this tree empty? */ bool isEmpty() { return d_tree.isEmpty(); } public: diff --git a/test/regress/regress0/fmf/Arrow_Order-smtlib.778341.smt b/test/regress/regress0/fmf/Arrow_Order-smtlib.778341.smt index e8c7949dc..bb2630b93 100644 --- a/test/regress/regress0/fmf/Arrow_Order-smtlib.778341.smt +++ b/test/regress/regress0/fmf/Arrow_Order-smtlib.778341.smt @@ -1,4 +1,4 @@ -; COMMAND-LINE: --finite-model-find --mbqi=gen-ev +; COMMAND-LINE: --finite-model-find ; EXPECT: unsat (benchmark Isabelle :status sat diff --git a/test/regress/regress0/fmf/QEpres-uf.855035.smt b/test/regress/regress0/fmf/QEpres-uf.855035.smt index 4fe592638..97a585090 100644 --- a/test/regress/regress0/fmf/QEpres-uf.855035.smt +++ b/test/regress/regress0/fmf/QEpres-uf.855035.smt @@ -1,4 +1,4 @@ -; COMMAND-LINE: --finite-model-find --mbqi=gen-ev +; COMMAND-LINE: --finite-model-find ; EXPECT: sat (benchmark Isabelle :status sat diff --git a/test/regress/regress1/fmf/nlp042+1.smt2 b/test/regress/regress1/fmf/nlp042+1.smt2 index 567a3c0b7..6159f0b41 100644 --- a/test/regress/regress1/fmf/nlp042+1.smt2 +++ b/test/regress/regress1/fmf/nlp042+1.smt2 @@ -1,4 +1,4 @@ -; COMMAND-LINE: --finite-model-find --mbqi=abs --no-check-models +; COMMAND-LINE: --finite-model-find --no-check-models ; EXPECT: sat (set-logic UF) (set-info :status sat) -- cgit v1.2.3 From 3687e098f4b6a969d265641e413ab05117bf53a7 Mon Sep 17 00:00:00 2001 From: Alex Ozdemir Date: Tue, 11 Dec 2018 17:19:07 -0800 Subject: [LRAT] signature robust against duplicate literals (#2743) * [LRAT] signature robust against duplicate literals The LRAT signature previously had complex, surprising, and occasionally incorrect behavior when given clauses with duplicate literals. Now it does not. Now clauses have true set semantics, and clauses with duplicate literals are treated identically to those without. * Test with logically = but structurally != clauses. --- proofs/signatures/lrat.plf | 39 ++++++++-- proofs/signatures/lrat_test.plf | 155 +++++++++++++++++++++++++++++++++------- 2 files changed, 163 insertions(+), 31 deletions(-) diff --git a/proofs/signatures/lrat.plf b/proofs/signatures/lrat.plf index f5af891bc..96cdd83b3 100644 --- a/proofs/signatures/lrat.plf +++ b/proofs/signatures/lrat.plf @@ -129,6 +129,28 @@ (ff (clause_contains_lit c' l)))) (cln ff))) +; Returns a copy of `c` with any duplicate literals removed. +; Never fails. +; Uses marks 3 & 4. Expects them to be clear before hand, and leaves them clear +; afterwards. +(program clause_dedup ((c clause)) clause + (match c + (cln cln) + ((clc l rest) + (match l + ((pos v) (ifmarked3 + v + (clause_dedup rest) + (do (markvar3 v) + (let result (clc (pos v) (clause_dedup rest)) + (do (markvar3 v) result))))) + ((neg v) (ifmarked4 + v + (clause_dedup rest) + (do (markvar4 v) + (let result (clc (neg v) (clause_dedup rest)) + (do (markvar4 v) result))))))))) + ; Append two traces (program Trace_concat ((t1 Trace) (t2 Trace)) Trace (match t1 @@ -473,9 +495,9 @@ (RATHintsn CTPn) ((RATHintsc i ht hints') (CTPc - (clause_append c + (clause_dedup (clause_append c (clause_remove_all (lit_flip (clause_head c)) - (CMap_get i cs))) + (CMap_get i cs)))) (Trace_concat t ht) (construct_ct_pairs cs c t hints'))))) @@ -534,17 +556,22 @@ (LRATProofn ff)) ) + ; Proof of a CMap from clause proofs. ; The idx is unelidable b/c it is unspecified. +; Robust against clauses with duplicat literals, but not against tautological +; clauses. (declare CMap_holds (! c CMap type)) (declare CMapn_proof (CMap_holds CMapn)) (declare CMapc_proof (! idx mpz ; Not elidable! (! c clause - (! rest CMap - (! proof_c (holds c) - ( ! proof_rest (CMap_holds rest) - (CMap_holds (CMapc idx c rest)))))))) + (! deduped_c clause + (! rest CMap + (! proof_c (holds c) + (! proof_rest (CMap_holds rest) + (! sc (^ (clause_dedup c) deduped_c) + (CMap_holds (CMapc idx deduped_c rest)))))))))) (define bottom (holds cln)) (declare lrat_proof_of_bottom diff --git a/proofs/signatures/lrat_test.plf b/proofs/signatures/lrat_test.plf index 3ba785507..0663a08f7 100644 --- a/proofs/signatures/lrat_test.plf +++ b/proofs/signatures/lrat_test.plf @@ -637,14 +637,66 @@ (% pf_c7 (holds (clc (neg v1) (clc (pos v2) (clc (pos v4) cln)))) (% pf_c8 (holds (clc (pos v1) (clc (neg v2) (clc (neg v4) cln)))) (@ pf_cmap - (CMapc_proof 1 _ _ pf_c1 - (CMapc_proof 2 _ _ pf_c2 - (CMapc_proof 3 _ _ pf_c3 - (CMapc_proof 4 _ _ pf_c4 - (CMapc_proof 5 _ _ pf_c5 - (CMapc_proof 6 _ _ pf_c6 - (CMapc_proof 7 _ _ pf_c7 - (CMapc_proof 8 _ _ pf_c8 + (CMapc_proof 1 _ _ _ pf_c1 + (CMapc_proof 2 _ _ _ pf_c2 + (CMapc_proof 3 _ _ _ pf_c3 + (CMapc_proof 4 _ _ _ pf_c4 + (CMapc_proof 5 _ _ _ pf_c5 + (CMapc_proof 6 _ _ _ pf_c6 + (CMapc_proof 7 _ _ _ pf_c7 + (CMapc_proof 8 _ _ _ pf_c8 + CMapn_proof)))))))) + (@ lrat_proof_witness + (LRATProofa 9 + (clc (pos v1) cln) + Tracen + (RATHintsc 2 (Tracec 6 (Tracec 8 Tracen)) + (RATHintsc 5 (Tracec 1 (Tracec 8 Tracen)) + (RATHintsc 7 (Tracec 6 (Tracec 1 Tracen)) + RATHintsn))) + (LRATProofd (CIListc 1 (CIListc 6 (CIListc 8 CIListn))) + (LRATProofa 10 + (clc (pos v2) cln) + (Tracec 9 (Tracec 7 (Tracec 5 (Tracec 3 Tracen)))) + RATHintsn + (LRATProofd (CIListc 3 (CIListc 7 CIListn)) + (LRATProofa 11 + cln + (Tracec 9 (Tracec 10 (Tracec 2 (Tracec 4 (Tracec 5 Tracen))))) + RATHintsn + LRATProofn))))) + (: + (holds cln) + (lrat_proof_of_bottom _ pf_cmap lrat_proof_witness)) + )) + )))))))) + )))) +) + +; Proof from Figure 2 of "Efficient Certified RAT Verification" +; With duplicates +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (% pf_c1 (holds (clc (pos v1) (clc (pos v1) (clc (pos v2) (clc (neg v3) cln))))) + (% pf_c2 (holds (clc (neg v1) (clc (neg v2) (clc (pos v3) cln)))) + (% pf_c3 (holds (clc (pos v2) (clc (pos v3) (clc (pos v3) (clc (pos v3) (clc (neg v4) cln)))))) + (% pf_c4 (holds (clc (neg v2) (clc (neg v3) (clc (pos v4) cln)))) + (% pf_c5 (holds (clc (neg v1) (clc (neg v3) (clc (neg v4) (clc (neg v4) cln))))) + (% pf_c6 (holds (clc (pos v1) (clc (pos v3) (clc (pos v3) (clc (pos v4) cln))))) + (% pf_c7 (holds (clc (neg v1) (clc (pos v2) (clc (pos v4) cln)))) + (% pf_c8 (holds (clc (pos v1) (clc (neg v2) (clc (neg v2) (clc (neg v4) cln))))) + (@ pf_cmap + (CMapc_proof 1 _ _ _ pf_c1 + (CMapc_proof 2 _ _ _ pf_c2 + (CMapc_proof 3 _ _ _ pf_c3 + (CMapc_proof 4 _ _ _ pf_c4 + (CMapc_proof 5 _ _ _ pf_c5 + (CMapc_proof 6 _ _ _ pf_c6 + (CMapc_proof 7 _ _ _ pf_c7 + (CMapc_proof 8 _ _ _ pf_c8 CMapn_proof)))))))) (@ lrat_proof_witness (LRATProofa 9 @@ -689,15 +741,68 @@ (% pf_c8 (holds (clc (pos v1) (clc (neg v2) (clc (neg v4) cln)))) (% pf_c9 (holds (clc (pos v1) (clc (pos v2) (clc (neg v3) cln)))) (@ pf_cmap - (CMapc_proof 1 _ _ pf_c1 - (CMapc_proof 2 _ _ pf_c2 - (CMapc_proof 3 _ _ pf_c3 - (CMapc_proof 4 _ _ pf_c4 - (CMapc_proof 5 _ _ pf_c5 - (CMapc_proof 6 _ _ pf_c6 - (CMapc_proof 7 _ _ pf_c7 - (CMapc_proof 8 _ _ pf_c8 - (CMapc_proof 9 _ _ pf_c9 + (CMapc_proof 1 _ _ _ pf_c1 + (CMapc_proof 2 _ _ _ pf_c2 + (CMapc_proof 3 _ _ _ pf_c3 + (CMapc_proof 4 _ _ _ pf_c4 + (CMapc_proof 5 _ _ _ pf_c5 + (CMapc_proof 6 _ _ _ pf_c6 + (CMapc_proof 7 _ _ _ pf_c7 + (CMapc_proof 8 _ _ _ pf_c8 + (CMapc_proof 9 _ _ _ pf_c9 + CMapn_proof))))))))) + (@ lrat_proof_witness + (LRATProofa 10 + (clc (pos v1) cln) + Tracen + (RATHintsc 2 (Tracec 6 (Tracec 8 Tracen)) + (RATHintsc 5 (Tracec 1 (Tracec 8 Tracen)) + (RATHintsc 7 (Tracec 6 (Tracec 9 Tracen)) + RATHintsn))) + (LRATProofd (CIListc 1 (CIListc 6 (CIListc 8 (CIListc 9 CIListn)))) + (LRATProofa 11 + (clc (pos v2) cln) + (Tracec 10 (Tracec 7 (Tracec 5 (Tracec 3 Tracen)))) + RATHintsn + (LRATProofd (CIListc 3 (CIListc 7 CIListn)) + (LRATProofa 12 + cln + (Tracec 10 (Tracec 11 (Tracec 2 (Tracec 4 (Tracec 5 Tracen))))) + RATHintsn + LRATProofn))))) + (: + (holds cln) + (lrat_proof_of_bottom _ pf_cmap lrat_proof_witness)) + )) + ))))))))) + )))) +) + +; Clauses 1 and 9 are logically identical, but the literals have been reordered. +(check + (% v1 var + (% v2 var + (% v3 var + (% v4 var + (% pf_c1 (holds (clc (pos v1) (clc (pos v2) (clc (neg v3) cln)))) + (% pf_c2 (holds (clc (neg v1) (clc (neg v2) (clc (pos v3) cln)))) + (% pf_c3 (holds (clc (pos v2) (clc (pos v3) (clc (neg v4) cln)))) + (% pf_c4 (holds (clc (neg v2) (clc (neg v3) (clc (pos v4) cln)))) + (% pf_c5 (holds (clc (neg v1) (clc (neg v3) (clc (neg v4) cln)))) + (% pf_c6 (holds (clc (pos v1) (clc (pos v3) (clc (pos v4) cln)))) + (% pf_c7 (holds (clc (neg v1) (clc (pos v2) (clc (pos v4) cln)))) + (% pf_c8 (holds (clc (pos v1) (clc (neg v2) (clc (neg v4) cln)))) + (% pf_c9 (holds (clc (neg v3) (clc (pos v2) (clc (pos v1) cln)))) + (@ pf_cmap + (CMapc_proof 1 _ _ _ pf_c1 + (CMapc_proof 2 _ _ _ pf_c2 + (CMapc_proof 3 _ _ _ pf_c3 + (CMapc_proof 4 _ _ _ pf_c4 + (CMapc_proof 5 _ _ _ pf_c5 + (CMapc_proof 6 _ _ _ pf_c6 + (CMapc_proof 7 _ _ _ pf_c7 + (CMapc_proof 8 _ _ _ pf_c8 + (CMapc_proof 9 _ _ _ pf_c9 CMapn_proof))))))))) (@ lrat_proof_witness (LRATProofa 10 @@ -741,14 +846,14 @@ (% pf_c7 (holds (clc (neg v1) (clc (pos v2) (clc (pos v4) cln)))) (% pf_c8 (holds (clc (pos v1) (clc (neg v2) (clc (neg v4) cln)))) (@ pf_cmap - (CMapc_proof 1 _ _ pf_c1 - (CMapc_proof 2 _ _ pf_c2 - (CMapc_proof 3 _ _ pf_c3 - (CMapc_proof 4 _ _ pf_c4 - (CMapc_proof 5 _ _ pf_c5 - (CMapc_proof 6 _ _ pf_c6 - (CMapc_proof 7 _ _ pf_c7 - (CMapc_proof 8 _ _ pf_c8 + (CMapc_proof 1 _ _ _ pf_c1 + (CMapc_proof 2 _ _ _ pf_c2 + (CMapc_proof 3 _ _ _ pf_c3 + (CMapc_proof 4 _ _ _ pf_c4 + (CMapc_proof 5 _ _ _ pf_c5 + (CMapc_proof 6 _ _ _ pf_c6 + (CMapc_proof 7 _ _ _ pf_c7 + (CMapc_proof 8 _ _ _ pf_c8 CMapn_proof)))))))) (@ lrat_proof_witness (LRATProofa 9 -- cgit v1.2.3 From fb6bab97d8a9103a0d9c94ea9ba54cb04ed2a2a8 Mon Sep 17 00:00:00 2001 From: Alex Ozdemir Date: Tue, 11 Dec 2018 17:35:26 -0800 Subject: [LRA proof] More complete LRA example proofs. (#2722) * [LRA proof] Refine "poly" and "term Real" distinction Short Version: Refined the LRA signature and used the refined version to write two new test proofs which are close to interface compatible with the LRA proofs that CVC4 will produce. Love Version: LRA proofs have the following interface: * Given predicates between real terms * Prove bottom However, even though the type of the interface does not express this, the predicates are **linear bounds**, not arbitrary real bounds. Thus LRA proofs have the following structure: 1. Prove that the input predicates are equivalent to a set of linear bounds. 2. Use the linear bounds to prove bottom using farkas coefficients. Notice that the distinction between linear bounds (associated in the signature with the string "poly") and real predicates (which relate "term Real"s to one another) matters quite a bit. We have certain inds of axioms for one, and other axioms for the other. The signature used to muddy this distinction using a constructor called "term_poly" which converted between them. I decided it was better to buy into the distinction fully. Now all of the axioms for step (2) use the linear bounds and axioms for step (1) use both kinds of bounds, which makes sense because step (1) is basically a conversion. Also had to add an axiom or two, because some were missing. * Update proofs/signatures/th_lra.plf Co-Authored-By: alex-ozdemir * Improved test readability, removed unused axioms The LRA proof tests did not have appropriate documentation, and did not specify **what** they proved. Now they each have a header comment stating their premises and conclusion, and that conclusion is enforced by a type annotation in the test. The LRA signature included some unused axioms concerning `poly_term`. Now they've been removed. Credits to Yoni for noticing both problems. --- proofs/signatures/th_lra.plf | 195 +++++++++++++++++++------------------- proofs/signatures/th_lra_test.plf | 155 ++++++++++++++++++++++++++++-- 2 files changed, 240 insertions(+), 110 deletions(-) diff --git a/proofs/signatures/th_lra.plf b/proofs/signatures/th_lra.plf index 67b17c9af..76e5127c2 100644 --- a/proofs/signatures/th_lra.plf +++ b/proofs/signatures/th_lra.plf @@ -1,4 +1,22 @@ -; Depends on th_real.plf, th_smt.plf +; Depends on th_real.plf, smt.plf, sat.plf + +; LRA proofs have the following interface: +; * Given predicates between real terms +; * Prove bottom +; +; However, even though the type of the interface does not express this, +; the predicates are **linear bounds**, not arbitrary real bounds. Thus +; LRA proofs have the following structure: +; +; 1. Prove that the input predicates are equivalent to a set of linear +; bounds. +; 2. Use the linear bounds to prove bottom using farkas coefficients. +; +; Notice that the distinction between linear bounds (associated in the signature +; with the string "poly") and real predicates (which relate "term Real"s to one +; another) matters quite a bit. We have certain kinds of axioms for one, and +; other axioms for the other. + (program mpq_ifpos ((x mpq)) bool (mp_ifneg x ff (mp_ifzero x ff tt))) @@ -100,61 +118,65 @@ ;; conversion to use polynomials in term formulas -(declare poly_term (! p poly (term Real))) + +(declare >=0_poly (! x poly formula)) +(declare =0_poly (! x poly formula)) +(declare >0_poly (! x poly formula)) +(declare distinct0_poly (! x poly formula)) ;; create new equality out of inequality (declare lra_>=_>=_to_= (! p1 poly (! p2 poly - (! f1 (th_holds (>=0_Real (poly_term p1))) - (! f2 (th_holds (>=0_Real (poly_term p2))) + (! f1 (th_holds (>=0_poly p1)) + (! f2 (th_holds (>=0_poly p2)) (! i2 (^ (mp_ifzero (is_poly_const (poly_add p1 p2)) tt ff) tt) - (th_holds (=0_Real (poly_term p2))))))))) + (th_holds (=0_poly p2)))))))) ;; axioms (declare lra_axiom_= - (th_holds (=0_Real (poly_term (polyc 0/1 lmonn))))) + (th_holds (=0_poly (polyc 0/1 lmonn)))) (declare lra_axiom_> (! c mpq (! i (^ (mpq_ifpos c) tt) - (th_holds (>0_Real (poly_term (polyc c lmonn))))))) + (th_holds (>0_poly (polyc c lmonn)))))) (declare lra_axiom_>= (! c mpq (! i (^ (mp_ifneg c tt ff) ff) - (th_holds (>=0_Real (poly_term (polyc c lmonn))))))) + (th_holds (>=0_poly (polyc c lmonn)))))) (declare lra_axiom_distinct (! c mpq (! i (^ (mp_ifzero c tt ff) ff) - (th_holds (distinct0_Real (poly_term (polyc c lmonn))))))) + (th_holds (distinct0_poly (polyc c lmonn)))))) ;; contradiction rules (declare lra_contra_= (! p poly - (! f (th_holds (=0_Real (poly_term p))) + (! f (th_holds (=0_poly p)) (! i (^ (mp_ifzero (is_poly_const p) tt ff) ff) (holds cln))))) (declare lra_contra_> (! p poly - (! f (th_holds (>0_Real (poly_term p))) + (! f (th_holds (>0_poly p)) (! i2 (^ (mpq_ifpos (is_poly_const p)) ff) (holds cln))))) (declare lra_contra_>= (! p poly - (! f (th_holds (>=0_Real (poly_term p))) + (! f (th_holds (>=0_poly p)) (! i2 (^ (mp_ifneg (is_poly_const p) tt ff) tt) (holds cln))))) (declare lra_contra_distinct (! p poly - (! f (th_holds (distinct0_Real (poly_term p))) + (! f (th_holds (distinct0_poly p)) (! i2 (^ (mp_ifzero (is_poly_const p) tt ff) tt) (holds cln))))) @@ -164,33 +186,33 @@ (! p poly (! p' poly (! c mpq - (! f (th_holds (=0_Real (poly_term p))) + (! f (th_holds (=0_poly p)) (! i (^ (poly_mul_c p c) p') - (th_holds (=0_Real (poly_term p'))))))))) + (th_holds (=0_poly p')))))))) (declare lra_mul_c_> (! p poly (! p' poly (! c mpq - (! f (th_holds (>0_Real (poly_term p))) + (! f (th_holds (>0_poly p)) (! i (^ (mp_ifneg c (fail poly) (mp_ifzero c (fail poly) (poly_mul_c p c))) p') - (th_holds (>0_Real (poly_term p')))))))));) + (th_holds (>0_poly p')))))))); (declare lra_mul_c_>= (! p poly (! p' poly (! c mpq - (! f (th_holds (>=0_Real (poly_term p))) + (! f (th_holds (>=0_poly p)) (! i (^ (mp_ifneg c (fail poly) (poly_mul_c p c)) p') - (th_holds (>=0_Real (poly_term p')))))))));) + (th_holds (>=0_poly p')))))))) (declare lra_mul_c_distinct (! p poly (! p' poly (! c mpq - (! f (th_holds (distinct0_Real (poly_term p))) + (! f (th_holds (distinct0_poly p)) (! i (^ (mp_ifzero c (fail poly) (poly_mul_c p c)) p') - (th_holds (distinct0_Real (poly_term p')))))))));) + (th_holds (distinct0_poly p')))))))) ;; adding equations @@ -198,64 +220,73 @@ (! p1 poly (! p2 poly (! p3 poly - (! f1 (th_holds (=0_Real (poly_term p1))) - (! f2 (th_holds (=0_Real (poly_term p2))) + (! f1 (th_holds (=0_poly p1)) + (! f2 (th_holds (=0_poly p2)) (! i (^ (poly_add p1 p2) p3) - (th_holds (=0_Real (poly_term p3)))))))))) + (th_holds (=0_poly p3))))))))) (declare lra_add_>_> (! p1 poly (! p2 poly (! p3 poly - (! f1 (th_holds (>0_Real (poly_term p1))) - (! f2 (th_holds (>0_Real (poly_term p2))) + (! f1 (th_holds (>0_poly p1)) + (! f2 (th_holds (>0_poly p2)) (! i (^ (poly_add p1 p2) p3) - (th_holds (>0_Real (poly_term p3)))))))))) + (th_holds (>0_poly p3))))))))) (declare lra_add_>=_>= (! p1 poly (! p2 poly (! p3 poly - (! f1 (th_holds (>=0_Real (poly_term p1))) - (! f2 (th_holds (>=0_Real (poly_term p2))) + (! f1 (th_holds (>=0_poly p1)) + (! f2 (th_holds (>=0_poly p2)) (! i (^ (poly_add p1 p2) p3) - (th_holds (>=0_Real (poly_term p3)))))))))) + (th_holds (>=0_poly p3))))))))) (declare lra_add_=_> (! p1 poly (! p2 poly (! p3 poly - (! f1 (th_holds (=0_Real (poly_term p1))) - (! f2 (th_holds (>0_Real (poly_term p2))) + (! f1 (th_holds (=0_poly p1)) + (! f2 (th_holds (>0_poly p2)) (! i (^ (poly_add p1 p2) p3) - (th_holds (>0_Real (poly_term p3)))))))))) + (th_holds (>0_poly p3))))))))) (declare lra_add_=_>= (! p1 poly (! p2 poly (! p3 poly - (! f1 (th_holds (=0_Real (poly_term p1))) - (! f2 (th_holds (>=0_Real (poly_term p2))) + (! f1 (th_holds (=0_poly p1)) + (! f2 (th_holds (>=0_poly p2)) (! i (^ (poly_add p1 p2) p3) - (th_holds (>=0_Real (poly_term p3)))))))))) + (th_holds (>=0_poly p3))))))))) (declare lra_add_>_>= (! p1 poly (! p2 poly (! p3 poly - (! f1 (th_holds (>0_Real (poly_term p1))) - (! f2 (th_holds (>=0_Real (poly_term p2))) + (! f1 (th_holds (>0_poly p1)) + (! f2 (th_holds (>=0_poly p2)) + (! i (^ (poly_add p1 p2) p3) + (th_holds (>0_poly p3))))))))) + +(declare lra_add_>=_> + (! p1 poly + (! p2 poly + (! p3 poly + (! f1 (th_holds (>=0_poly p1)) + (! f2 (th_holds (>0_poly p2)) (! i (^ (poly_add p1 p2) p3) - (th_holds (>0_Real (poly_term p3)))))))))) + (th_holds (>0_poly p3))))))))) (declare lra_add_=_distinct (! p1 poly (! p2 poly (! p3 poly - (! f1 (th_holds (=0_Real (poly_term p1))) - (! f2 (th_holds (distinct0_Real (poly_term p2))) + (! f1 (th_holds (=0_poly p1)) + (! f2 (th_holds (distinct0_poly p2)) (! i (^ (poly_add p1 p2) p3) - (th_holds (distinct0_Real (poly_term p3)))))))))) + (th_holds (distinct0_poly p3))))))))) ;; substracting equations @@ -263,37 +294,37 @@ (! p1 poly (! p2 poly (! p3 poly - (! f1 (th_holds (=0_Real (poly_term p1))) - (! f2 (th_holds (=0_Real (poly_term p2))) + (! f1 (th_holds (=0_poly p1)) + (! f2 (th_holds (=0_poly p2)) (! i (^ (poly_sub p1 p2) p3) - (th_holds (=0_Real (poly_term p3))))))))))) + (th_holds (=0_poly p3))))))))) (declare lra_sub_>_= (! p1 poly (! p2 poly (! p3 poly - (! f1 (th_holds (>0_Real (poly_term p1))) - (! f2 (th_holds (=0_Real (poly_term p2))) + (! f1 (th_holds (>0_poly p1)) + (! f2 (th_holds (=0_poly p2)) (! i (^ (poly_sub p1 p2) p3) - (th_holds (>0_Real (poly_term p3)))))))))) + (th_holds (>0_poly p3))))))))) (declare lra_sub_>=_= (! p1 poly (! p2 poly (! p3 poly - (! f1 (th_holds (>=0_Real (poly_term p1))) - (! f2 (th_holds (=0_Real (poly_term p2))) + (! f1 (th_holds (>=0_poly p1)) + (! f2 (th_holds (=0_poly p2)) (! i (^ (poly_sub p1 p2) p3) - (th_holds (>=0_Real (poly_term p3)))))))))) + (th_holds (>=0_poly p3))))))))) (declare lra_sub_distinct_= (! p1 poly (! p2 poly (! p3 poly - (! f1 (th_holds (distinct0_Real (poly_term p1))) - (! f2 (th_holds (=0_Real (poly_term p2))) + (! f1 (th_holds (distinct0_poly p1)) + (! f2 (th_holds (=0_poly p2)) (! i (^ (poly_sub p1 p2) p3) - (th_holds (distinct0_Real (poly_term p3))))))))))) + (th_holds (distinct0_poly p3))))))))) ;; converting between terms and polynomials @@ -357,6 +388,14 @@ (! a (^ (poly_mul_c py x) pz) (poly_norm (*_Real y (a_real x)) pz)))))))) +(declare poly_flip_not_>= + (! p poly + (! p_negged poly + (! pf_formula (th_holds (not (>=0_poly p))) + (! sc (^ (poly_neg p) p_negged) + (th_holds (>0_poly p_negged))))))) + + ;; for polynomializing other terms, in particular ite's (declare term_atom (! v var_real (! t (term Real) type))) @@ -395,54 +434,10 @@ (! u (th_holds (not ft)) (th_holds (not fp))))))) -; form equivalence between term formula and polynomial formula - -(declare poly_norm_= - (! x (term Real) - (! y (term Real) - (! p poly - (! h (th_holds (= Real x y)) - (! n (poly_norm (-_Real x y) p) - (! u (! pn (th_holds (=0_Real (poly_term p))) - (holds cln)) - (holds cln)))))))) - -(declare poly_norm_> +(declare poly_formula_norm_>= (! x (term Real) (! y (term Real) (! p poly - (! h (th_holds (>_Real x y)) - (! n (poly_norm (-_Real x y) p) - (! u (! pn (th_holds (>0_Real (poly_term p))) - (holds cln)) - (holds cln)))))))) - -(declare poly_norm_< - (! x (term Real) - (! y (term Real) - (! p poly - (! h (th_holds (<_Real x y)) (! n (poly_norm (-_Real y x) p) - (! u (! pn (th_holds (>0_Real (poly_term p))) - (holds cln)) - (holds cln)))))))) - -(declare poly_norm_>= - (! x (term Real) - (! y (term Real) - (! p poly - (! h (th_holds (>=_Real x y)) - (! n (poly_norm (-_Real x y) p) - (! u (! pn (th_holds (>=0_Real (poly_term p))) - (holds cln)) - (holds cln)))))))) + (poly_formula_norm (>=_Real y x) (>=0_poly p))))))) -(declare poly_norm_<= - (! x (term Real) - (! y (term Real) - (! p poly - (! h (th_holds (<=_Real x y)) - (! n (poly_norm (-_Real y x) p) - (! u (! pn (th_holds (>=0_Real (poly_term p))) - (holds cln)) - (holds cln)))))))) diff --git a/proofs/signatures/th_lra_test.plf b/proofs/signatures/th_lra_test.plf index 687ff988b..fb3ca828c 100644 --- a/proofs/signatures/th_lra_test.plf +++ b/proofs/signatures/th_lra_test.plf @@ -1,4 +1,10 @@ ; Depends On: th_lra.plf +;; Proof (from predicates on linear polynomials) that the following imply bottom +; +; -x - 1/2 y + 2 >= 0 +; x + y - 8 >= 0 +; x - y + 0 >= 0 +; (check ; Variables (% x var_real @@ -11,22 +17,151 @@ (@ p1 (polyc 2/1 m1) (@ p2 (polyc (~ 8/1) m2) (@ p3 (polyc 0/1 m3) - (% pf_nonneg_1 (th_holds (>=0_Real (poly_term p1))) - (% pf_nonneg_2 (th_holds (>=0_Real (poly_term p2))) - (% pf_nonneg_3 (th_holds (>=0_Real (poly_term p3))) - (lra_contra_>= - _ - (lra_add_>=_>= _ _ _ - (lra_mul_c_>= _ _ 4/1 pf_nonneg_1) + (% pf_nonneg_1 (th_holds (>=0_poly p1)) + (% pf_nonneg_2 (th_holds (>=0_poly p2)) + (% pf_nonneg_3 (th_holds (>=0_poly p3)) + (: + (holds cln) + (lra_contra_>= + _ (lra_add_>=_>= _ _ _ - (lra_mul_c_>= _ _ 3/1 pf_nonneg_2) + (lra_mul_c_>= _ _ 4/1 pf_nonneg_1) (lra_add_>=_>= _ _ _ - (lra_mul_c_>= _ _ 1/1 pf_nonneg_3) - (lra_axiom_>= 0/1))))) + (lra_mul_c_>= _ _ 3/1 pf_nonneg_2) + (lra_add_>=_>= _ _ _ + (lra_mul_c_>= _ _ 1/1 pf_nonneg_3) + (lra_axiom_>= 0/1)))))) ))))) )))) )) ) +;; Proof (from predicates on real terms) that the following imply bottom +; +; -x - 1/2 y >= 2 +; x + y >= 8 +; x - y >= 0 +; +(check + ; Declarations + ; Variables + (% x var_real + (% y var_real + ; real predicates + (@ f1 (>=_Real (+_Real (*_Real (a_real (~ 1/1)) (a_var_real x)) (*_Real (a_real (~ 1/2)) (a_var_real y))) (a_real (~ 2/1))) + (@ f2 (>=_Real (+_Real (*_Real (a_real 1/1) (a_var_real x)) (*_Real (a_real 1/1) (a_var_real y))) (a_real 8/1)) + (@ f3 (>=_Real (+_Real (*_Real (a_real 1/1) (a_var_real x)) (*_Real (a_real (~ 1/1)) (a_var_real y))) (a_real 0/1)) + ; proof of real predicates + (% pf_f1 (th_holds f1) + (% pf_f2 (th_holds f2) + (% pf_f3 (th_holds f3) + + + ; Normalization + ; real term -> linear polynomial normalization witnesses + (@ n1 (poly_formula_norm_>= _ _ _ + (pn_- _ _ _ _ _ + (pn_+ _ _ _ _ _ + (pn_mul_c_L _ _ _ (~ 1/1) (pn_var x)) + (pn_mul_c_L _ _ _ (~ 1/2) (pn_var y))) + (pn_const (~ 2/1)))) + (@ n2 (poly_formula_norm_>= _ _ _ + (pn_- _ _ _ _ _ + (pn_+ _ _ _ _ _ + (pn_mul_c_L _ _ _ 1/1 (pn_var x)) + (pn_mul_c_L _ _ _ 1/1 (pn_var y))) + (pn_const 8/1))) + (@ n3 (poly_formula_norm_>= _ _ _ + (pn_- _ _ _ _ _ + (pn_+ _ _ _ _ _ + (pn_mul_c_L _ _ _ 1/1 (pn_var x)) + (pn_mul_c_L _ _ _ (~ 1/1) (pn_var y))) + (pn_const 0/1))) + ; proof of linear polynomial predicates + (@ pf_n1 (poly_form _ _ n1 pf_f1) + (@ pf_n2 (poly_form _ _ n2 pf_f2) + (@ pf_n3 (poly_form _ _ n3 pf_f3) + + ; derivation of a contradiction using farkas coefficients + (: + (holds cln) + (lra_contra_>= _ + (lra_add_>=_>= _ _ _ + (lra_mul_c_>= _ _ 4/1 pf_n1) + (lra_add_>=_>= _ _ _ + (lra_mul_c_>= _ _ 3/1 pf_n2) + (lra_add_>=_>= _ _ _ + (lra_mul_c_>= _ _ 1/1 pf_n3) + (lra_axiom_>= 0/1)))))) + ))) + ))) + ))) + ))) + )) +) + +;; Term proof, 2 (>=), one (not >=) +;; Proof (from predicates on real terms) that the following imply bottom +; +; -x + y >= 2 +; x + y >= 2 +; not[ y >= -2] => [y < -2] => [-y > 2] +; +(check + ; Declarations + ; Variables + (% x var_real + (% y var_real + ; real predicates + (@ f1 (>=_Real + (+_Real (*_Real (a_real (~ 1/1)) (a_var_real x)) (a_var_real y)) + (a_real 2/1)) + (@ f2 (>=_Real + (+_Real (a_var_real x) (a_var_real y)) + (a_real 2/1)) + (@ f3 (not (>=_Real (a_var_real y) (a_real (~ 2/1)))) + ; Normalization + ; proof of real predicates + (% pf_f1 (th_holds f1) + (% pf_f2 (th_holds f2) + (% pf_f3 (th_holds f3) + ; real term -> linear polynomial normalization witnesses + (@ n1 (poly_formula_norm_>= _ _ _ + (pn_- _ _ _ _ _ + (pn_+ _ _ _ _ _ + (pn_mul_c_L _ _ _ (~ 1/1) (pn_var x)) + (pn_var y)) + (pn_const 2/1))) + (@ n2 (poly_formula_norm_>= _ _ _ + (pn_- _ _ _ _ _ + (pn_+ _ _ _ _ _ + (pn_var x) + (pn_var y)) + (pn_const 2/1))) + (@ n3 (poly_formula_norm_>= _ _ _ + (pn_- _ _ _ _ _ + (pn_var y) + (pn_const (~ 2/1)))) + ; proof of linear polynomial predicates + (@ pf_n1 (poly_form _ _ n1 pf_f1) + (@ pf_n2 (poly_form _ _ n2 pf_f2) + (@ pf_n3 (poly_flip_not_>= _ _ (poly_form_not _ _ n3 pf_f3)) + ; derivation of a contradiction using farkas coefficients + (: + (holds cln) + (lra_contra_> _ + (lra_add_>=_> _ _ _ + (lra_mul_c_>= _ _ 1/1 pf_n1) + (lra_add_>=_> _ _ _ + (lra_mul_c_>= _ _ 1/1 pf_n2) + (lra_add_>_>= _ _ _ + (lra_mul_c_> _ _ 2/1 pf_n3) + (lra_axiom_>= 0/1)))))) + ))) + ))) + ))) + ))) + )) +) -- cgit v1.2.3 From 12f88ad664c24ee522643073dcddf144854ca1ef Mon Sep 17 00:00:00 2001 From: Andres Noetzli Date: Wed, 12 Dec 2018 23:19:30 +0000 Subject: API: Add simple empty/sigma regexp unit tests (#2746) --- test/unit/api/solver_black.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/unit/api/solver_black.h b/test/unit/api/solver_black.h index 538899a0f..7527a5c55 100644 --- a/test/unit/api/solver_black.h +++ b/test/unit/api/solver_black.h @@ -36,6 +36,8 @@ class SolverBlack : public CxxTest::TestSuite void testDefineFun(); void testDefineFunRec(); void testDefineFunsRec(); + void testMkRegexpEmpty(); + void testMkRegexpSigma(); private: Solver d_solver; @@ -228,3 +230,19 @@ void SolverBlack::testDefineFunsRec() d_solver.defineFunsRec({f1, f2}, {{b1, b11}, {b4}}, {v1, v4}), CVC4ApiException&); } + +void SolverBlack::testMkRegexpEmpty() +{ + Sort strSort = d_solver.getStringSort(); + Term s = d_solver.mkVar("s", strSort); + TS_ASSERT_THROWS_NOTHING( + d_solver.mkTerm(STRING_IN_REGEXP, s, d_solver.mkRegexpEmpty())); +} + +void SolverBlack::testMkRegexpSigma() +{ + Sort strSort = d_solver.getStringSort(); + Term s = d_solver.mkVar("s", strSort); + TS_ASSERT_THROWS_NOTHING( + d_solver.mkTerm(STRING_IN_REGEXP, s, d_solver.mkRegexpSigma())); +} -- cgit v1.2.3 From c2be681200406d8a96a1c2e1b9fbbb228334eed8 Mon Sep 17 00:00:00 2001 From: Aina Niemetz Date: Wed, 12 Dec 2018 16:37:59 -0800 Subject: Fix compiler warnings. (#2748) --- src/theory/bv/theory_bv_rewrite_rules_simplification.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/theory/bv/theory_bv_rewrite_rules_simplification.h b/src/theory/bv/theory_bv_rewrite_rules_simplification.h index 7efdc2c81..c58d69f6f 100644 --- a/src/theory/bv/theory_bv_rewrite_rules_simplification.h +++ b/src/theory/bv/theory_bv_rewrite_rules_simplification.h @@ -535,7 +535,7 @@ inline Node RewriteRule::apply(TNode node) { Debug("bv-rewrite") << "RewriteRule(" << node << ")" << std::endl; - uint32_t m, my, mz, n; + uint32_t m, my, mz; size_t nc; Kind kind = node.getKind(); TNode concat; @@ -589,7 +589,9 @@ inline Node RewriteRule::apply(TNode node) z = nc > 1 ? zb.constructNode() : zb[0]; } m = utils::getSize(x); - n = utils::getSize(c); +#ifdef CVC4_ASSERTIONS + uint32_t n = utils::getSize(c); +#endif my = y.isNull() ? 0 : utils::getSize(y); mz = z.isNull() ? 0 : utils::getSize(z); Assert(mz == m - my - n); -- cgit v1.2.3 From f038f308e044bd1e842996e891e3cb119825113a Mon Sep 17 00:00:00 2001 From: Andrew Reynolds Date: Thu, 13 Dec 2018 12:03:16 -0600 Subject: Remove spurious map (#2750) --- src/theory/quantifiers/sygus/sygus_unif_io.cpp | 150 +++++++++++-------------- 1 file changed, 68 insertions(+), 82 deletions(-) diff --git a/src/theory/quantifiers/sygus/sygus_unif_io.cpp b/src/theory/quantifiers/sygus/sygus_unif_io.cpp index 0b378875c..c9db62735 100644 --- a/src/theory/quantifiers/sygus/sygus_unif_io.cpp +++ b/src/theory/quantifiers/sygus/sygus_unif_io.cpp @@ -1323,7 +1323,6 @@ Node SygusUnifIo::constructSol( Trace("sygus-sui-dt") << "...try STRATEGY " << strat << "..." << std::endl; - std::map look_ahead_solved_children; std::vector dt_children_cons; bool success = true; @@ -1338,108 +1337,95 @@ Node SygusUnifIo::constructSol( Trace("sygus-sui-dt") << "construct PBE child #" << sc << "..." << std::endl; Node rec_c; - std::map::iterator itla = - look_ahead_solved_children.find(sc); - if (itla != look_ahead_solved_children.end()) + + std::pair& cenum = etis->d_cenum[sc]; + + // update the context + std::vector prev; + if (strat == strat_ITE && sc > 0) { - rec_c = itla->second; - indent("sygus-sui-dt-debug", ind + 1); - Trace("sygus-sui-dt-debug") - << "ConstructPBE: look ahead solved : " - << d_tds->sygusToBuiltin(rec_c) << std::endl; + EnumCache& ecache_cond = d_ecache[split_cond_enum]; + Assert(set_split_cond_res_index); + Assert(split_cond_res_index < ecache_cond.d_enum_vals_res.size()); + prev = x.d_vals; + x.updateContext(this, + ecache_cond.d_enum_vals_res[split_cond_res_index], + sc == 1); + // return value of above call may be false in corner cases where we + // must choose a non-separating condition to traverse to another + // strategy node } - else - { - std::pair& cenum = etis->d_cenum[sc]; - // update the context - std::vector prev; - if (strat == strat_ITE && sc > 0) - { - EnumCache& ecache_cond = d_ecache[split_cond_enum]; - Assert(set_split_cond_res_index); - Assert(split_cond_res_index < ecache_cond.d_enum_vals_res.size()); - prev = x.d_vals; - x.updateContext(this, - ecache_cond.d_enum_vals_res[split_cond_res_index], - sc == 1); - // return value of above call may be false in corner cases where we - // must choose a non-separating condition to traverse to another - // strategy node - } - - // recurse - if (strat == strat_ITE && sc == 0) - { - Node ce = cenum.first; + // recurse + if (strat == strat_ITE && sc == 0) + { + Node ce = cenum.first; - EnumCache& ecache_child = d_ecache[ce]; + EnumCache& ecache_child = d_ecache[ce]; - // get the conditionals in the current context : they must be - // distinguishable - std::map > possible_cond; - std::map solved_cond; // stores branch - ecache_child.d_term_trie.getLeaves(x.d_vals, true, possible_cond); + // get the conditionals in the current context : they must be + // distinguishable + std::map > possible_cond; + std::map solved_cond; // stores branch + ecache_child.d_term_trie.getLeaves(x.d_vals, true, possible_cond); - std::map>::iterator itpc = - possible_cond.find(0); - if (itpc != possible_cond.end()) + std::map>::iterator itpc = + possible_cond.find(0); + if (itpc != possible_cond.end()) + { + if (Trace.isOn("sygus-sui-dt-debug")) { - if (Trace.isOn("sygus-sui-dt-debug")) + indent("sygus-sui-dt-debug", ind + 1); + Trace("sygus-sui-dt-debug") + << "PBE : We have " << itpc->second.size() + << " distinguishable conditionals:" << std::endl; + for (Node& cond : itpc->second) { - indent("sygus-sui-dt-debug", ind + 1); + indent("sygus-sui-dt-debug", ind + 2); Trace("sygus-sui-dt-debug") - << "PBE : We have " << itpc->second.size() - << " distinguishable conditionals:" << std::endl; - for (Node& cond : itpc->second) - { - indent("sygus-sui-dt-debug", ind + 2); - Trace("sygus-sui-dt-debug") - << d_tds->sygusToBuiltin(cond) << std::endl; - } - } - if (rec_c.isNull()) - { - rec_c = constructBestConditional(ce, itpc->second); - Assert(!rec_c.isNull()); - indent("sygus-sui-dt", ind); - Trace("sygus-sui-dt") - << "PBE: ITE strategy : choose best conditional " - << d_tds->sygusToBuiltin(rec_c) << std::endl; + << d_tds->sygusToBuiltin(cond) << std::endl; } } - else + if (rec_c.isNull()) { - // TODO (#1250) : degenerate case where children have different - // types? + rec_c = constructBestConditional(ce, itpc->second); + Assert(!rec_c.isNull()); indent("sygus-sui-dt", ind); - Trace("sygus-sui-dt") << "return PBE: failed ITE strategy, " - "cannot find a distinguishable condition" - << std::endl; - } - if (!rec_c.isNull()) - { - Assert(ecache_child.d_enum_val_to_index.find(rec_c) - != ecache_child.d_enum_val_to_index.end()); - split_cond_res_index = ecache_child.d_enum_val_to_index[rec_c]; - set_split_cond_res_index = true; - split_cond_enum = ce; - Assert(split_cond_res_index - < ecache_child.d_enum_vals_res.size()); + Trace("sygus-sui-dt") + << "PBE: ITE strategy : choose best conditional " + << d_tds->sygusToBuiltin(rec_c) << std::endl; } } else { - did_recurse = true; - rec_c = constructSol(f, cenum.first, cenum.second, ind + 2, lemmas); + // TODO (#1250) : degenerate case where children have different + // types? + indent("sygus-sui-dt", ind); + Trace("sygus-sui-dt") << "return PBE: failed ITE strategy, " + "cannot find a distinguishable condition" + << std::endl; } - - // undo update the context - if (strat == strat_ITE && sc > 0) + if (!rec_c.isNull()) { - x.d_vals = prev; + Assert(ecache_child.d_enum_val_to_index.find(rec_c) + != ecache_child.d_enum_val_to_index.end()); + split_cond_res_index = ecache_child.d_enum_val_to_index[rec_c]; + set_split_cond_res_index = true; + split_cond_enum = ce; + Assert(split_cond_res_index + < ecache_child.d_enum_vals_res.size()); } } + else + { + did_recurse = true; + rec_c = constructSol(f, cenum.first, cenum.second, ind + 2, lemmas); + } + // undo update the context + if (strat == strat_ITE && sc > 0) + { + x.d_vals = prev; + } if (!rec_c.isNull()) { dt_children_cons.push_back(rec_c); -- cgit v1.2.3 From bc59b160f3890f68c497dde13ff54c194f476eb4 Mon Sep 17 00:00:00 2001 From: Aina Niemetz Date: Thu, 13 Dec 2018 13:17:22 -0800 Subject: New C++ API: Add tests for sort functions of solver object. (#2752) --- src/api/cvc4cpp.cpp | 1 + test/unit/api/solver_black.h | 99 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/src/api/cvc4cpp.cpp b/src/api/cvc4cpp.cpp index b4d3b013d..cadad4eff 100644 --- a/src/api/cvc4cpp.cpp +++ b/src/api/cvc4cpp.cpp @@ -1750,6 +1750,7 @@ Sort Solver::mkUninterpretedSort(const std::string& symbol) const Sort Solver::mkSortConstructorSort(const std::string& symbol, size_t arity) const { + CVC4_API_ARG_CHECK_EXPECTED(arity > 0, arity) << "an arity > 0"; return d_exprMgr->mkSortConstructor(symbol, arity); } diff --git a/test/unit/api/solver_black.h b/test/unit/api/solver_black.h index 7527a5c55..b0249b8a0 100644 --- a/test/unit/api/solver_black.h +++ b/test/unit/api/solver_black.h @@ -26,16 +26,31 @@ class SolverBlack : public CxxTest::TestSuite void setUp() override; void tearDown() override; + void testGetBooleanSort(); + void testGetIntegerSort(); + void testGetRealSort(); + void testGetRegExpSort(); + void testGetStringSort(); + void testGetRoundingmodeSort(); + + void testMkArraySort(); void testMkBitVectorSort(); void testMkFloatingPointSort(); void testMkDatatypeSort(); void testMkFunctionSort(); + void testMkParamSort(); void testMkPredicateSort(); + void testMkRecordSort(); + void testMkSetSort(); + void testMkSortConstructorSort(); + void testMkUninterpretedSort(); void testMkTupleSort(); + void testDeclareFun(); void testDefineFun(); void testDefineFunRec(); void testDefineFunsRec(); + void testMkRegexpEmpty(); void testMkRegexpSigma(); @@ -47,6 +62,53 @@ void SolverBlack::setUp() {} void SolverBlack::tearDown() {} +void SolverBlack::testGetBooleanSort() +{ + TS_ASSERT_THROWS_NOTHING(d_solver.getBooleanSort()); +} + +void SolverBlack::testGetIntegerSort() +{ + TS_ASSERT_THROWS_NOTHING(d_solver.getIntegerSort()); +} + +void SolverBlack::testGetRealSort() +{ + TS_ASSERT_THROWS_NOTHING(d_solver.getRealSort()); +} + +void SolverBlack::testGetRegExpSort() +{ + TS_ASSERT_THROWS_NOTHING(d_solver.getRegExpSort()); +} + +void SolverBlack::testGetStringSort() +{ + TS_ASSERT_THROWS_NOTHING(d_solver.getStringSort()); +} + +void SolverBlack::testGetRoundingmodeSort() +{ + TS_ASSERT_THROWS_NOTHING(d_solver.getRoundingmodeSort()); +} + +void SolverBlack::testMkArraySort() +{ + Sort boolSort = d_solver.getBooleanSort(); + Sort intSort = d_solver.getIntegerSort(); + Sort realSort = d_solver.getRealSort(); + Sort bvSort = d_solver.mkBitVectorSort(32); + Sort fpSort = d_solver.mkFloatingPointSort(3, 5); + TS_ASSERT_THROWS_NOTHING(d_solver.mkArraySort(boolSort, boolSort)); + TS_ASSERT_THROWS_NOTHING(d_solver.mkArraySort(intSort, intSort)); + TS_ASSERT_THROWS_NOTHING(d_solver.mkArraySort(realSort, realSort)); + TS_ASSERT_THROWS_NOTHING(d_solver.mkArraySort(bvSort, bvSort)); + TS_ASSERT_THROWS_NOTHING(d_solver.mkArraySort(fpSort, fpSort)); + TS_ASSERT_THROWS_NOTHING(d_solver.mkArraySort(boolSort, intSort)); + TS_ASSERT_THROWS_NOTHING(d_solver.mkArraySort(realSort, bvSort)); + TS_ASSERT_THROWS_NOTHING(d_solver.mkArraySort(bvSort, fpSort)); +} + void SolverBlack::testMkBitVectorSort() { TS_ASSERT_THROWS_NOTHING(d_solver.mkBitVectorSort(32)); @@ -99,6 +161,12 @@ void SolverBlack::testMkFunctionSort() CVC4ApiException&); } +void SolverBlack::testMkParamSort() +{ + TS_ASSERT_THROWS_NOTHING(d_solver.mkParamSort("T")); + TS_ASSERT_THROWS_NOTHING(d_solver.mkParamSort("")); +} + void SolverBlack::testMkPredicateSort() { TS_ASSERT_THROWS_NOTHING( @@ -111,6 +179,37 @@ void SolverBlack::testMkPredicateSort() CVC4ApiException&); } +void SolverBlack::testMkRecordSort() +{ + std::vector> fields = { + std::make_pair("b", d_solver.getBooleanSort()), + std::make_pair("bv", d_solver.mkBitVectorSort(8)), + std::make_pair("i", d_solver.getIntegerSort())}; + std::vector> empty; + TS_ASSERT_THROWS_NOTHING(d_solver.mkRecordSort(fields)); + TS_ASSERT_THROWS_NOTHING(d_solver.mkRecordSort(empty)); +} + +void SolverBlack::testMkSetSort() +{ + TS_ASSERT_THROWS_NOTHING(d_solver.mkSetSort(d_solver.getBooleanSort())); + TS_ASSERT_THROWS_NOTHING(d_solver.mkSetSort(d_solver.getIntegerSort())); + TS_ASSERT_THROWS_NOTHING(d_solver.mkSetSort(d_solver.mkBitVectorSort(4))); +} + +void SolverBlack::testMkUninterpretedSort() +{ + TS_ASSERT_THROWS_NOTHING(d_solver.mkUninterpretedSort("u")); + TS_ASSERT_THROWS_NOTHING(d_solver.mkUninterpretedSort("")); +} + +void SolverBlack::testMkSortConstructorSort() +{ + TS_ASSERT_THROWS_NOTHING(d_solver.mkSortConstructorSort("s", 2)); + TS_ASSERT_THROWS_NOTHING(d_solver.mkSortConstructorSort("", 2)); + TS_ASSERT_THROWS(d_solver.mkSortConstructorSort("", 0), CVC4ApiException&); +} + void SolverBlack::testMkTupleSort() { TS_ASSERT_THROWS_NOTHING(d_solver.mkTupleSort({d_solver.getIntegerSort()})); -- cgit v1.2.3 From 000feaeb02292d7a2873198664022801b12e5151 Mon Sep 17 00:00:00 2001 From: Andrew Reynolds Date: Thu, 13 Dec 2018 18:39:26 -0600 Subject: Make single invocation and invariant pre/post condition templates independent (#2749) --cegqi-si=none previously disabled pre/post-condition templates for invariant synthesis. This PR eliminates this dependency. There are no major code changes in this PR, unfortunately a large block of code changed indentation so I refactored it to be more up to date with the coding guidelines. --- .../quantifiers/sygus/ce_guided_single_inv.cpp | 318 ++++++++++++--------- 1 file changed, 177 insertions(+), 141 deletions(-) diff --git a/src/theory/quantifiers/sygus/ce_guided_single_inv.cpp b/src/theory/quantifiers/sygus/ce_guided_single_inv.cpp index d4735b3d8..aa20c1f76 100644 --- a/src/theory/quantifiers/sygus/ce_guided_single_inv.cpp +++ b/src/theory/quantifiers/sygus/ce_guided_single_inv.cpp @@ -138,159 +138,195 @@ void CegSingleInv::initialize(Node q) } } // compute single invocation partition - if( options::cegqiSingleInvMode()!=CEGQI_SI_MODE_NONE ){ - Node qq; - if( q[1].getKind()==NOT && q[1][0].getKind()==FORALL ){ - qq = q[1][0][1]; - }else{ - qq = TermUtil::simpleNegate( q[1] ); - } - //process the single invocation-ness of the property - if( !d_sip->init( progs, qq ) ){ - Trace("cegqi-si") << "...not single invocation (type mismatch)" << std::endl; - }else{ - Trace("cegqi-si") << "- Partitioned to single invocation parts : " << std::endl; - d_sip->debugPrint( "cegqi-si" ); + Node qq; + if (q[1].getKind() == NOT && q[1][0].getKind() == FORALL) + { + qq = q[1][0][1]; + } + else + { + qq = TermUtil::simpleNegate(q[1]); + } + // process the single invocation-ness of the property + if (!d_sip->init(progs, qq)) + { + Trace("cegqi-si") << "...not single invocation (type mismatch)" + << std::endl; + return; + } + Trace("cegqi-si") << "- Partitioned to single invocation parts : " + << std::endl; + d_sip->debugPrint("cegqi-si"); + + // map from program to bound variables + std::vector funcs; + d_sip->getFunctions(funcs); + for (unsigned j = 0, size = funcs.size(); j < size; j++) + { + Assert(std::find(progs.begin(), progs.end(), funcs[j]) != progs.end()); + d_prog_to_sol_index[funcs[j]] = j; + } - //map from program to bound variables - std::vector funcs; - d_sip->getFunctions(funcs); - for (unsigned j = 0, size = funcs.size(); j < size; j++) + // check if it is single invocation + if (d_sip->isPurelySingleInvocation()) + { + // We are fully single invocation, set single invocation if we haven't + // disabled single invocation techniques. + if (options::cegqiSingleInvMode() != CEGQI_SI_MODE_NONE) + { + d_single_invocation = true; + return; + } + } + // We are processing without single invocation techniques, now check if + // we should fix an invariant template (post-condition strengthening or + // pre-condition weakening). + SygusInvTemplMode tmode = options::sygusInvTemplMode(); + if (tmode != SYGUS_INV_TEMPL_MODE_NONE) + { + // currently only works for single predicate synthesis + if (q[0].getNumChildren() > 1 || !q[0][0].getType().isPredicate()) + { + tmode = SYGUS_INV_TEMPL_MODE_NONE; + } + else if (!options::sygusInvTemplWhenSyntax()) + { + // only use invariant templates if no syntactic restrictions + if (CegGrammarConstructor::hasSyntaxRestrictions(q)) { - Assert(std::find(progs.begin(), progs.end(), funcs[j]) != progs.end()); - d_prog_to_sol_index[funcs[j]] = j; + tmode = SYGUS_INV_TEMPL_MODE_NONE; } + } + } + + if (tmode == SYGUS_INV_TEMPL_MODE_NONE) + { + // not processing invariant templates + return; + } + // if we are doing invariant templates, then construct the template + Trace("cegqi-si") << "- Do transition inference..." << std::endl; + d_ti[q].process(qq); + Trace("cegqi-inv") << std::endl; + if (d_ti[q].d_func.isNull()) + { + // the invariant could not be inferred + return; + } + NodeManager* nm = NodeManager::currentNM(); + // map the program back via non-single invocation map + Node prog = d_ti[q].d_func; + std::vector prog_templ_vars; + prog_templ_vars.insert( + prog_templ_vars.end(), d_ti[q].d_vars.begin(), d_ti[q].d_vars.end()); + d_trans_pre[prog] = d_ti[q].getComponent(1); + d_trans_post[prog] = d_ti[q].getComponent(-1); + Trace("cegqi-inv") << " precondition : " << d_trans_pre[prog] << std::endl; + Trace("cegqi-inv") << " postcondition : " << d_trans_post[prog] << std::endl; + std::vector sivars; + d_sip->getSingleInvocationVariables(sivars); + Node invariant = d_sip->getFunctionInvocationFor(prog); + Assert(!invariant.isNull()); + invariant = invariant.substitute(sivars.begin(), + sivars.end(), + prog_templ_vars.begin(), + prog_templ_vars.end()); + Trace("cegqi-inv") << " invariant : " << invariant << std::endl; - //check if it is single invocation - if (!d_sip->isPurelySingleInvocation()) + // store simplified version of quantified formula + d_simp_quant = d_sip->getFullSpecification(); + std::vector new_bv; + for( const Node& v : sivars ) + { + new_bv.push_back(nm->mkBoundVar(v.getType())); + } + d_simp_quant = d_simp_quant.substitute( + sivars.begin(), sivars.end(), new_bv.begin(), new_bv.end()); + Assert(q[1].getKind() == NOT && q[1][0].getKind() == FORALL); + for (const Node& v : q[1][0][0]) + { + new_bv.push_back(v); + } + d_simp_quant = + nm->mkNode(FORALL, nm->mkNode(BOUND_VAR_LIST, new_bv), d_simp_quant) + .negate(); + d_simp_quant = Rewriter::rewrite(d_simp_quant); + d_simp_quant = nm->mkNode(FORALL, q[0], d_simp_quant, q[2]); + Trace("cegqi-si") << "Rewritten quantifier : " << d_simp_quant << std::endl; + + // construct template argument + d_templ_arg[prog] = nm->mkSkolem("I", invariant.getType()); + + // construct template + Node templ; + if (options::sygusInvAutoUnfold()) + { + if (d_ti[q].isComplete()) + { + Trace("cegqi-inv-auto-unfold") + << "Automatic deterministic unfolding... " << std::endl; + // auto-unfold + DetTrace dt; + int init_dt = d_ti[q].initializeTrace(dt); + if (init_dt == 0) { - SygusInvTemplMode tmode = options::sygusInvTemplMode(); - if (tmode != SYGUS_INV_TEMPL_MODE_NONE) + Trace("cegqi-inv-auto-unfold") << " Init : "; + dt.print("cegqi-inv-auto-unfold"); + Trace("cegqi-inv-auto-unfold") << std::endl; + unsigned counter = 0; + unsigned status = 0; + while (counter < 100 && status == 0) { - // currently only works for single predicate synthesis - if (q[0].getNumChildren() > 1 || !q[0][0].getType().isPredicate()) - { - tmode = SYGUS_INV_TEMPL_MODE_NONE; - } - else if (!options::sygusInvTemplWhenSyntax()) - { - // only use invariant templates if no syntactic restrictions - if (CegGrammarConstructor::hasSyntaxRestrictions(q)) - { - tmode = SYGUS_INV_TEMPL_MODE_NONE; - } - } + status = d_ti[q].incrementTrace(dt); + counter++; + Trace("cegqi-inv-auto-unfold") << " #" << counter << " : "; + dt.print("cegqi-inv-auto-unfold"); + Trace("cegqi-inv-auto-unfold") + << "...status = " << status << std::endl; } - - if (tmode != SYGUS_INV_TEMPL_MODE_NONE) + if (status == 1) { - //if we are doing invariant templates, then construct the template - Trace("cegqi-si") << "- Do transition inference..." << std::endl; - d_ti[q].process( qq ); - Trace("cegqi-inv") << std::endl; - if( !d_ti[q].d_func.isNull() ){ - // map the program back via non-single invocation map - Node prog = d_ti[q].d_func; - std::vector< Node > prog_templ_vars; - prog_templ_vars.insert( prog_templ_vars.end(), d_ti[q].d_vars.begin(), d_ti[q].d_vars.end() ); - d_trans_pre[prog] = d_ti[q].getComponent( 1 ); - d_trans_post[prog] = d_ti[q].getComponent( -1 ); - Trace("cegqi-inv") << " precondition : " << d_trans_pre[prog] << std::endl; - Trace("cegqi-inv") << " postcondition : " << d_trans_post[prog] << std::endl; - std::vector sivars; - d_sip->getSingleInvocationVariables(sivars); - Node invariant = d_sip->getFunctionInvocationFor(prog); - Assert(!invariant.isNull()); - invariant = invariant.substitute(sivars.begin(), - sivars.end(), - prog_templ_vars.begin(), - prog_templ_vars.end()); - Trace("cegqi-inv") << " invariant : " << invariant << std::endl; - - // store simplified version of quantified formula - d_simp_quant = d_sip->getFullSpecification(); - std::vector< Node > new_bv; - for (unsigned j = 0, size = sivars.size(); j < size; j++) - { - new_bv.push_back( - NodeManager::currentNM()->mkBoundVar(sivars[j].getType())); - } - d_simp_quant = d_simp_quant.substitute( - sivars.begin(), sivars.end(), new_bv.begin(), new_bv.end()); - Assert( q[1].getKind()==NOT && q[1][0].getKind()==FORALL ); - for( unsigned j=0; jmkNode( kind::FORALL, NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, new_bv ), d_simp_quant ).negate(); - d_simp_quant = Rewriter::rewrite( d_simp_quant ); - d_simp_quant = NodeManager::currentNM()->mkNode( kind::FORALL, q[0], d_simp_quant, q[2] ); - Trace("cegqi-si") << "Rewritten quantifier : " << d_simp_quant << std::endl; - - //construct template argument - d_templ_arg[prog] = NodeManager::currentNM()->mkSkolem( "I", invariant.getType() ); - - //construct template - Node templ; - if( options::sygusInvAutoUnfold() ){ - if( d_ti[q].isComplete() ){ - Trace("cegqi-inv-auto-unfold") << "Automatic deterministic unfolding... " << std::endl; - // auto-unfold - DetTrace dt; - int init_dt = d_ti[q].initializeTrace( dt ); - if( init_dt==0 ){ - Trace("cegqi-inv-auto-unfold") << " Init : "; - dt.print("cegqi-inv-auto-unfold"); - Trace("cegqi-inv-auto-unfold") << std::endl; - unsigned counter = 0; - unsigned status = 0; - while( counter<100 && status==0 ){ - status = d_ti[q].incrementTrace( dt ); - counter++; - Trace("cegqi-inv-auto-unfold") << " #" << counter << " : "; - dt.print("cegqi-inv-auto-unfold"); - Trace("cegqi-inv-auto-unfold") << "...status = " << status << std::endl; - } - if( status==1 ){ - // we have a trivial invariant - templ = d_ti[q].constructFormulaTrace( dt ); - Trace("cegqi-inv") << "By finite deterministic terminating trace, a solution invariant is : " << std::endl; - Trace("cegqi-inv") << " " << templ << std::endl; - // FIXME : this should be unnecessary - templ = NodeManager::currentNM()->mkNode( AND, templ, d_templ_arg[prog] ); - } - }else{ - Trace("cegqi-inv-auto-unfold") << "...failed initialize." << std::endl; - } - } - } - Trace("cegqi-inv") << "Make the template... " << tmode << " " - << templ << std::endl; - if( templ.isNull() ){ - if (tmode == SYGUS_INV_TEMPL_MODE_PRE) - { - //d_templ[prog] = NodeManager::currentNM()->mkNode( AND, NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], invariant ), d_trans_post[prog] ); - templ = NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], d_templ_arg[prog] ); - }else{ - Assert(tmode == SYGUS_INV_TEMPL_MODE_POST); - //d_templ[prog] = NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], NodeManager::currentNM()->mkNode( AND, d_trans_post[prog], invariant ) ); - templ = NodeManager::currentNM()->mkNode( AND, d_trans_post[prog], d_templ_arg[prog] ); - } - } - Trace("cegqi-inv") << " template (pre-substitution) : " << templ << std::endl; - Assert( !templ.isNull() ); - // subsitute the template arguments - Assert(prog_templ_vars.size() == prog_vars[prog].size()); - templ = templ.substitute( prog_templ_vars.begin(), prog_templ_vars.end(), prog_vars[prog].begin(), prog_vars[prog].end() ); - Trace("cegqi-inv") << " template : " << templ << std::endl; - d_templ[prog] = templ; - } + // we have a trivial invariant + templ = d_ti[q].constructFormulaTrace(dt); + Trace("cegqi-inv") << "By finite deterministic terminating trace, a " + "solution invariant is : " + << std::endl; + Trace("cegqi-inv") << " " << templ << std::endl; + // this should be unnecessary + templ = nm->mkNode(AND, templ, d_templ_arg[prog]); } - }else{ - //we are fully single invocation - d_single_invocation = true; } + else + { + Trace("cegqi-inv-auto-unfold") << "...failed initialize." << std::endl; + } + } + } + Trace("cegqi-inv") << "Make the template... " << tmode << " " << templ + << std::endl; + if (templ.isNull()) + { + if (tmode == SYGUS_INV_TEMPL_MODE_PRE) + { + templ = nm->mkNode(OR, d_trans_pre[prog], d_templ_arg[prog]); + } + else + { + Assert(tmode == SYGUS_INV_TEMPL_MODE_POST); + templ = nm->mkNode(AND, d_trans_post[prog], d_templ_arg[prog]); } } + Trace("cegqi-inv") << " template (pre-substitution) : " << templ + << std::endl; + Assert(!templ.isNull()); + // subsitute the template arguments + Assert(prog_templ_vars.size() == prog_vars[prog].size()); + templ = templ.substitute(prog_templ_vars.begin(), + prog_templ_vars.end(), + prog_vars[prog].begin(), + prog_vars[prog].end()); + Trace("cegqi-inv") << " template : " << templ << std::endl; + d_templ[prog] = templ; } void CegSingleInv::finishInit(bool syntaxRestricted) -- cgit v1.2.3 From 075e3d97974c89dcbd4cf6c7a1c3b37cbb27403d Mon Sep 17 00:00:00 2001 From: Andrew Reynolds Date: Thu, 13 Dec 2018 20:17:50 -0600 Subject: Fix extended rewriter for binary associative operators. (#2751) This was causing assertion failures when using Sets + Sygus. --- src/theory/quantifiers/extended_rewrite.cpp | 3 ++- src/theory/quantifiers/term_util.cpp | 20 ++++++++++++++++++-- src/theory/quantifiers/term_util.h | 16 ++++++++++++---- src/theory/sets/theory_sets_rewriter.cpp | 3 ++- test/regress/CMakeLists.txt | 1 + test/regress/regress0/sets/sets-extr.smt2 | 15 +++++++++++++++ 6 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 test/regress/regress0/sets/sets-extr.smt2 diff --git a/src/theory/quantifiers/extended_rewrite.cpp b/src/theory/quantifiers/extended_rewrite.cpp index b583a55da..ae053930c 100644 --- a/src/theory/quantifiers/extended_rewrite.cpp +++ b/src/theory/quantifiers/extended_rewrite.cpp @@ -119,7 +119,8 @@ Node ExtendedRewriter::extendedRewrite(Node n) Kind k = n.getKind(); bool childChanged = false; bool isNonAdditive = TermUtil::isNonAdditive(k); - bool isAssoc = TermUtil::isAssoc(k); + // We flatten associative operators below, which requires k to be n-ary. + bool isAssoc = TermUtil::isAssoc(k, true); for (unsigned i = 0; i < n.getNumChildren(); i++) { Node nc = extendedRewrite(n[i]); diff --git a/src/theory/quantifiers/term_util.cpp b/src/theory/quantifiers/term_util.cpp index f8e8ed5ad..4c9cf2c8d 100644 --- a/src/theory/quantifiers/term_util.cpp +++ b/src/theory/quantifiers/term_util.cpp @@ -654,7 +654,15 @@ bool TermUtil::isNegate(Kind k) return k == NOT || k == BITVECTOR_NOT || k == BITVECTOR_NEG || k == UMINUS; } -bool TermUtil::isAssoc( Kind k ) { +bool TermUtil::isAssoc(Kind k, bool reqNAry) +{ + if (reqNAry) + { + if (k == UNION || k == INTERSECTION) + { + return false; + } + } return k == PLUS || k == MULT || k == NONLINEAR_MULT || k == AND || k == OR || k == XOR || k == BITVECTOR_PLUS || k == BITVECTOR_MULT || k == BITVECTOR_AND || k == BITVECTOR_OR || k == BITVECTOR_XOR @@ -663,7 +671,15 @@ bool TermUtil::isAssoc( Kind k ) { || k == SEP_STAR; } -bool TermUtil::isComm( Kind k ) { +bool TermUtil::isComm(Kind k, bool reqNAry) +{ + if (reqNAry) + { + if (k == UNION || k == INTERSECTION) + { + return false; + } + } return k == EQUAL || k == PLUS || k == MULT || k == NONLINEAR_MULT || k == AND || k == OR || k == XOR || k == BITVECTOR_PLUS || k == BITVECTOR_MULT || k == BITVECTOR_AND || k == BITVECTOR_OR || k == BITVECTOR_XOR diff --git a/src/theory/quantifiers/term_util.h b/src/theory/quantifiers/term_util.h index dd3e76ee2..820821991 100644 --- a/src/theory/quantifiers/term_util.h +++ b/src/theory/quantifiers/term_util.h @@ -270,10 +270,18 @@ public: * double negation if applicable, e.g. mkNegate( ~, ~x ) ---> x. */ static Node mkNegate(Kind notk, Node n); - /** is assoc */ - static bool isAssoc( Kind k ); - /** is k commutative? */ - static bool isComm( Kind k ); + /** is k associative? + * + * If flag reqNAry is true, then we additionally require that k is an + * n-ary operator. + */ + static bool isAssoc(Kind k, bool reqNAry = false); + /** is k commutative? + * + * If flag reqNAry is true, then we additionally require that k is an + * n-ary operator. + */ + static bool isComm(Kind k, bool reqNAry = false); /** is k non-additive? * Returns true if diff --git a/src/theory/sets/theory_sets_rewriter.cpp b/src/theory/sets/theory_sets_rewriter.cpp index a3f1f9893..2a2015319 100644 --- a/src/theory/sets/theory_sets_rewriter.cpp +++ b/src/theory/sets/theory_sets_rewriter.cpp @@ -47,7 +47,7 @@ bool checkConstantMembership(TNode elementTerm, TNode setTerm) RewriteResponse TheorySetsRewriter::postRewrite(TNode node) { NodeManager* nm = NodeManager::currentNM(); Kind kind = node.getKind(); - + Trace("sets-postrewrite") << "Process: " << node << std::endl; if(node.isConst()) { // Dare you touch the const and mangle it to something else. @@ -204,6 +204,7 @@ RewriteResponse TheorySetsRewriter::postRewrite(TNode node) { if( rew!=node ){ Trace("sets-rewrite") << "Sets::rewrite " << node << " -> " << rew << std::endl; } + Trace("sets-rewrite") << "...no rewrite." << std::endl; return RewriteResponse(REWRITE_DONE, rew); } break; diff --git a/test/regress/CMakeLists.txt b/test/regress/CMakeLists.txt index 9e942aae1..95e4b8875 100644 --- a/test/regress/CMakeLists.txt +++ b/test/regress/CMakeLists.txt @@ -776,6 +776,7 @@ set(regress_0_tests regress0/sets/pre-proc-univ.smt2 regress0/sets/rec_copy_loop_check_heap_access_43_4.smt2 regress0/sets/sets-equal.smt2 + regress0/sets/sets-extr.smt2 regress0/sets/sets-inter.smt2 regress0/sets/sets-of-sets-subtypes.smt2 regress0/sets/sets-poly-int-real.smt2 diff --git a/test/regress/regress0/sets/sets-extr.smt2 b/test/regress/regress0/sets/sets-extr.smt2 new file mode 100644 index 000000000..c497ff189 --- /dev/null +++ b/test/regress/regress0/sets/sets-extr.smt2 @@ -0,0 +1,15 @@ +; COMMAND-LINE: --ext-rew-prep --ext-rew-prep-agg +; EXPECT: sat +(set-logic ALL) +(declare-sort Atom 0) + +(declare-fun a () Atom) +(declare-fun b () Atom) +(declare-fun c () Atom) +(declare-fun S () (Set Atom)) + + +(assert (= S (union (singleton a) (union (singleton c) (singleton b))))) + +(check-sat) + -- cgit v1.2.3 From a383b73fbb01acb8bc1726c6a1b61c8d1b214aae Mon Sep 17 00:00:00 2001 From: Aina Niemetz Date: Fri, 14 Dec 2018 10:25:15 -0800 Subject: New C++ API: Add tests for opterm object. (#2756) --- src/api/cvc4cpp.cpp | 16 +++++++++++-- test/unit/api/CMakeLists.txt | 1 + test/unit/api/opterm_black.h | 57 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 test/unit/api/opterm_black.h diff --git a/src/api/cvc4cpp.cpp b/src/api/cvc4cpp.cpp index cadad4eff..68b0301ec 100644 --- a/src/api/cvc4cpp.cpp +++ b/src/api/cvc4cpp.cpp @@ -635,6 +635,10 @@ class CVC4ApiExceptionStream CVC4_PREDICT_FALSE(cond) \ ? (void)0 : OstreamVoider() & CVC4ApiExceptionStream().ostream() +#define CVC4_API_CHECK_NOT_NULL \ + CVC4_API_CHECK(!isNull()) << "Invalid call to '" << __PRETTY_FUNCTION__ \ + << "', expected non-null object"; + #define CVC4_API_KIND_CHECK(kind) \ CVC4_API_CHECK(isDefinedKind(kind)) \ << "Invalid kind '" << kindToString(kind) << "'"; @@ -1166,9 +1170,17 @@ bool OpTerm::operator==(const OpTerm& t) const { return *d_expr == *t.d_expr; } bool OpTerm::operator!=(const OpTerm& t) const { return *d_expr != *t.d_expr; } -Kind OpTerm::getKind() const { return intToExtKind(d_expr->getKind()); } +Kind OpTerm::getKind() const +{ + CVC4_API_CHECK_NOT_NULL; + return intToExtKind(d_expr->getKind()); +} -Sort OpTerm::getSort() const { return Sort(d_expr->getType()); } +Sort OpTerm::getSort() const +{ + CVC4_API_CHECK_NOT_NULL; + return Sort(d_expr->getType()); +} bool OpTerm::isNull() const { return d_expr->isNull(); } diff --git a/test/unit/api/CMakeLists.txt b/test/unit/api/CMakeLists.txt index eeab46f99..8a6be70b9 100644 --- a/test/unit/api/CMakeLists.txt +++ b/test/unit/api/CMakeLists.txt @@ -4,3 +4,4 @@ cvc4_add_unit_test_black(solver_black api) cvc4_add_unit_test_black(sort_black api) cvc4_add_unit_test_black(term_black api) +cvc4_add_unit_test_black(opterm_black api) diff --git a/test/unit/api/opterm_black.h b/test/unit/api/opterm_black.h new file mode 100644 index 000000000..637301dd3 --- /dev/null +++ b/test/unit/api/opterm_black.h @@ -0,0 +1,57 @@ +/********************* */ +/*! \file opterm_black.h + ** \verbatim + ** Top contributors (to current version): + ** Aina Niemetz + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2018 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Black box testing of the Term class + **/ + +#include + +#include "api/cvc4cpp.h" + +using namespace CVC4::api; + +class OpTermBlack : public CxxTest::TestSuite +{ + public: + void setUp() override {} + void tearDown() override {} + + void testGetKind(); + void testGetSort(); + void testIsNull(); + + private: + Solver d_solver; +}; + +void OpTermBlack::testGetKind() +{ + OpTerm x; + TS_ASSERT_THROWS(x.getSort(), CVC4ApiException&); + x = d_solver.mkOpTerm(BITVECTOR_EXTRACT_OP, 31, 1); + TS_ASSERT_THROWS_NOTHING(x.getKind()); +} + +void OpTermBlack::testGetSort() +{ + OpTerm x; + TS_ASSERT_THROWS(x.getSort(), CVC4ApiException&); + x = d_solver.mkOpTerm(BITVECTOR_EXTRACT_OP, 31, 1); + TS_ASSERT_THROWS_NOTHING(x.getSort()); +} + +void OpTermBlack::testIsNull() +{ + OpTerm x; + TS_ASSERT(x.isNull()); + x = d_solver.mkOpTerm(BITVECTOR_EXTRACT_OP, 31, 1); + TS_ASSERT(!x.isNull()); +} -- cgit v1.2.3 From 0a5e63b5c4c851275cf8928cf9224857b61aa650 Mon Sep 17 00:00:00 2001 From: Aina Niemetz Date: Fri, 14 Dec 2018 15:12:27 -0800 Subject: Fixed typos. --- src/options/options_template.cpp | 2 +- src/theory/arith/cut_log.h | 2 +- src/util/sexpr.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/options/options_template.cpp b/src/options/options_template.cpp index 85a9747fe..9650aba7a 100644 --- a/src/options/options_template.cpp +++ b/src/options/options_template.cpp @@ -112,7 +112,7 @@ struct OptionHandler { if(!success){ throw OptionException(option + ": failed to parse "+ optionarg + - " as an integer of the appropraite type."); + " as an integer of the appropriate type."); } // Depending in the platform unsigned numbers with '-' signs may parse. diff --git a/src/theory/arith/cut_log.h b/src/theory/arith/cut_log.h index 6650e6680..5fd585588 100644 --- a/src/theory/arith/cut_log.h +++ b/src/theory/arith/cut_log.h @@ -216,7 +216,7 @@ public: int getUpId() const; /** - * Looks up a row id to the appropraite arith variable. + * Looks up a row id to the appropriate arith variable. * Be careful these are deleted in context during replay! * failure returns ARITHVAR_SENTINEL */ ArithVar lookupRowId(int rowId) const; diff --git a/src/util/sexpr.h b/src/util/sexpr.h index 427eba5f8..bad6cdb2b 100644 --- a/src/util/sexpr.h +++ b/src/util/sexpr.h @@ -13,7 +13,7 @@ ** ** Simple representation of S-expressions. ** These are used when a simple, and obvious interface for basic - ** expressions is appropraite. + ** expressions is appropriate. ** ** These are quite ineffecient. ** These are totally disconnected from any ExprManager. -- cgit v1.2.3 From 4983fb0e4339d1c03c8eb5567aca566a378114ea Mon Sep 17 00:00:00 2001 From: Alex Ozdemir Date: Fri, 14 Dec 2018 17:44:39 -0800 Subject: [LRA Proof] Storage for LRA proofs (#2747) * [LRA Proof] Storage for LRA proofs During LRA solving the `ConstraintDatabase` contains the reasoning behind different constraints. Combinations of constraints are periodically used to justify lemmas (conflict clauses, propegations, ... ?). `ConstraintDatabase` is SAT context-dependent. ArithProofRecorder will be used to store concise representations of the proof for each lemma raised by the (LR)A theory. The (LR)A theory will write to it, and the ArithProof class will read from it to produce LFSC proofs. Right now, it's pretty simplistic -- it allows for only Farkas proofs. In future PRs I'll: 1. add logic that stores proofs therein 2. add logic that retrieves and prints proofs 3. enable LRA proof production, checking, and testing * Document ArithProofRecorder use-sites * Update src/proof/arith_proof_recorder.cpp Co-Authored-By: alex-ozdemir * Yoni's review * clang-format * Response to Mathias' review. --- src/CMakeLists.txt | 2 + src/proof/arith_proof.cpp | 6 ++- src/proof/arith_proof.h | 6 +++ src/proof/arith_proof_recorder.cpp | 81 ++++++++++++++++++++++++++++ src/proof/arith_proof_recorder.h | 107 +++++++++++++++++++++++++++++++++++++ src/theory/arith/theory_arith.cpp | 1 + src/theory/arith/theory_arith.h | 11 ++++ 7 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 src/proof/arith_proof_recorder.cpp create mode 100644 src/proof/arith_proof_recorder.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 91c06ddd9..7ecee2dee 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -124,6 +124,8 @@ libcvc4_add_sources( printer/tptp/tptp_printer.h proof/arith_proof.cpp proof/arith_proof.h + proof/arith_proof_recorder.cpp + proof/arith_proof_recorder.h proof/array_proof.cpp proof/array_proof.h proof/bitvector_proof.cpp diff --git a/src/proof/arith_proof.cpp b/src/proof/arith_proof.cpp index 3b57be1f2..1d51f99e1 100644 --- a/src/proof/arith_proof.cpp +++ b/src/proof/arith_proof.cpp @@ -640,8 +640,10 @@ Node ProofArith::toStreamRecLFSC(std::ostream& out, } ArithProof::ArithProof(theory::arith::TheoryArith* arith, TheoryProofEngine* pe) - : TheoryProof(arith, pe), d_realMode(false) -{} + : TheoryProof(arith, pe), d_recorder(), d_realMode(false) +{ + arith->setProofRecorder(&d_recorder); +} theory::TheoryId ArithProof::getTheoryId() { return theory::THEORY_ARITH; } void ArithProof::registerTerm(Expr term) { diff --git a/src/proof/arith_proof.h b/src/proof/arith_proof.h index 27012184a..a58294998 100644 --- a/src/proof/arith_proof.h +++ b/src/proof/arith_proof.h @@ -23,6 +23,7 @@ #include #include "expr/expr.h" +#include "proof/arith_proof_recorder.h" #include "proof/proof_manager.h" #include "proof/theory_proof.h" #include "theory/uf/equality_engine.h" @@ -62,6 +63,11 @@ protected: // TypeSet d_sorts; // all the uninterpreted sorts in this theory ExprSet d_declarations; // all the variable/function declarations + /** + * @brief Where farkas proofs of lemmas are stored. + */ + proof::ArithProofRecorder d_recorder; + bool d_realMode; theory::TheoryId getTheoryId() override; diff --git a/src/proof/arith_proof_recorder.cpp b/src/proof/arith_proof_recorder.cpp new file mode 100644 index 000000000..d654ea073 --- /dev/null +++ b/src/proof/arith_proof_recorder.cpp @@ -0,0 +1,81 @@ +/********************* */ +/*! \file arith_proof_recorder.cpp + ** \verbatim + ** Top contributors (to current version): + ** Alex Ozdemir + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2018 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief A class for recording the skeletons of arithmetic proofs at solve + ** time so they can later be used during proof-production time. + **/ + +#include "proof/arith_proof_recorder.h" + +#include +#include + +#include "base/map_util.h" + +namespace CVC4 { +namespace proof { + +ArithProofRecorder::ArithProofRecorder() : d_lemmasToFarkasCoefficients() +{ + // Nothing else +} +void ArithProofRecorder::saveFarkasCoefficients( + Node conflict, theory::arith::RationalVectorCP farkasCoefficients) +{ + Assert(conflict.getKind() == kind::AND); + Assert(conflict.getNumChildren() == farkasCoefficients->size()); + for (size_t i = 0; i < conflict.getNumChildren(); ++i) + { + const Node& child = conflict[i]; + Assert(child.getType().isBoolean() && child[0].getType().isReal()); + } + Debug("pf::arith") << "Saved Farkas Coefficients:" << std::endl; + if (Debug.isOn("pf::arith")) + { + for (size_t i = 0; i < conflict.getNumChildren(); ++i) + { + const Node& child = conflict[i]; + const Rational& r = (*farkasCoefficients)[i]; + Debug("pf::arith") << " " << std::setw(8) << r; + Debug("pf::arith") << " " << child << std::endl; + } + } + + std::set lits; + std::copy( + conflict.begin(), conflict.end(), std::inserter(lits, lits.begin())); + + d_lemmasToFarkasCoefficients[lits] = + std::make_pair(std::move(conflict), *farkasCoefficients); +} + +bool ArithProofRecorder::hasFarkasCoefficients( + const std::set& conflict) const +{ + return d_lemmasToFarkasCoefficients.find(conflict) + != d_lemmasToFarkasCoefficients.end(); +} + +std::pair +ArithProofRecorder::getFarkasCoefficients(const std::set& conflict) const +{ + if (auto *p = FindOrNull(d_lemmasToFarkasCoefficients, conflict)) + { + return std::make_pair(p->first, &p->second); + } + else + { + return std::make_pair(Node(), theory::arith::RationalVectorCPSentinel); + } +} + +} // namespace proof +} // namespace CVC4 diff --git a/src/proof/arith_proof_recorder.h b/src/proof/arith_proof_recorder.h new file mode 100644 index 000000000..2d0501332 --- /dev/null +++ b/src/proof/arith_proof_recorder.h @@ -0,0 +1,107 @@ +/********************* */ +/*! \file arith_proof_recorder.h + ** \verbatim + ** Top contributors (to current version): + ** Alex Ozdemir + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2018 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief A class for recording the skeletons of arithmetic proofs at solve + ** time so they can later be used during proof-production time. + ** + ** In particular, we're interested in proving bottom from a conjunction of + ** theory literals. + ** + ** For now, we assume that this can be done using a Farkas combination, and if + ** that doesn't work for some reason, then we give up and "trust" the lemma. + ** In the future we'll build support for more sophisticated reasoning. + ** + ** Given this scope, our task is to... + ** for each lemma (a set of literals) + ** save the Farkas coefficients for those literals + ** which requires we save an ordering of the literals + ** and a parallel ordering of Farkas coefficients. + ** + ** Farkas proofs have the following core structure: + ** For a list of affine bounds: c[i] dot x >= b[i] + ** (x is a vector of variables) + ** (c[i] is a vector of coefficients) + ** and a list of non-negative coefficients: f[i], + ** compute + ** + ** sum_i{ (c[i] dot x) * f[i] } and sum_i{b[i]*f[i]} + ** + ** and then verify that the left is actually < the right, a contradiction + ** + ** To be clear: this code does not check Farkas proofs, it just stores the + ** information needed to write them. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__PROOF__ARITH_PROOF_RECORDER_H +#define __CVC4__PROOF__ARITH_PROOF_RECORDER_H + +#include +#include + +#include "expr/node.h" +#include "theory/arith/constraint_forward.h" + +namespace CVC4 { +namespace proof { + +class ArithProofRecorder +{ + public: + ArithProofRecorder(); + + /** + * @brief For a set of incompatible literals, save the Farkas coefficients + * demonstrating their incompatibility + * + * @param conflict a conjunction of conflicting literals + * @param farkasCoefficients a list of rational coefficients which the literals + * should be multiplied by (pairwise) to produce a contradiction. + * + * The orders of the two vectors must agree! + */ + void saveFarkasCoefficients( + Node conflict, theory::arith::RationalVectorCP farkasCoefficients); + + /** + * @brief Determine whether some literals have a Farkas proof of their + * incompatibility + * + * @param conflict a conjunction of (putatively) conflicting literals + * + * @return whether or not there is actually a proof for them. + */ + bool hasFarkasCoefficients(const std::set& conflict) const; + + /** + * @brief Get the Farkas Coefficients object + * + * @param conflict a conjunction of conflicting literals + * @return theory::arith::RationalVectorCP -- the Farkas coefficients + * Node -- a conjunction of the problem literals in coefficient order + * + * theory::arith::RationalVectorCPSentinel if there is no entry for + * these lits + */ + std::pair getFarkasCoefficients( + const std::set& conflict) const; + + protected: + // For each lemma, save the Farkas coefficients of that lemma + std::map, std::pair> + d_lemmasToFarkasCoefficients; +}; + +} // namespace proof +} // namespace CVC4 + +#endif diff --git a/src/theory/arith/theory_arith.cpp b/src/theory/arith/theory_arith.cpp index d7113b17d..9902121d0 100644 --- a/src/theory/arith/theory_arith.cpp +++ b/src/theory/arith/theory_arith.cpp @@ -36,6 +36,7 @@ TheoryArith::TheoryArith(context::Context* c, context::UserContext* u, : Theory(THEORY_ARITH, c, u, out, valuation, logicInfo) , d_internal(new TheoryArithPrivate(*this, c, u, out, valuation, logicInfo)) , d_ppRewriteTimer("theory::arith::ppRewriteTimer") + , d_proofRecorder(nullptr) { smtStatisticsRegistry()->registerStat(&d_ppRewriteTimer); if (options::nlExt()) { diff --git a/src/theory/arith/theory_arith.h b/src/theory/arith/theory_arith.h index 195cb1883..e4b1c5b26 100644 --- a/src/theory/arith/theory_arith.h +++ b/src/theory/arith/theory_arith.h @@ -19,6 +19,7 @@ #include "theory/theory.h" #include "expr/node.h" +#include "proof/arith_proof_recorder.h" #include "theory/arith/theory_arith_private_forward.h" @@ -40,6 +41,11 @@ private: TimerStat d_ppRewriteTimer; + /** + * @brief Where to store Farkas proofs of lemmas + */ + proof::ArithProofRecorder * d_proofRecorder; + public: TheoryArith(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); @@ -90,6 +96,11 @@ public: const EntailmentCheckParameters* params, EntailmentCheckSideEffects* out) override; + void setProofRecorder(proof::ArithProofRecorder * proofRecorder) + { + d_proofRecorder = proofRecorder; + } + };/* class TheoryArith */ }/* CVC4::theory::arith namespace */ -- cgit v1.2.3 From e644cb6dc8b5b8c64663bd4e57d14e1d86588695 Mon Sep 17 00:00:00 2001 From: Andres Noetzli Date: Fri, 14 Dec 2018 23:37:56 -0800 Subject: Revert "Move ss-combine rewrite to extended rewriter (#2703)" This reverts commit eb7226ebeabf7cc70ec023107d74ffc5c1bad5e7. After merging #2740, our experiments show that the ss-combine rewrite is now beneficial for solving performance. --- src/theory/quantifiers/extended_rewrite.cpp | 4 ---- src/theory/strings/theory_strings_rewriter.cpp | 21 ++++++--------------- src/theory/strings/theory_strings_rewriter.h | 9 --------- 3 files changed, 6 insertions(+), 28 deletions(-) diff --git a/src/theory/quantifiers/extended_rewrite.cpp b/src/theory/quantifiers/extended_rewrite.cpp index ae053930c..46dcb7151 100644 --- a/src/theory/quantifiers/extended_rewrite.cpp +++ b/src/theory/quantifiers/extended_rewrite.cpp @@ -1677,10 +1677,6 @@ Node ExtendedRewriter::extendedRewriteStrings(Node ret) { new_ret = strings::TheoryStringsRewriter::rewriteEqualityExt(ret); } - else if (ret.getKind() == STRING_SUBSTR) - { - new_ret = strings::TheoryStringsRewriter::rewriteSubstrExt(ret); - } return new_ret; } diff --git a/src/theory/strings/theory_strings_rewriter.cpp b/src/theory/strings/theory_strings_rewriter.cpp index 0de42f686..57a99532e 100644 --- a/src/theory/strings/theory_strings_rewriter.cpp +++ b/src/theory/strings/theory_strings_rewriter.cpp @@ -1806,18 +1806,8 @@ Node TheoryStringsRewriter::rewriteSubstr(Node node) } } } - Trace("strings-rewrite-nf") << "No rewrites for : " << node << std::endl; - return node; -} - -Node TheoryStringsRewriter::rewriteSubstrExt(Node node) -{ - Assert(node.getKind() == STRING_SUBSTR); - - NodeManager* nm = NodeManager::currentNM(); - // combine substr - if (node[0].getKind() == STRING_SUBSTR) + if (node[0].getKind() == kind::STRING_SUBSTR) { Node start_inner = node[0][1]; Node start_outer = node[1]; @@ -1830,7 +1820,7 @@ Node TheoryStringsRewriter::rewriteSubstrExt(Node node) // the length of a string from the inner substr subtracts the start point // of the outer substr Node len_from_inner = - Rewriter::rewrite(nm->mkNode(MINUS, node[0][2], start_outer)); + Rewriter::rewrite(nm->mkNode(kind::MINUS, node[0][2], start_outer)); Node len_from_outer = node[2]; Node new_len; // take quantity that is for sure smaller than the other @@ -1848,13 +1838,14 @@ Node TheoryStringsRewriter::rewriteSubstrExt(Node node) } if (!new_len.isNull()) { - Node new_start = nm->mkNode(PLUS, start_inner, start_outer); - Node ret = nm->mkNode(STRING_SUBSTR, node[0][0], new_start, new_len); + Node new_start = nm->mkNode(kind::PLUS, start_inner, start_outer); + Node ret = + nm->mkNode(kind::STRING_SUBSTR, node[0][0], new_start, new_len); return returnRewrite(node, ret, "ss-combine"); } } } - + Trace("strings-rewrite-nf") << "No rewrites for : " << node << std::endl; return node; } diff --git a/src/theory/strings/theory_strings_rewriter.h b/src/theory/strings/theory_strings_rewriter.h index 7731cd078..69d6ff68e 100644 --- a/src/theory/strings/theory_strings_rewriter.h +++ b/src/theory/strings/theory_strings_rewriter.h @@ -159,21 +159,12 @@ class TheoryStringsRewriter { * Returns the rewritten form of node. */ static Node rewriteConcat(Node node); - /** rewrite substr * This is the entry point for post-rewriting terms node of the form * str.substr( s, i1, i2 ) * Returns the rewritten form of node. */ static Node rewriteSubstr(Node node); - - /** rewrite substr extended - * This is the entry point for extended post-rewriting terms node of the form - * str.substr( s, i1, i2 ) - * Returns the rewritten form of node. - */ - static Node rewriteSubstrExt(Node node); - /** rewrite contains * This is the entry point for post-rewriting terms node of the form * str.contains( t, s ) -- cgit v1.2.3