diff options
author | Andres Noetzli <andres.noetzli@gmail.com> | 2018-12-15 10:51:18 -0800 |
---|---|---|
committer | Andres Noetzli <andres.noetzli@gmail.com> | 2018-12-15 10:51:18 -0800 |
commit | e0cc55905bae01121ebbbc95bf5a123b2f23d638 (patch) | |
tree | f6bb2fa716be0df5c6452c8eaab6c129907f7134 /src | |
parent | 38567260a8bbd9dffdc71a6d2fa71d53f6affaa7 (diff) | |
parent | e644cb6dc8b5b8c64663bd4e57d14e1d86588695 (diff) |
Merge branch 'revertMoveSSCombine' into prePostKindsprePostKinds
Diffstat (limited to 'src')
77 files changed, 2282 insertions, 3901 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9a1cfe9e7..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 @@ -142,6 +144,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 +206,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 @@ -472,8 +477,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/api/cvc4cpp.cpp b/src/api/cvc4cpp.cpp index b4d3b013d..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(); } @@ -1750,6 +1762,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/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 <iostream> + + +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 <iosfwd> + +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 bd5b00728..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") { @@ -1301,6 +1295,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) { @@ -1632,7 +1670,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/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/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<T, true, true> { 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/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/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]] 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 <string> -#include <unordered_map> -#include <vector> +#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<Node> 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<TNode> visit; + visit.push_back(a); + std::unordered_set<TNode, TNodeHashFunction> visited; + // for ite mode, keeps track of whether you're in an ite condition + // for all mode, unused + std::unordered_set<TNode, TNodeHashFunction> 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<Node>& assertions, - std::vector<Node>& 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<Node>& assertions, - std::vector<Node>& 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<Node, Node, NodeHashFunction> d_lowerCache; +}; // class BoolToBV + } // namespace passes } // namespace preprocessing } // namespace CVC4 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 <unordered_set> #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 <algorithm> +#include <vector> + +#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<Node> 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<Node>& conflict) const +{ + return d_lemmasToFarkasCoefficients.find(conflict) + != d_lemmasToFarkasCoefficients.end(); +} + +std::pair<Node, theory::arith::RationalVectorCP> +ArithProofRecorder::getFarkasCoefficients(const std::set<Node>& 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 <map> +#include <set> + +#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<Node>& 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<Node, theory::arith::RationalVectorCP> getFarkasCoefficients( + const std::set<Node>& conflict) const; + + protected: + // For each lemma, save the Farkas coefficients of that lemma + std::map<std::set<Node>, std::pair<Node, theory::arith::RationalVector>> + d_lemmasToFarkasCoefficients; +}; + +} // namespace proof +} // namespace CVC4 + +#endif diff --git a/src/proof/bitvector_proof.cpp b/src/proof/bitvector_proof.cpp index 8f001ffa1..9eb39e2e2 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<bool>(true); - Node false_node = NodeManager::currentNM()->mkConst<bool>(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<Node>* bb) { - Assert (d_bitblaster == NULL); +void BitVectorProof::setBitblaster(theory::bv::TBitblaster<Node>* 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,9 +78,12 @@ 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 (options::lfscLetification() && term.isConst() + && term.getType().isBitVector()) + { if (d_constantLetMap.find(term) == d_constantLetMap.end()) { std::ostringstream name; name << "letBvc" << d_constantLetMap.size(); @@ -131,8 +93,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 +109,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> 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" <<clause_id<< " => " << conflict << "\n"; - d_isAssumptionConflict = false; -} - -void BitVectorProof::finalizeConflicts(std::vector<Expr>& 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<bool>()) || - (confl.getKind() == kind::NOT && confl[0].isConst() && !confl[0].getConst<bool>())) { - ignoreConflict = true; - } else if (confl.getKind() == kind::OR) { - for (unsigned k = 0; k < confl.getNumChildren(); ++k) { - if ((confl[k].isConst() && confl[k].getConst<bool>()) || - (confl[k].getKind() == kind::NOT && confl[k][0].isConst() && !confl[k][0].getConst<bool>())) { - 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 +225,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<BitVectorBitOf>().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 +261,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 +282,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 +293,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 +306,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 +337,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<Expr>& 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, 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, 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::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 +371,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<Expr,std::string>::const_iterator it; @@ -694,7 +396,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 +425,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 +564,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 +597,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,15 +616,16 @@ 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<Expr>::const_iterator it = d_bbTerms.begin(); std::vector<Expr>::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; @@ -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<Node>* LFSCBitVectorProof::getAtomsInBitblastingProof() { +const std::set<Node>* 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<Node>::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 <iostream> #include <set> -#include <sstream> #include <unordered_map> #include <unordered_set> #include <vector> - #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 T> class TBitblaster; -} /* namespace CVC4::theory::bv */ -} /* namespace CVC4::theory */ - -class CnfProof; -} /* namespace CVC4 */ - -namespace CVC4 { - -template <class Solver> class TSatProof; -typedef TSatProof< CVC4::BVMinisat::Solver> BVSatProof; - typedef std::unordered_set<Expr, ExprHashFunction> ExprSet; typedef std::unordered_map<Expr, ClauseId, ExprHashFunction> ExprToClauseId; typedef std::unordered_map<Expr, unsigned, ExprHashFunction> ExprToId; typedef std::unordered_map<Expr, Expr, ExprHashFunction> ExprToExpr; typedef std::unordered_map<Expr, std::string, ExprHashFunction> 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<Expr> d_bbTerms; // order of bit-blasting - ExprSet d_seenBBTerms; // terms that need to be bit-blasted - std::vector<Expr> 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<Node>* 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<Expr,std::string> 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<Expr, std::string> 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<Node>* 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<Node> d_atomsInBitblastingProof; - BVSatProof* getSatProof(); - CnfProof* getCnfProof() {return d_cnfProof; } - void finalizeConflicts(std::vector<Expr>& 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<CnfProof> 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<Node>* 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<Node>* getAtomsInBitblastingProof() = 0; - virtual void calculateAtomsInBitblastingProof() = 0; -}; + CnfProof* getCnfProof() { return d_cnfProof.get(); } + + void setBitblaster(theory::bv::TBitblaster<Node>* bb); -class LFSCBitVectorProof: public BitVectorProof { + private: + ExprToString d_exprToVariableName; + + ExprToString d_assignedAliases; + std::map<std::string, std::string> 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<std::string, std::string> 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<Node> 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<Expr>& 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<Node>* 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..9878972bf 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<proof::ResolutionBitVectorProof*>(pf); } ArrayProof* ProofManager::getArrayProof() { @@ -558,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(); @@ -729,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/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 Solver> class LFSCSatProof; typedef TSatProof<CVC4::Minisat::Solver> CoreSatProof; @@ -77,7 +80,6 @@ typedef TSatProof<CVC4::Minisat::Solver> 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 <iostream> +#include <sstream> + +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<bool>(true); + Node false_node = NodeManager::currentNM()->mkConst<bool>(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> 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<Expr>& 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<bool>()) + || (confl.getKind() == kind::NOT && confl[0].isConst() + && !confl[0].getConst<bool>())) + { + ignoreConflict = true; + } + else if (confl.getKind() == kind::OR) + { + for (unsigned k = 0; k < confl.getNumChildren(); ++k) + { + if ((confl[k].isConst() && confl[k].getConst<bool>()) + || (confl[k].getKind() == kind::NOT && confl[k][0].isConst() + && !confl[k][0].getConst<bool>())) + { + 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<Expr>& 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<Node>::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..ccb288f6e --- /dev/null +++ b/src/proof/resolution_bitvector_proof.h @@ -0,0 +1,133 @@ +/********************* */ +/*! \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 <iosfwd> + +#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 T> +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 Solver> +class TSatProof; +typedef TSatProof<CVC4::BVMinisat::Solver> 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<Expr>& 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: + context::Context d_fakeContext; + + // The CNF formula that results from bit-blasting will need a proof. + // This is that proof. + std::unique_ptr<BVSatProof> d_resolutionProof; + + bool d_isAssumptionConflict; + + theory::TheoryId getTheoryId() override; +}; + +class LFSCBitVectorProof : public ResolutionBitVectorProof +{ + public: + LFSCBitVectorProof(theory::bv::TheoryBV* bv, TheoryProofEngine* proofEngine) + : ResolutionBitVectorProof(bv, proofEngine) + { + } + void printTheoryLemmaProof(std::vector<Expr>& 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<theory::bv::TheoryBV*>(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<Lit>& 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<Lit>& 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 <string> #include <sstream> +#include <string> +#include <vector> namespace CVC4 { namespace prop { diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index a0939f4db..ae20fa156 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; @@ -1376,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()) @@ -1446,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 @@ -1614,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)) { @@ -1788,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 ); @@ -1831,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/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") << ", <coeffs>"; + 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 */ 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/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 */ 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 <vector> #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<T>::initTermBBStrategies() } template <class T> -TBitblaster<T>::TBitblaster() : d_termCache(), d_modelCache(), d_bvp(NULL) +TBitblaster<T>::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..019918c2f 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(), @@ -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; } @@ -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<Node> bool solve(); bool solve(const std::vector<Node>& 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<Node> std::unique_ptr<BitblastingRegistrar> d_bitblastingRegistrar; std::unique_ptr<prop::CnfStream> 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<Node>(), 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<Node> * 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<Node> 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<Node> }; TheoryBV* d_bv; + proof::ResolutionBitVectorProof* d_bvp; context::Context* d_ctx; std::unique_ptr<prop::NullRegistrar> d_nullRegistrar; std::unique_ptr<context::Context> d_nullContext; // sat solver used for bitblasting and associated CnfStream std::unique_ptr<prop::BVSatSolverInterface> d_satSolver; - std::unique_ptr<prop::BVSatSolverInterface::Notify> d_satSolverNotify; + std::unique_ptr<prop::BVSatSolverNotify> d_satSolverNotify; std::unique_ptr<prop::CnfStream> 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 <vector> #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<Node, NodeHashFunction> 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<uint32_t> 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..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<BitwiseEq>::applies(t)) { + if (options::bitwiseEq() && RewriteRule<BitwiseEq>::applies(t)) { Node result = RewriteRule<BitwiseEq>::run<false>(t); res = Rewriter::rewrite(result); } else if (d_isCoreTheory && t.getKind() == kind::EQUAL) { @@ -986,9 +986,10 @@ bool TheoryBV::applyAbstraction(const std::vector<Node>& 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<Node>& assertions, std::vector<Node>& new_assertions); - void setProofLog( BitVectorProof * bvp ); + void setResolutionProofLog(proof::ResolutionBitVectorProof* bvp); -private: + private: class Statistics { public: 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<AndOrXorConcatPullUp>::apply(TNode node) { Debug("bv-rewrite") << "RewriteRule<AndOrXorConcatPullUp>(" << 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<AndOrXorConcatPullUp>::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); diff --git a/src/theory/quantifiers/extended_rewrite.cpp b/src/theory/quantifiers/extended_rewrite.cpp index b583a55da..46dcb7151 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]); @@ -1676,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/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<Node> 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<Node, Node>::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( childDepIndex<posDepIndex ){ - posDepIndex = childDepIndex; - if( posDepIndex==-1 ){ - break; - } - } - }else if( eValT==0 ){ - if( eVal==baseVal ){ - eVal = 0; - } - } - } - if( eVal!=0 ){ - depIndex = eVal==-baseVal ? posDepIndex : negDepIndex; - return eVal; - }else{ - return 0; - } - }else if( n.getKind()==EQUAL && n[0].getType().isBoolean() ){ - int depIndex1; - int eVal = evaluate( n[0], depIndex1, ri ); - if( eVal!=0 ){ - int depIndex2; - int eVal2 = evaluate( n[1], depIndex2, ri ); - if( eVal2!=0 ){ - depIndex = depIndex1>depIndex2 ? 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; i<argDepIndex; i++ ){ - int index = d_eval_uf_use_default[n] ? i : d_eval_term_index_order[n][i]; - Debug("fmf-eval-debug") << "Add variables from " << index << "..." << std::endl; - if( children_depIndex[index]>depIndex ){ - 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)<getMaxVariableNum(j));} -}; - -void FirstOrderModelIG::makeEvalUfIndexOrder( Node n ){ - if( d_eval_term_index_order.find( n )==d_eval_term_index_order.end() ){ - //sort arguments in order of least significant vs. most significant variable in default ordering - std::map< Node, std::vector< int > > 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<Node, AbsDef*>::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<Node, AbsDef * >::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<tno.getNumChildren(); i++) { - //make sure a representative of the type exists - if( !d_rep_set.hasType( tno[i] ) ){ - Node e = getSomeDomainElement( tno[i] ); - Trace("ambqi-debug") << " * Initialize type " << tno[i] << ", add "; - Trace("ambqi-debug") << e << " " << e.getType() << std::endl; - //d_rep_set.add( e ); - } - } - } - } -} - -unsigned FirstOrderModelAbs::getRepresentativeId( TNode n ) { - TNode r = getUsedRepresentative( n ); - std::map< TNode, unsigned >::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; i<type.getNumChildren()-1; i++ ){ - std::stringstream ss; - ss << argPrefix << (i+1); - Node b = NodeManager::currentNM()->mkBoundVar( 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<n.getNumChildren(); i++ ){ - if( n.getKind()==EQUAL && n[i].getKind()==BOUND_VARIABLE ){ - int v = getVariableId( q, n[i] ); - Assert( v>=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<q[0].getNumChildren(); i++ ){ - eq_vars[i] = false; - } - collectEqVars( q, q[1], eq_vars ); - for( unsigned r=0; r<2; r++ ){ - for( std::map< int, bool >::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<IsStarAttributeId, bool> 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<Node, std::map<Node, int> > 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<Node, Node> d_set_values[2][2]; - /** stores the set of non-ground keys in the above maps */ - std::vector<Node> 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<Node, uf::UfModelTree> d_uf_model_tree; - /** model generators for each UF operator */ - std::map<Node, UfModelTreeGenerator> 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<fapps.size(); i++ ){ - // std::cout << "...." << fapps[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; i<fapps.size(); i++ ){ - unsigned r = m->getRepresentativeId( 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()<minSize ){ - minSize = it->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; i<to_erase.size(); i++ ){ - d_def.erase( to_erase[i] ); - } - } - //if constant, propagate the value upwards - if( isConstant ){ - d_value = defVal; - }else{ - d_value = val_none; - } - } -} - -void AbsDef::debugPrintUInt( const char * c, unsigned dSize, unsigned u ) const{ - for( unsigned i=0; i<dSize; i++ ){ - Trace(c) << ( ( u & ( 1 << i ) )!=0 ? "1" : "0"); - } - //Trace(c) << "("; - //for( unsigned i=0; i<32; i++ ){ - // Trace(c) << ( ( u & ( 1 << i ) )!=0 ? "1" : "0"); - //} - //Trace(c) << ")"; -} - -void AbsDef::debugPrint( const char * c, FirstOrderModelAbs * m, TNode f, unsigned depth ) const{ - if( Trace.isOn(c) ){ - if( depth==f.getNumChildren() ){ - for( unsigned i=0; i<depth; i++ ){ Trace(c) << " ";} - Trace(c) << "V[" << d_value << "]" << std::endl; - }else{ - TypeNode tn = f[depth].getType(); - const RepSet* rs = m->getRepSet(); - 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; i<depth; i++ ){ Trace(c) << " ";} - debugPrintUInt( c, dSize, it->first ); - 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<defs.size(); i++ ){ - //process each simple child - for( std::map< unsigned, AbsDef >::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; j<defs.size(); j++ ){ - defs[j]->get_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; j<defs.size(); j++ ){ - defs[j]->get_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( depth<n.getNumChildren() ){ - TypeNode tn = q.isNull() ? n[depth].getType() : m->getVariable( 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; i<entry.size(); i++ ){ - unsigned dSize = - rs->getNumRepresentatives(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; i<numReps; i++ ){ - curr = 1 << i; - d_def[curr].construct_var_eq( m, q, v1, v2, curr, currv, depth+1 ); - } - d_default = curr; - }else{ - d_def[curr].construct_var_eq( m, q, v1, v2, curr, 1, depth+1 ); - dom = dom & ~curr; - d_def[dom].construct_var_eq( m, q, v1, v2, curr, 0, depth+1 ); - d_default = dom; - } - } - } -} - -void AbsDef::construct_var( FirstOrderModelAbs * m, TNode q, unsigned v, int currv, unsigned depth ) { - if( depth==q[0].getNumChildren() ){ - Assert( currv!=val_none ); - d_value = currv; - }else{ - TypeNode tn = m->getVariable( q, depth ).getType(); - if( v==depth ){ - const unsigned numReps = m->getRepSet()->getNumRepresentatives(tn); - CVC4_CHECK(numReps > 0 && numReps < 32); - for( unsigned i=0; i<numReps; i++ ){ - d_def[ 1 << i ].construct_var( m, q, v, i, depth+1 ); - } - d_default = 1 << (numReps - 1); - }else{ - unsigned dom = m->d_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; i<count; i++ ){ - entry.push_back( m->d_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<count; i++ ){ - entry.pop_back(); - entry_def.pop_back(); - } - return; - } - } - } - if( entry.size()==q[0].getNumChildren() ){ - if( f ){ - if( Trace.isOn("ambqi-check-debug2") ){ - for( unsigned i=0; i<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; } - Trace("ambqi-check-debug2") << "Evaluate uninterpreted function entry..." << std::endl; - } - //we are composing with an uninterpreted function - std::vector< int > 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; i<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; } - Trace("ambqi-check-debug2") << "Evaluate interpreted function entry ( "; - for( unsigned i=0; i<values.size(); i++ ){ - Assert( !values[i].isNull() ); - Trace("ambqi-check-debug2") << values[i] << " "; - } - Trace("ambqi-check-debug2") << ")..." << std::endl; - } - //evaluate - Node vv = NodeManager::currentNM()->mkNode( 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<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; } - Trace("ambqi-check-debug2") << "Take product of arguments" << std::endl; - } - for( std::map< unsigned, AbsDef * >::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; i<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; } - Trace("ambqi-check-debug2") << "...process : "; - debugPrintUInt("ambqi-check-debug2", - rs->getNumRepresentatives(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<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; } - Trace("ambqi-check-debug2") << "Make default argument" << std::endl; - } - std::map< unsigned, AbsDef * > 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; i<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; } - Trace("ambqi-check-debug2") << "...process default : "; - debugPrintUInt( - "ambqi-check-debug2", rs->getNumRepresentatives(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<n.getNumChildren(); i++ ){ - Trace("ambqi-check-debug3") << i << " : "; - Trace("ambqi-check-debug3") << ((children.find( i )!=children.end()) ? "X" : "."); - if( bchildren.find( i )!=bchildren.end() ){ - Trace("ambqi-check-debug3") << bchildren[i]; - }else{ - Trace("ambqi-check-debug3") << "."; - } - if( vchildren.find( i )!=vchildren.end() ){ - Trace("ambqi-check-debug3") << vchildren[i]; - }else{ - Trace("ambqi-check-debug3") << "."; - } - Trace("ambqi-check-debug3") << std::endl; - } - Trace("ambqi-check-debug3") << "varChCount : " << varChCount << std::endl; - } - if( varChCount==0 || f ){ - //short-circuit - if( n.getKind()==AND || n.getKind()==OR ){ - for( std::map< unsigned, int >::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; i<args.size(); i++ ){ - unsigned v = 1 << m->getRepresentativeId( 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<TypeNode, std::vector<Node> >::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; i<it->second.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<Node, AbsDef * >::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; i<itut->second.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; i<fapps[0].getNumChildren(); i++ ){ - if( fm->d_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; i<fm->d_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<Node, AbsDef * >::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; i<q[0].getNumChildren(); i++ ){ - if( !fma->isValidType( 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; i<n.getNumChildren(); i++ ){ - if( n[i].getKind()==FORALL ){ - bchildren[i] = AbsDef::val_unk; - }else if( n[i].getKind() == BOUND_VARIABLE ){ - varChCount++; - vchildren[i] = m->d_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<double>(d_value_pro_con[0][v].size())) - / (1.0 + static_cast<double>(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; i<fm->getNumAssertedQuantifiers(); 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; i<fm->getNumAssertedQuantifiers(); 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; i<fm->getNumAssertedQuantifiers(); 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; i<itut->second.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; i<uf_terms.size(); i++ ){ - if( uf_terms[i].getNumChildren()>maxChildren ){ - 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 = score<selectLitScore; - } - //see if we wish to choose this as a selection literal - d_quant_selection_lit_candidates[f].push_back( value ? n : n.notNode() ); - if( selectLit ){ - selectLitScore = getSelectionScore( uf_terms ); - Trace("inst-gen-debug") << "Choose selection literal " << gn << std::endl; - Trace("inst-gen-debug") << " flags: " << isConst << " " << selectLitConstraints << " " << selectLitScore << std::endl; - d_quant_selection_lit[f] = value ? n : n.notNode(); - selectionLitTerms.clear(); - selectionLitTerms.insert( selectionLitTerms.begin(), uf_terms.begin(), uf_terms.end() ); - if( !selectLitConstraints ){ - break; - } - } - pref = 1; - }else{ - pref = -1; - } - //if we are not yet SAT, so we will add to preferences - if( d_qe->getModel()->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<d_quant_selection_lit_candidates[f].size(); i++ ){ - bool phase = d_quant_selection_lit_candidates[f][i].getKind()!=NOT; - Node lit = d_quant_selection_lit_candidates[f][i].getKind()==NOT ? d_quant_selection_lit_candidates[f][i][0] : d_quant_selection_lit_candidates[f][i]; - Assert( TermUtil::hasInstConstAttr(lit) ); - std::vector< Node > 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; i<itut->second.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<Node, bool, NodeHashFunction> 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<Node> 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<Node, std::vector<Node> > 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<Node, UfModelPreferenceData> 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/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<Node> 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<Node> 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<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<Node> 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<Node> 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<Node> 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; j<q[1][0][0].getNumChildren(); j++ ){ - new_bv.push_back( q[1][0][0][j] ); - } - d_simp_quant = NodeManager::currentNM()->mkNode( 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) 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<bool>() == 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<std::vector<Node> >& ex, std::vector<Node>& exo, std::vector<unsigned>& 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<unsigned> 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_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. diff --git a/src/theory/quantifiers/sygus/sygus_unif_io.cpp b/src/theory/quantifiers/sygus/sygus_unif_io.cpp index 89619639d..c9db62735 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<Node>& 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<Node>& 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,13 @@ Node SygusUnifIo::constructSol( EnumCache& ecache = d_ecache[e]; + bool retValMod = x.isReturnValueModified(); + Node ret_dt; + Node cached_ret_dt; if (nrole == role_equal) { - if (!x.isReturnValueModified()) + if (!retValMod) { if (ecache.isSolved()) { @@ -1069,11 +1090,86 @@ Node SygusUnifIo::constructSol( } } } + // maybe we can find one in the cache + if (ret_dt.isNull() && !retValMod) + { + bool firstTime = true; + std::unordered_set<Node, NodeHashFunction> intersection; + std::map<TypeNode, std::unordered_set<Node, NodeHashFunction>>::iterator + pit; + for (size_t i = 0, nvals = x.d_vals.size(); i < nvals; i++) + { + if (x.d_vals[i].getConst<bool>()) + { + pit = d_psolutions[i].find(etn); + if (pit == d_psolutions[i].end()) + { + // no cached solution + intersection.clear(); + break; + } + if (firstTime) + { + intersection = pit->second; + firstTime = false; + } + else + { + std::vector<Node> 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()) + { + 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<Node> 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: "; + 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; + } + } + } } 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<Node, std::vector<unsigned> > incr; bool isPrefix = nrole == role_string_prefix; @@ -1227,7 +1323,6 @@ Node SygusUnifIo::constructSol( Trace("sygus-sui-dt") << "...try STRATEGY " << strat << "..." << std::endl; - std::map<unsigned, Node> look_ahead_solved_children; std::vector<Node> dt_children_cons; bool success = true; @@ -1242,109 +1337,95 @@ Node SygusUnifIo::constructSol( Trace("sygus-sui-dt") << "construct PBE child #" << sc << "..." << std::endl; Node rec_c; - std::map<unsigned, Node>::iterator itla = - look_ahead_solved_children.find(sc); - if (itla != look_ahead_solved_children.end()) + + std::pair<Node, NodeRole>& cenum = etis->d_cenum[sc]; + + // update the context + std::vector<Node> 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<Node, NodeRole>& cenum = etis->d_cenum[sc]; - - // update the context - std::vector<Node> 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; - bool ret = x.updateContext( - this, - ecache_cond.d_enum_vals_res[split_cond_res_index], - sc == 1); - AlwaysAssert(ret); - } - // 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<int, std::vector<Node> > possible_cond; - std::map<Node, int> 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<int, std::vector<Node> > possible_cond; + std::map<Node, int> solved_cond; // stores branch + ecache_child.d_term_trie.getLeaves(x.d_vals, true, possible_cond); - std::map<int, std::vector<Node> >::iterator itpc = - possible_cond.find(0); - if (itpc != possible_cond.end()) + std::map<int, std::vector<Node>>::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; - } - } - - // otherwise, guess a conditional - 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); @@ -1380,9 +1461,54 @@ 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<Node> 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()); - 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<bool>()) + { + 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][etn].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..7f48645bf 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<Node>& 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<Node>& vals, bool pol, std::map<int, std::vector<Node>>& v); @@ -300,6 +302,16 @@ 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, 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<size_t, + std::map<TypeNode, std::unordered_set<Node, NodeHashFunction>>> + d_psolutions; /** * This flag is set to true if the solution construction was * non-deterministic with respect to failure/success. @@ -427,6 +439,12 @@ class SygusUnifIo : public SygusUnif bool useStrContainsEnumeratorExclude(Node e); /** cache for the above function */ std::map<Node, bool> 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<Node, bool> d_use_str_contains_eexc_conditional; /** the unification context used within constructSolution */ UnifContextIo d_context; 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/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/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() ){ 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/src/theory/strings/theory_strings.cpp b/src/theory/strings/theory_strings.cpp index 1d76fd4f6..88fc3e36d 100644 --- a/src/theory/strings/theory_strings.cpp +++ b/src/theory/strings/theory_strings.cpp @@ -1675,9 +1675,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/src/theory/strings/theory_strings_rewriter.cpp b/src/theory/strings/theory_strings_rewriter.cpp index 35b230035..0841eb810 100644 --- a/src/theory/strings/theory_strings_rewriter.cpp +++ b/src/theory/strings/theory_strings_rewriter.cpp @@ -1859,18 +1859,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]; @@ -1883,7 +1873,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 @@ -1901,13 +1891,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 ) 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<Node>& 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<ind; i++ ){ out << " "; diff --git a/src/theory/uf/theory_uf_model.h b/src/theory/uf/theory_uf_model.h index 201faccee..ac57bde27 100644 --- a/src/theory/uf/theory_uf_model.h +++ b/src/theory/uf/theory_uf_model.h @@ -31,8 +31,6 @@ public: std::map< Node, UfModelTreeNode > 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/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. |