/********************* */ /*! \file clausal_bitvector_proof.cpp ** \verbatim ** Top contributors (to current version): ** Alex Ozdemir, Mathias Preiner, Andres Noetzli ** This file is part of the CVC4 project. ** Copyright (c) 2009-2020 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 using the DRAT proof format ** ** Contains DRAT-specific printing logic. **/ #include "cvc4_private.h" #include #include #include #include #include "options/bv_options.h" #include "proof/clausal_bitvector_proof.h" #include "proof/dimacs.h" #include "proof/drat/drat_proof.h" #include "proof/er/er_proof.h" #include "proof/lfsc_proof_printer.h" #include "proof/lrat/lrat_proof.h" #include "prop/sat_solver_types.h" #include "smt/smt_statistics_registry.h" #include "theory/bv/theory_bv.h" #if CVC4_USE_DRAT2ER #include "drat2er_options.h" #include "drat_trim_interface.h" #endif namespace CVC4 { namespace proof { ClausalBitVectorProof::ClausalBitVectorProof(theory::bv::TheoryBV* bv, TheoryProofEngine* proofEngine) : BitVectorProof(bv, proofEngine), d_clauses(), d_originalClauseIndices(), d_binaryDratProof(), d_coreClauseIndices(), d_dratTranslationStatistics(), d_dratOptimizationStatistics() { } void ClausalBitVectorProof::attachToSatSolver(prop::SatSolver& sat_solver) { sat_solver.setClausalProofLog(this); } void ClausalBitVectorProof::initCnfProof(prop::CnfStream* cnfStream, context::Context* cnf, prop::SatVariable trueVar, prop::SatVariable falseVar) { Assert(d_cnfProof == nullptr); d_cnfProof.reset(new LFSCCnfProof(cnfStream, cnf, "bb")); // Create a clause which forces the true variable to be true, and register it int trueClauseId = ClauseId(ProofManager::currentPM()->nextId()); // with the CNF proof d_cnfProof->registerTrueUnitClause(trueClauseId); // and with (this) bit-vector proof prop::SatClause c{prop::SatLiteral(trueVar, false)}; registerUsedClause(trueClauseId, c); // The same for false. int falseClauseId = ClauseId(ProofManager::currentPM()->nextId()); d_cnfProof->registerFalseUnitClause(falseClauseId); c[0] = prop::SatLiteral(falseVar, true); registerUsedClause(falseClauseId, c); } void ClausalBitVectorProof::registerUsedClause(ClauseId id, prop::SatClause& clause) { prop::SatClause& emplaced_clause = d_clauses.emplace(id, clause).first->second; canonicalizeClause(emplaced_clause); d_originalClauseIndices.push_back(id); }; void ClausalBitVectorProof::calculateAtomsInBitblastingProof() { optimizeDratProof(); // Debug dump of DRAT Proof if (Debug.isOn("bv::clausal")) { std::string serializedDratProof = d_binaryDratProof.str(); Debug("bv::clausal") << "option: " << options::bvOptimizeSatProof() << std::endl; Debug("bv::clausal") << "binary DRAT proof byte count: " << serializedDratProof.size() << std::endl; Debug("bv::clausal") << "clause count: " << d_coreClauseIndices.size() << std::endl; } // Empty any old record of which atoms were used d_atomsInBitblastingProof.clear(); Assert(d_atomsInBitblastingProof.size() == 0); // For each used clause, ask the CNF proof which atoms are used in it for (const ClauseId usedIdx : d_coreClauseIndices) { d_cnfProof->collectAtoms(&d_clauses.at(usedIdx), d_atomsInBitblastingProof); } } struct SatClausePointerComparator { inline bool operator()(const prop::SatClause* const& l, const prop::SatClause* const& r) const { prop::SatClauseLessThan cmp; return cmp(*l, *r); } }; void ClausalBitVectorProof::optimizeDratProof() { TimerStat::CodeTimer optimizeDratProofTimer{ d_dratOptimizationStatistics.d_totalTime}; if (options::bvOptimizeSatProof() == options::BvOptimizeSatProof::PROOF || options::bvOptimizeSatProof() == options::BvOptimizeSatProof::FORMULA) { Debug("bv::clausal") << "Optimizing DRAT" << std::endl; std::string formulaFilename("cvc4-dimacs-XXXXXX"); std::string dratFilename("cvc4-drat-XXXXXX"); std::string optDratFilename("cvc4-optimized-drat-XXXXXX"); std::string optFormulaFilename("cvc4-optimized-formula-XXXXXX"); { std::unique_ptr formStream = openTmpFile(&formulaFilename); const int64_t startPos = static_cast(formStream->tellp()); printDimacs(*formStream, d_clauses, d_originalClauseIndices); d_dratOptimizationStatistics.d_initialFormulaSize.setData( static_cast(formStream->tellp()) - startPos); formStream->close(); } { std::unique_ptr dratStream = openTmpFile(&dratFilename); const int64_t startPos = static_cast(dratStream->tellp()); (*dratStream) << d_binaryDratProof.str(); d_dratOptimizationStatistics.d_initialDratSize.setData( static_cast(dratStream->tellp()) - startPos); dratStream->close(); } std::unique_ptr optDratStream = openTmpFile(&optDratFilename); std::unique_ptr optFormulaStream = openTmpFile(&optFormulaFilename); #if CVC4_USE_DRAT2ER { TimerStat::CodeTimer runDratTimeOptimizationTimer{ d_dratOptimizationStatistics.d_toolTime}; int dratTrimExitCode = drat2er::drat_trim::OptimizeWithDratTrim(formulaFilename, dratFilename, optFormulaFilename, optDratFilename, drat2er::options::QUIET); AlwaysAssert(dratTrimExitCode == 0) << "drat-trim exited with " << dratTrimExitCode; } #else Unimplemented() << "Proof production when using CryptoMiniSat requires drat2er.\n" << "Run contrib/get-drat2er, reconfigure with --drat2er, and rebuild"; #endif { d_binaryDratProof.str(""); Assert(d_binaryDratProof.str().size() == 0); const int64_t startPos = static_cast(d_binaryDratProof.tellp()); std::ifstream lratStream(optDratFilename); std::copy(std::istreambuf_iterator(lratStream), std::istreambuf_iterator(), std::ostreambuf_iterator(d_binaryDratProof)); d_dratOptimizationStatistics.d_optimizedDratSize.setData( static_cast(d_binaryDratProof.tellp()) - startPos); } if (options::bvOptimizeSatProof() == options::BvOptimizeSatProof::FORMULA) { std::ifstream optFormulaInStream{optFormulaFilename}; const int64_t startPos = static_cast(optFormulaInStream.tellg()); std::vector core = parseDimacs(optFormulaInStream); d_dratOptimizationStatistics.d_optimizedFormulaSize.setData( static_cast(optFormulaInStream.tellg()) - startPos); CodeTimer clauseMatchingTimer{ d_dratOptimizationStatistics.d_clauseMatchingTime}; // Now we need to compute the clause indices for the UNSAT core. This is a // bit difficult because drat-trim may have reordered clauses, and/or // removed duplicate literals. We use literal sets as the canonical clause // form. // // TODO (aozdemir) It may be better to use a hash map instead of a tree // map here. std::map cannonicalClausesToIndices; for (const auto& kv : d_clauses) { cannonicalClausesToIndices[&kv.second] = kv.first; } d_coreClauseIndices.clear(); for (prop::SatClause& coreClause : core) { canonicalizeClause(coreClause); d_coreClauseIndices.push_back( cannonicalClausesToIndices.at(&coreClause)); } Debug("bv::clausal") << "Optimizing the DRAT proof and the formula" << std::endl; } else { Debug("bv::clausal") << "Optimizing the DRAT proof but not the formula" << std::endl; d_coreClauseIndices = d_originalClauseIndices; } optFormulaStream->close(); Assert(d_coreClauseIndices.size() > 0); remove(formulaFilename.c_str()); remove(dratFilename.c_str()); remove(optDratFilename.c_str()); remove(optFormulaFilename.c_str()); Debug("bv::clausal") << "Optimized DRAT" << std::endl; } else { Debug("bv::clausal") << "Not optimizing the formula or the DRAT proof" << std::endl; d_coreClauseIndices = d_originalClauseIndices; } } void ClausalBitVectorProof::canonicalizeClause(prop::SatClause& clause) { std::sort(clause.begin(), clause.end()); clause.erase(std::unique(clause.begin(), clause.end()), clause.end()); } ClausalBitVectorProof::DratTranslationStatistics::DratTranslationStatistics() : d_totalTime("proof::bv::dratTranslation::totalTime"), d_toolTime("proof::bv::dratTranslation::toolTime") { smtStatisticsRegistry()->registerStat(&d_totalTime); smtStatisticsRegistry()->registerStat(&d_toolTime); } ClausalBitVectorProof::DratTranslationStatistics::~DratTranslationStatistics() { smtStatisticsRegistry()->unregisterStat(&d_totalTime); smtStatisticsRegistry()->unregisterStat(&d_toolTime); } ClausalBitVectorProof::DratOptimizationStatistics::DratOptimizationStatistics() : d_totalTime("proof::bv::dratOptimization::totalTime"), d_toolTime("proof::bv::dratOptimization::toolTime"), d_clauseMatchingTime("proof::bv::dratOptimization::clauseMatchingTime"), d_initialDratSize("proof::bv::dratOptimization::initialDratSize", 0), d_optimizedDratSize("proof::bv::dratOptimization::optimizedDratSize", 0), d_initialFormulaSize("proof::bv::dratOptimization::initialFormulaSize", 0), d_optimizedFormulaSize( "proof::bv::dratOptimization::optimizedFormulaSize", 0) { smtStatisticsRegistry()->registerStat(&d_totalTime); smtStatisticsRegistry()->registerStat(&d_toolTime); smtStatisticsRegistry()->registerStat(&d_clauseMatchingTime); smtStatisticsRegistry()->registerStat(&d_initialDratSize); smtStatisticsRegistry()->registerStat(&d_optimizedDratSize); smtStatisticsRegistry()->registerStat(&d_initialFormulaSize); smtStatisticsRegistry()->registerStat(&d_optimizedFormulaSize); } ClausalBitVectorProof::DratOptimizationStatistics::~DratOptimizationStatistics() { smtStatisticsRegistry()->unregisterStat(&d_totalTime); smtStatisticsRegistry()->unregisterStat(&d_toolTime); smtStatisticsRegistry()->unregisterStat(&d_clauseMatchingTime); smtStatisticsRegistry()->unregisterStat(&d_initialDratSize); smtStatisticsRegistry()->unregisterStat(&d_optimizedDratSize); smtStatisticsRegistry()->unregisterStat(&d_initialFormulaSize); smtStatisticsRegistry()->unregisterStat(&d_optimizedFormulaSize); } void LfscClausalBitVectorProof::printTheoryLemmaProof(std::vector& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map) { Unreachable() << "Clausal bit-vector proofs should only be used in " "combination with eager " "bitblasting, which **does not use theory lemmas**"; } void LfscClausalBitVectorProof::printBBDeclarationAndCnf(std::ostream& os, std::ostream& paren, ProofLetMap& letMap) { os << "\n;; Bitblasting mappings\n"; printBitblasting(os, paren); os << "\n;; BB-CNF mappings\n"; d_cnfProof->printAtomMapping(d_atomsInBitblastingProof, os, paren, letMap); os << "\n;; BB-CNF proofs\n"; for (const ClauseId id : d_coreClauseIndices) { d_cnfProof->printCnfProofForClause(id, &d_clauses.at(id), os, paren); } } void LfscDratBitVectorProof::printEmptyClauseProof(std::ostream& os, std::ostream& paren) { Assert(options::bitblastMode() == options::BitblastMode::EAGER) << "the BV theory should only be proving bottom directly in the eager " "bitblasting mode"; os << "\n;; Proof of input to SAT solver\n"; os << "(@ proofOfSatInput "; paren << ")"; LFSCProofPrinter::printSatInputProof(d_coreClauseIndices, os, "bb"); os << "\n;; DRAT Proof Value\n"; os << "(@ dratProof "; paren << ")"; d_dratTranslationStatistics.d_totalTime.start(); drat::DratProof pf = drat::DratProof::fromBinary(d_binaryDratProof.str()); d_dratTranslationStatistics.d_totalTime.stop(); pf.outputAsLfsc(os, 2); os << "\n"; os << "\n;; Verification of DRAT Proof\n"; os << "(drat_proof_of_bottom _ proofOfSatInput dratProof " << "\n)"; } void LfscLratBitVectorProof::printEmptyClauseProof(std::ostream& os, std::ostream& paren) { Assert(options::bitblastMode() == options::BitblastMode::EAGER) << "the BV theory should only be proving bottom directly in the eager " "bitblasting mode"; os << "\n;; Proof of input to SAT solver\n"; os << "(@ proofOfCMap "; paren << ")"; LFSCProofPrinter::printCMapProof(d_coreClauseIndices, os, "bb"); os << "\n;; DRAT Proof Value\n"; os << "(@ lratProof "; paren << ")"; d_dratTranslationStatistics.d_totalTime.start(); lrat::LratProof pf = lrat::LratProof::fromDratProof(d_clauses, d_coreClauseIndices, d_binaryDratProof.str(), d_dratTranslationStatistics.d_toolTime); d_dratTranslationStatistics.d_totalTime.stop(); pf.outputAsLfsc(os); os << "\n"; os << "\n;; Verification of DRAT Proof\n"; os << "(lrat_proof_of_bottom _ proofOfCMap lratProof " << "\n)"; } void LfscErBitVectorProof::printEmptyClauseProof(std::ostream& os, std::ostream& paren) { Assert(options::bitblastMode() == options::BitblastMode::EAGER) << "the BV theory should only be proving bottom directly in the eager " "bitblasting mode"; d_dratTranslationStatistics.d_totalTime.start(); er::ErProof pf = er::ErProof::fromBinaryDratProof(d_clauses, d_coreClauseIndices, d_binaryDratProof.str(), d_dratTranslationStatistics.d_toolTime); d_dratTranslationStatistics.d_totalTime.stop(); pf.outputAsLfsc(os); } } // namespace proof }; // namespace CVC4