/********************* */ /*! \file inst_strategy_enumerative.cpp ** \verbatim ** Top contributors (to current version): ** Andrew Reynolds, Morgan Deters ** This file is part of the CVC4 project. ** Copyright (c) 2009-2019 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 an enumerative instantiation strategy. **/ #include "theory/quantifiers/inst_strategy_enumerative.h" #include "options/quantifiers_options.h" #include "theory/quantifiers/instantiate.h" #include "theory/quantifiers/relevant_domain.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/term_util.h" #include "theory/quantifiers_engine.h" namespace CVC4 { using namespace kind; using namespace context; namespace theory { using namespace inst; namespace quantifiers { InstStrategyEnum::InstStrategyEnum(QuantifiersEngine* qe, RelevantDomain* rd) : QuantifiersModule(qe), d_rd(rd), d_fullSaturateLimit(-1) { } void InstStrategyEnum::presolve() { d_fullSaturateLimit = options::fullSaturateLimit(); } bool InstStrategyEnum::needsCheck(Theory::Effort e) { if (d_fullSaturateLimit == 0) { return false; } if (options::fullSaturateInterleave()) { if (d_quantEngine->getInstWhenNeedsCheck(e)) { return true; } } if (options::fullSaturateQuant()) { if (e >= Theory::EFFORT_LAST_CALL) { return true; } } return false; } void InstStrategyEnum::reset_round(Theory::Effort e) {} void InstStrategyEnum::check(Theory::Effort e, QEffort quant_e) { bool doCheck = false; bool fullEffort = false; if (d_fullSaturateLimit != 0) { if (options::fullSaturateInterleave()) { // we only add when interleaved with other strategies doCheck = quant_e == QEFFORT_STANDARD && d_quantEngine->hasAddedLemma(); } if (options::fullSaturateQuant() && !doCheck) { if (!d_quantEngine->theoryEngineNeedsCheck()) { doCheck = quant_e == QEFFORT_LAST_CALL; fullEffort = true; } } } if (!doCheck) { return; } Assert(!d_quantEngine->inConflict()); double clSet = 0; if (Trace.isOn("fs-engine")) { clSet = double(clock()) / double(CLOCKS_PER_SEC); Trace("fs-engine") << "---Full Saturation Round, effort = " << e << "---" << std::endl; } unsigned rstart = options::fullSaturateQuantRd() ? 0 : 1; unsigned rend = fullEffort ? 1 : rstart; unsigned addedLemmas = 0; // First try in relevant domain of all quantified formulas, if no // instantiations exist, try arbitrary ground terms. // Notice that this stratification of effort levels makes it so that some // quantified formulas may not be instantiated (if they have no instances // at effort level r=0 but another quantified formula does). We prefer // this stratification since effort level r=1 may be highly expensive in the // case where we have a quantified formula with many entailed instances. FirstOrderModel* fm = d_quantEngine->getModel(); unsigned nquant = fm->getNumAssertedQuantifiers(); std::map alreadyProc; for (unsigned r = rstart; r <= rend; r++) { if (d_rd || r > 0) { if (r == 0) { Trace("inst-alg") << "-> Relevant domain instantiate..." << std::endl; Trace("inst-alg-debug") << "Compute relevant domain..." << std::endl; d_rd->compute(); Trace("inst-alg-debug") << "...finished" << std::endl; } else { Trace("inst-alg") << "-> Ground term instantiate..." << std::endl; } for (unsigned i = 0; i < nquant; i++) { Node q = fm->getAssertedQuantifier(i, true); bool doProcess = d_quantEngine->hasOwnership(q, this) && fm->isQuantifierActive(q) && alreadyProc.find(q) == alreadyProc.end(); if (doProcess) { if (process(q, fullEffort, r == 0)) { // don't need to mark this if we are not stratifying if (!options::fullSaturateStratify()) { alreadyProc[q] = true; } // added lemma addedLemmas++; } if (d_quantEngine->inConflict()) { break; } } } if (d_quantEngine->inConflict() || (addedLemmas > 0 && options::fullSaturateStratify())) { // we break if we are in conflict, or if we added any lemma at this // effort level and we stratify effort levels. break; } } } if (Trace.isOn("fs-engine")) { Trace("fs-engine") << "Added lemmas = " << addedLemmas << std::endl; double clSet2 = double(clock()) / double(CLOCKS_PER_SEC); Trace("fs-engine") << "Finished full saturation engine, time = " << (clSet2 - clSet) << std::endl; } if (d_fullSaturateLimit > 0) { d_fullSaturateLimit--; } } bool InstStrategyEnum::process(Node f, bool fullEffort, bool isRd) { // ignore if constant true (rare case of non-standard quantifier whose body is // rewritten to true) if (f[1].isConst() && f[1].getConst()) { return false; } unsigned final_max_i = 0; std::vector maxs; std::vector max_zero; bool has_zero = false; std::map > term_db_list; std::vector ftypes; TermDb* tdb = d_quantEngine->getTermDatabase(); EqualityQuery* qy = d_quantEngine->getEqualityQuery(); // iterate over substitutions for variables for (unsigned i = 0; i < f[0].getNumChildren(); i++) { TypeNode tn = f[0][i].getType(); ftypes.push_back(tn); unsigned ts; if (isRd) { ts = d_rd->getRDomain(f, i)->d_terms.size(); } else { ts = tdb->getNumTypeGroundTerms(tn); std::map >::iterator ittd = term_db_list.find(tn); if (ittd == term_db_list.end()) { std::map reps_found; for (unsigned j = 0; j < ts; j++) { Node gt = tdb->getTypeGroundTerm(ftypes[i], j); if (!options::cegqi() || !quantifiers::TermUtil::hasInstConstAttr(gt)) { Node rep = qy->getRepresentative(gt); if (reps_found.find(rep) == reps_found.end()) { reps_found[rep] = gt; term_db_list[tn].push_back(gt); } } } ts = term_db_list[tn].size(); } else { ts = ittd->second.size(); } } // consider a default value if at full effort max_zero.push_back(fullEffort && ts == 0); ts = (fullEffort && ts == 0) ? 1 : ts; Trace("inst-alg-rd") << "Variable " << i << " has " << ts << " in relevant domain." << std::endl; if (ts == 0) { has_zero = true; break; } maxs.push_back(ts); if (ts > final_max_i) { final_max_i = ts; } } if (!has_zero) { Trace("inst-alg-rd") << "Will do " << final_max_i << " stages of instantiation." << std::endl; unsigned max_i = 0; bool success; Instantiate* ie = d_quantEngine->getInstantiate(); while (max_i <= final_max_i) { Trace("inst-alg-rd") << "Try stage " << max_i << "..." << std::endl; std::vector childIndex; int index = 0; do { while (index >= 0 && index < (int)f[0].getNumChildren()) { if (index == static_cast(childIndex.size())) { childIndex.push_back(-1); } else { Assert(index == static_cast(childIndex.size()) - 1); unsigned nv = childIndex[index] + 1; if (nv < maxs[index] && nv <= max_i) { childIndex[index] = nv; index++; } else { childIndex.pop_back(); index--; } } } success = index >= 0; if (success) { if (Trace.isOn("inst-alg-rd")) { Trace("inst-alg-rd") << "Try instantiation { "; for (unsigned i : childIndex) { Trace("inst-alg-rd") << i << " "; } Trace("inst-alg-rd") << "}" << std::endl; } // try instantiation std::vector terms; for (unsigned i = 0, nchild = f[0].getNumChildren(); i < nchild; i++) { if (max_zero[i]) { // no terms available, will report incomplete instantiation terms.push_back(Node::null()); Trace("inst-alg-rd") << " null" << std::endl; } else if (isRd) { terms.push_back(d_rd->getRDomain(f, i)->d_terms[childIndex[i]]); Trace("inst-alg-rd") << " (rd) " << d_rd->getRDomain(f, i)->d_terms[childIndex[i]] << std::endl; } else { Assert(childIndex[i] < term_db_list[ftypes[i]].size()); terms.push_back(term_db_list[ftypes[i]][childIndex[i]]); Trace("inst-alg-rd") << " " << term_db_list[ftypes[i]][childIndex[i]] << std::endl; } Assert(terms[i].isNull() || terms[i].getType().isComparableTo(ftypes[i])); } if (ie->addInstantiation(f, terms)) { Trace("inst-alg-rd") << "Success!" << std::endl; ++(d_quantEngine->d_statistics.d_instantiations_guess); return true; } else { index--; } if (d_quantEngine->inConflict()) { // could be conflicting for an internal reason (such as term // indices computed in above calls) return false; } } } while (success); max_i++; } } // TODO : term enumerator instantiation? return false; } } /* CVC4::theory::quantifiers namespace */ } /* CVC4::theory namespace */ } /* CVC4 namespace */