diff options
author | PaulMeng <pmtruth@hotmail.com> | 2016-07-05 13:56:53 -0400 |
---|---|---|
committer | PaulMeng <pmtruth@hotmail.com> | 2016-07-05 13:56:53 -0400 |
commit | 36a0d1d948f201471596e092136c5a00103f78af (patch) | |
tree | 7a9b0d79074da1cb0c1cbed986584d50792a30e9 /src/theory | |
parent | 66525e81928d0d025dbcc197ab3ef772eac31103 (diff) | |
parent | a58abbe71fb1fc07129ff9c7568ac544145fb57c (diff) |
Merge branch 'master' of https://github.com/CVC4/CVC4.git
Conflicts:
proofs/signatures/Makefile.am
src/Makefile.am
src/expr/datatype.cpp
src/options/datatypes_options
src/options/options_template.cpp
src/options/quantifiers_options
src/proof/arith_proof.cpp
src/proof/arith_proof.h
src/proof/array_proof.cpp
src/proof/array_proof.h
src/proof/bitvector_proof.cpp
src/proof/bitvector_proof.h
src/proof/cnf_proof.cpp
src/proof/cnf_proof.h
src/proof/proof_manager.cpp
src/proof/proof_manager.h
src/proof/sat_proof.h
src/proof/sat_proof_implementation.h
src/proof/skolemization_manager.h
src/proof/theory_proof.cpp
src/proof/theory_proof.h
src/proof/uf_proof.cpp
src/proof/uf_proof.h
src/prop/cnf_stream.cpp
src/prop/cnf_stream.h
src/prop/minisat/core/Solver.cc
src/prop/prop_engine.cpp
src/prop/prop_engine.h
src/prop/theory_proxy.cpp
src/smt/smt_engine.cpp
src/smt/smt_engine_check_proof.cpp
src/theory/arrays/array_proof_reconstruction.cpp
src/theory/arrays/theory_arrays.cpp
src/theory/bv/eager_bitblaster.cpp
src/theory/bv/lazy_bitblaster.cpp
src/theory/datatypes/theory_datatypes.cpp
src/theory/quantifiers/alpha_equivalence.cpp
src/theory/quantifiers/candidate_generator.cpp
src/theory/quantifiers/candidate_generator.h
src/theory/quantifiers/ce_guided_single_inv.cpp
src/theory/quantifiers/ceg_instantiator.cpp
src/theory/quantifiers/conjecture_generator.cpp
src/theory/quantifiers/equality_infer.cpp
src/theory/quantifiers/equality_infer.h
src/theory/quantifiers/inst_match_generator.cpp
src/theory/quantifiers/inst_propagator.cpp
src/theory/quantifiers/inst_propagator.h
src/theory/quantifiers/inst_strategy_e_matching.cpp
src/theory/quantifiers/inst_strategy_e_matching.h
src/theory/quantifiers/instantiation_engine.cpp
src/theory/quantifiers/model_builder.cpp
src/theory/quantifiers/model_engine.cpp
src/theory/quantifiers/quant_conflict_find.cpp
src/theory/quantifiers/quant_conflict_find.h
src/theory/quantifiers/quant_split.cpp
src/theory/quantifiers/quant_util.cpp
src/theory/quantifiers/quantifiers_rewriter.cpp
src/theory/quantifiers/quantifiers_rewriter.h
src/theory/quantifiers/term_database.cpp
src/theory/quantifiers/term_database.h
src/theory/quantifiers/trigger.cpp
src/theory/quantifiers/trigger.h
src/theory/quantifiers_engine.cpp
src/theory/quantifiers_engine.h
src/theory/sets/kinds
src/theory/sets/theory_sets_private.cpp
src/theory/sets/theory_sets_private.h
src/theory/sets/theory_sets_rewriter.cpp
src/theory/sets/theory_sets_type_rules.h
src/theory/strings/theory_strings.cpp
src/theory/strings/theory_strings.h
src/theory/theory_engine.cpp
src/theory/theory_engine.h
src/theory/uf/equality_engine.cpp
test/regress/regress0/fmf/Makefile.am
test/regress/regress0/quantifiers/Makefile.am
test/regress/regress0/strings/Makefile.am
test/regress/regress0/sygus/Makefile.am
test/regress/regress0/sygus/max2-univ.sy
Diffstat (limited to 'src/theory')
134 files changed, 6493 insertions, 2775 deletions
diff --git a/src/theory/arrays/array_proof_reconstruction.cpp b/src/theory/arrays/array_proof_reconstruction.cpp index 11c3dc081..6dfd14157 100644 --- a/src/theory/arrays/array_proof_reconstruction.cpp +++ b/src/theory/arrays/array_proof_reconstruction.cpp @@ -101,8 +101,59 @@ void ArrayProofReconstruction::notify(unsigned reasonType, Node reason, Node a, Debug("pf::ee") << "Getting explanation for ROW guard: " << indexOne << " != " << indexTwo << std::endl; + eq::EqProof* childProof = new eq::EqProof; d_equalityEngine->explainEquality(indexOne, indexTwo, false, equalities, childProof); + + // It could be that the guard condition is a constant disequality. In this case, + // we need to change it to a different format. + if (childProof->d_id == theory::eq::MERGED_THROUGH_CONSTANTS) { + // The proof has two children, explaining why each index is a (different) constant. + Assert(childProof->d_children.size() == 2); + + Node constantOne, constantTwo; + // Each subproof explains why one of the indices is constant. + + if (childProof->d_children[0]->d_id == theory::eq::MERGED_THROUGH_REFLEXIVITY) { + constantOne = childProof->d_children[0]->d_node; + } else { + Assert(childProof->d_children[0]->d_id == theory::eq::MERGED_THROUGH_EQUALITY); + if ((childProof->d_children[0]->d_node[0] == indexOne) || + (childProof->d_children[0]->d_node[0] == indexTwo)) { + constantOne = childProof->d_children[0]->d_node[1]; + } else { + constantOne = childProof->d_children[0]->d_node[0]; + } + } + + if (childProof->d_children[1]->d_id == theory::eq::MERGED_THROUGH_REFLEXIVITY) { + constantTwo = childProof->d_children[1]->d_node; + } else { + Assert(childProof->d_children[1]->d_id == theory::eq::MERGED_THROUGH_EQUALITY); + if ((childProof->d_children[1]->d_node[0] == indexOne) || + (childProof->d_children[1]->d_node[0] == indexTwo)) { + constantTwo = childProof->d_children[1]->d_node[1]; + } else { + constantTwo = childProof->d_children[1]->d_node[0]; + } + } + + eq::EqProof* constantDisequalityProof = new eq::EqProof; + constantDisequalityProof->d_id = theory::eq::MERGED_THROUGH_CONSTANTS; + constantDisequalityProof->d_node = + NodeManager::currentNM()->mkNode(kind::EQUAL, constantOne, constantTwo).negate(); + + // Middle is where we need to insert the new disequality + std::vector<eq::EqProof *>::iterator middle = childProof->d_children.begin(); + ++middle; + + childProof->d_children.insert(middle, constantDisequalityProof); + + childProof->d_id = theory::eq::MERGED_THROUGH_TRANS; + childProof->d_node = + NodeManager::currentNM()->mkNode(kind::EQUAL, indexOne, indexTwo).negate(); + } + proof->d_children.push_back(childProof); } else { // This is the case of (i == k) because ((a[i]:=t)[k] != a[k]), diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp index 6add1b55f..28a08630e 100644 --- a/src/theory/arrays/theory_arrays.cpp +++ b/src/theory/arrays/theory_arrays.cpp @@ -829,7 +829,8 @@ void TheoryArrays::propagate(Effort e) Node TheoryArrays::explain(TNode literal) { - return explain(literal, NULL); + Node explanation = explain(literal, NULL); + return explanation; } Node TheoryArrays::explain(TNode literal, eq::EqProof *proof) @@ -1394,6 +1395,7 @@ void TheoryArrays::check(Effort e) { break; default: Unreachable(); + break; } } @@ -2231,6 +2233,7 @@ bool TheoryArrays::dischargeLemmas() void TheoryArrays::conflict(TNode a, TNode b) { Debug("pf::array") << "TheoryArrays::Conflict called" << std::endl; eq::EqProof* proof = d_proofsEnabled ? new eq::EqProof() : NULL; + if (a.getKind() == kind::CONST_BOOLEAN) { d_conflictNode = explain(a.iffNode(b), proof); } else { diff --git a/src/theory/bv/abstraction.cpp b/src/theory/bv/abstraction.cpp index fdc36ce72..dc5520411 100644 --- a/src/theory/bv/abstraction.cpp +++ b/src/theory/bv/abstraction.cpp @@ -41,7 +41,7 @@ bool AbstractionModule::applyAbstraction(const std::vector<Node>& assertions, st continue; } Node signature = computeSignature(assertions[i][j]); - storeSignature(signature, assertions[i][j]); + storeSignature(signature, assertions[i][j]); Debug("bv-abstraction") << " assertion: " << assertions[i][j] <<"\n"; Debug("bv-abstraction") << " signature: " << signature <<"\n"; } @@ -52,7 +52,7 @@ bool AbstractionModule::applyAbstraction(const std::vector<Node>& assertions, st for (unsigned i = 0; i < assertions.size(); ++i) { if (assertions[i].getKind() == kind::OR && assertions[i][0].getKind() == kind::AND) { - std::vector<Node> new_children; + std::vector<Node> new_children; for (unsigned j = 0; j < assertions[i].getNumChildren(); ++j) { if (hasSignature(assertions[i][j])) { new_children.push_back(abstractSignatures(assertions[i][j])); @@ -60,10 +60,10 @@ bool AbstractionModule::applyAbstraction(const std::vector<Node>& assertions, st new_children.push_back(assertions[i][j]); } } - new_assertions.push_back(utils::mkNode(kind::OR, new_children)); + new_assertions.push_back(utils::mkNode(kind::OR, new_children)); } else { // assertions that are not changed - new_assertions.push_back(assertions[i]); + new_assertions.push_back(assertions[i]); } } @@ -71,21 +71,21 @@ bool AbstractionModule::applyAbstraction(const std::vector<Node>& assertions, st skolemizeArguments(new_assertions); } - + // if we are using the eager solver reverse the abstraction if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { if (d_funcToSignature.size() == 0) { // we did not change anything return false; } - NodeNodeMap seen; + NodeNodeMap seen; for (unsigned i = 0; i < new_assertions.size(); ++i) { - new_assertions[i] = reverseAbstraction(new_assertions[i], seen); + new_assertions[i] = reverseAbstraction(new_assertions[i], seen); } // we undo the abstraction functions so the logic is QF_BV still - return true; + return true; } - + // return true if we have created new function symbols for the problem return d_funcToSignature.size() != 0; } @@ -99,7 +99,7 @@ bool AbstractionModule::isConjunctionOfAtoms(TNode node) { bool AbstractionModule::isConjunctionOfAtomsRec(TNode node, TNodeSet& seen) { if (seen.find(node)!= seen.end()) return true; - + if (!node.getType().isBitVector()) { return (node.getKind() == kind::AND || utils::isBVPredicate(node)); } @@ -120,30 +120,30 @@ Node AbstractionModule::reverseAbstraction(Node assertion, NodeNodeMap& seen) { if (seen.find(assertion) != seen.end()) return seen[assertion]; - + if (isAbstraction(assertion)) { Node interp = getInterpretation(assertion); seen[assertion] = interp; - Assert (interp.getType() == assertion.getType()); + Assert (interp.getType() == assertion.getType()); return interp; } if (assertion.getNumChildren() == 0) { seen[assertion] = assertion; - return assertion; + return assertion; } - + NodeBuilder<> result(assertion.getKind()); if (assertion.getMetaKind() == kind::metakind::PARAMETERIZED) { - result << assertion.getOperator(); + result << assertion.getOperator(); } for (unsigned i = 0; i < assertion.getNumChildren(); ++i) { - result << reverseAbstraction(assertion[i], seen); + result << reverseAbstraction(assertion[i], seen); } Node res = result; seen[assertion] = res; - return res; + return res; } void AbstractionModule::skolemizeArguments(std::vector<Node>& assertions) { @@ -151,7 +151,7 @@ void AbstractionModule::skolemizeArguments(std::vector<Node>& assertions) { TNode assertion = assertions[i]; if (assertion.getKind() != kind::OR) continue; - + bool is_skolemizable = true; for (unsigned k = 0; k < assertion.getNumChildren(); ++k) { if (assertion[k].getKind() != kind::EQUAL || @@ -191,54 +191,54 @@ void AbstractionModule::skolemizeArguments(std::vector<Node>& assertions) { NodeBuilder<> skolem_func (kind::APPLY_UF); skolem_func << func; std::vector<Node> skolem_args; - + for (unsigned j = 0; j < args.getArity(); ++j) { bool all_same = true; for (unsigned k = 1; k < args.getNumEntries(); ++k) { if ( args.getEntry(k)[j] != args.getEntry(0)[j]) - all_same = false; + all_same = false; } - Node new_arg = all_same ? (Node)args.getEntry(0)[j] : utils::mkVar(utils::getSize(args.getEntry(0)[j])); + Node new_arg = all_same ? (Node)args.getEntry(0)[j] : utils::mkVar(utils::getSize(args.getEntry(0)[j])); skolem_args.push_back(new_arg); - skolem_func << new_arg; + skolem_func << new_arg; } - - Node skolem_func_eq1 = utils::mkNode(kind::EQUAL, (Node)skolem_func, utils::mkConst(1, 1u)); - + + Node skolem_func_eq1 = utils::mkNode(kind::EQUAL, (Node)skolem_func, utils::mkConst(1, 1u)); + // enumerate arguments assignments - std::vector<Node> or_assignments; + std::vector<Node> or_assignments; for (ArgsTableEntry::iterator it = args.begin(); it != args.end(); ++it) { NodeBuilder<> arg_assignment(kind::AND); ArgsVec& args = *it; for (unsigned k = 0; k < args.size(); ++k) { Node eq = utils::mkNode(kind::EQUAL, args[k], skolem_args[k]); - arg_assignment << eq; + arg_assignment << eq; } or_assignments.push_back(arg_assignment); } - + Node new_func_def = utils::mkNode(kind::AND, skolem_func_eq1, utils::mkNode(kind::OR, or_assignments)); - assertion_builder << new_func_def; + assertion_builder << new_func_def; } Node new_assertion = assertion_builder; Debug("bv-abstraction-dbg") << "AbstractionModule::skolemizeArguments " << assertions[i] << " => \n"; - Debug("bv-abstraction-dbg") << " " << new_assertion; + Debug("bv-abstraction-dbg") << " " << new_assertion; assertions[i] = new_assertion; } } void AbstractionModule::storeSignature(Node signature, TNode assertion) { if(d_signatures.find(signature) == d_signatures.end()) { - d_signatures[signature] = 0; + d_signatures[signature] = 0; } - d_signatures[signature] = d_signatures[signature] + 1; - d_assertionToSignature[assertion] = signature; + d_signatures[signature] = d_signatures[signature] + 1; + d_assertionToSignature[assertion] = signature; } Node AbstractionModule::computeSignature(TNode node) { - resetSignatureIndex(); - NodeNodeMap cache; + resetSignatureIndex(); + NodeNodeMap cache; Node sig = computeSignatureRec(node, cache); return sig; } @@ -247,17 +247,17 @@ Node AbstractionModule::getSignatureSkolem(TNode node) { Assert (node.getKind() == kind::VARIABLE); unsigned bitwidth = utils::getSize(node); if (d_signatureSkolems.find(bitwidth) == d_signatureSkolems.end()) { - d_signatureSkolems[bitwidth] = vector<Node>(); + d_signatureSkolems[bitwidth] = vector<Node>(); } - + vector<Node>& skolems = d_signatureSkolems[bitwidth]; // get the index of bv variables of this size - unsigned index = getBitwidthIndex(bitwidth); + unsigned index = getBitwidthIndex(bitwidth); Assert (skolems.size() + 1 >= index ); if (skolems.size() == index) { ostringstream os; os << "sig_" <<bitwidth <<"_" << index; - NodeManager* nm = NodeManager::currentNM(); + NodeManager* nm = NodeManager::currentNM(); skolems.push_back(nm->mkSkolem(os.str(), nm->mkBitVectorType(bitwidth), "skolem for computing signatures")); } ++(d_signatureIndices[bitwidth]); @@ -268,12 +268,12 @@ unsigned AbstractionModule::getBitwidthIndex(unsigned bitwidth) { if (d_signatureIndices.find(bitwidth) == d_signatureIndices.end()) { d_signatureIndices[bitwidth] = 0; } - return d_signatureIndices[bitwidth]; + return d_signatureIndices[bitwidth]; } void AbstractionModule::resetSignatureIndex() { for (IndexMap::iterator it = d_signatureIndices.begin(); it != d_signatureIndices.end(); ++it) { - it->second = 0; + it->second = 0; } } @@ -282,24 +282,24 @@ bool AbstractionModule::hasSignature(Node node) { } Node AbstractionModule::getGeneralizedSignature(Node node) { - NodeNodeMap::const_iterator it = d_assertionToSignature.find(node); + NodeNodeMap::const_iterator it = d_assertionToSignature.find(node); Assert (it != d_assertionToSignature.end()); - Node generalized_signature = getGeneralization(it->second); - return generalized_signature; + Node generalized_signature = getGeneralization(it->second); + return generalized_signature; } Node AbstractionModule::computeSignatureRec(TNode node, NodeNodeMap& cache) { if (cache.find(node) != cache.end()) { - return cache.find(node)->second; + return cache.find(node)->second; } - + if (node.getNumChildren() == 0) { if (node.getKind() == kind::CONST_BITVECTOR) return node; Node sig = getSignatureSkolem(node); - cache[node] = sig; - return sig; + cache[node] = sig; + return sig; } NodeBuilder<> builder(node.getKind()); @@ -308,30 +308,30 @@ Node AbstractionModule::computeSignatureRec(TNode node, NodeNodeMap& cache) { } for (unsigned i = 0; i < node.getNumChildren(); ++i) { Node converted = computeSignatureRec(node[i], cache); - builder << converted; + builder << converted; } Node result = builder; cache[node] = result; - return result; + return result; } -/** +/** * Returns 0, if the two are equal, * 1 if s is a generalization of t * 2 if t is a generalization of s * -1 if the two cannot be unified * - * @param s - * @param t - * - * @return + * @param s + * @param t + * + * @return */ int AbstractionModule::comparePatterns(TNode s, TNode t) { if (s.getKind() == kind::SKOLEM && t.getKind() == kind::SKOLEM) { return 0; } - + if (s.getKind() == kind::CONST_BITVECTOR && t.getKind() == kind::CONST_BITVECTOR) { if (s == t) { @@ -350,7 +350,7 @@ int AbstractionModule::comparePatterns(TNode s, TNode t) { t.getKind() == kind::SKOLEM) { return 2; } - + if (s.getNumChildren() != t.getNumChildren() || s.getKind() != t.getKind()) return -1; @@ -370,26 +370,26 @@ int AbstractionModule::comparePatterns(TNode s, TNode t) { } TNode AbstractionModule::getGeneralization(TNode term) { - NodeNodeMap::iterator it = d_sigToGeneralization.find(term); + NodeNodeMap::iterator it = d_sigToGeneralization.find(term); // if not in the map we add it if (it == d_sigToGeneralization.end()) { d_sigToGeneralization[term] = term; - return term; + return term; } - // doesn't have a generalization + // doesn't have a generalization if (it->second == term) return term; - + TNode generalization = getGeneralization(it->second); Assert (generalization != term); d_sigToGeneralization[term] = generalization; - return generalization; + return generalization; } void AbstractionModule::storeGeneralization(TNode s, TNode t) { Assert (s == getGeneralization(s)); Assert (t == getGeneralization(t)); - d_sigToGeneralization[s] = t; + d_sigToGeneralization[s] = t; } void AbstractionModule::finalizeSignatures() { @@ -402,29 +402,29 @@ void AbstractionModule::finalizeSignatures() { for (SignatureMap::const_iterator tt = ss; tt != d_signatures.end(); ++tt) { TNode t = getGeneralization(tt->first); TNode s = getGeneralization(ss->first); - + if (t != s) { int status = comparePatterns(s, t); - Assert (status); + Assert (status); if (status < 0) continue; if (status == 1) { - storeGeneralization(t, s); + storeGeneralization(t, s); } else { - storeGeneralization(s, t); + storeGeneralization(s, t); } } } } // keep only most general signatures for (SignatureMap::iterator it = d_signatures.begin(); it != d_signatures.end(); ) { - TNode sig = it->first; + TNode sig = it->first; TNode gen = getGeneralization(sig); if (sig != gen) { - Assert (d_signatures.find(gen) != d_signatures.end()); + Assert (d_signatures.find(gen) != d_signatures.end()); // update the count d_signatures[gen]+= d_signatures[sig]; - d_signatures.erase(it++); + d_signatures.erase(it++); } else { ++it; } @@ -434,12 +434,12 @@ void AbstractionModule::finalizeSignatures() { // remove signatures that are not frequent enough for (SignatureMap::iterator it = d_signatures.begin(); it != d_signatures.end(); ) { if (it->second <= 7) { - d_signatures.erase(it++); + d_signatures.erase(it++); } else { ++it; } } - + for (SignatureMap::const_iterator it = d_signatures.begin(); it != d_signatures.end(); ++it) { TNode signature = it->first; // we already processed this signature @@ -451,31 +451,31 @@ void AbstractionModule::finalizeSignatures() { collectArgumentTypes(signature, arg_types, seen); Assert (signature.getType().isBoolean()); // make function return a bitvector of size 1 - //Node bv_function = utils::mkNode(kind::ITE, signature, utils::mkConst(1, 1u), utils::mkConst(1, 0u)); + //Node bv_function = utils::mkNode(kind::ITE, signature, utils::mkConst(1, 1u), utils::mkConst(1, 0u)); TypeNode range = NodeManager::currentNM()->mkBitVectorType(1); - + TypeNode abs_type = nm->mkFunctionType(arg_types, range); Node abs_func = nm->mkSkolem("abs_$$", abs_type, "abstraction function for bv theory"); Debug("bv-abstraction") << " abstracted by function " << abs_func << "\n"; // NOTE: signature expression type is BOOLEAN d_signatureToFunc[signature] = abs_func; - d_funcToSignature[abs_func] = signature; + d_funcToSignature[abs_func] = signature; } d_statistics.d_numFunctionsAbstracted.setData(d_signatureToFunc.size()); - + Debug("bv-abstraction") << "AbstractionModule::finalizeSignatures abstracted " << d_signatureToFunc.size() << " signatures. \n"; } void AbstractionModule::collectArgumentTypes(TNode sig, std::vector<TypeNode>& types, TNodeSet& seen) { if (seen.find(sig) != seen.end()) return; - + if (sig.getKind() == kind::SKOLEM) { types.push_back(sig.getType()); - seen.insert(sig); - return; + seen.insert(sig); + return; } for (unsigned i = 0; i < sig.getNumChildren(); ++i) { @@ -487,36 +487,36 @@ void AbstractionModule::collectArgumentTypes(TNode sig, std::vector<TypeNode>& t void AbstractionModule::collectArguments(TNode node, TNode signature, std::vector<Node>& args, TNodeSet& seen) { if (seen.find(node)!= seen.end()) return; - + if (node.getKind() == kind::VARIABLE || node.getKind() == kind::CONST_BITVECTOR) { // a constant in the node can either map to an argument of the abstraction - // or can be hard-coded and part of the abstraction + // or can be hard-coded and part of the abstraction if (signature.getKind() == kind::SKOLEM) { args.push_back(node); seen.insert(node); } else { - Assert (signature.getKind() == kind::CONST_BITVECTOR); + Assert (signature.getKind() == kind::CONST_BITVECTOR); } - // - return; + // + return; } Assert (node.getKind() == signature.getKind() && - node.getNumChildren() == signature.getNumChildren()); + node.getNumChildren() == signature.getNumChildren()); for (unsigned i = 0; i < node.getNumChildren(); ++i) { - collectArguments(node[i], signature[i], args, seen); - seen.insert(node); + collectArguments(node[i], signature[i], args, seen); + seen.insert(node); } } Node AbstractionModule::abstractSignatures(TNode assertion) { - Debug("bv-abstraction") << "AbstractionModule::abstractSignatures "<< assertion <<"\n"; + Debug("bv-abstraction") << "AbstractionModule::abstractSignatures "<< assertion <<"\n"; // assume the assertion has been fully abstracted Node signature = getGeneralizedSignature(assertion); - - Debug("bv-abstraction") << " with sig "<< signature <<"\n"; + + Debug("bv-abstraction") << " with sig "<< signature <<"\n"; NodeNodeMap::iterator it = d_signatureToFunc.find(signature); if (it!= d_signatureToFunc.end()) { std::vector<Node> args; @@ -527,16 +527,16 @@ Node AbstractionModule::abstractSignatures(TNode assertion) { collectArguments(assertion, signature, args, seen); std::vector<TNode> real_args; for (unsigned i = 1; i < args.size(); ++i) { - real_args.push_back(args[i]); + real_args.push_back(args[i]); } - d_argsTable.addEntry(func, real_args); - Node result = utils::mkNode(kind::EQUAL, utils::mkNode(kind::APPLY_UF, args), + d_argsTable.addEntry(func, real_args); + Node result = utils::mkNode(kind::EQUAL, utils::mkNode(kind::APPLY_UF, args), utils::mkConst(1, 1u)); - Debug("bv-abstraction") << "=> "<< result << "\n"; - Assert (result.getType() == assertion.getType()); - return result; + Debug("bv-abstraction") << "=> "<< result << "\n"; + Assert (result.getType() == assertion.getType()); + return result; } - return assertion; + return assertion; } bool AbstractionModule::isAbstraction(TNode node) { @@ -557,11 +557,11 @@ bool AbstractionModule::isAbstraction(TNode node) { if (constant != utils::mkConst(1, 1u)) return false; - TNode func_symbol = func.getOperator(); + TNode func_symbol = func.getOperator(); if (d_funcToSignature.find(func_symbol) == d_funcToSignature.end()) return false; - return true; + return true; } Node AbstractionModule::getInterpretation(TNode node) { @@ -571,51 +571,51 @@ Node AbstractionModule::getInterpretation(TNode node) { Assert (constant.getKind() == kind::CONST_BITVECTOR && apply.getKind() == kind::APPLY_UF); - Node func = apply.getOperator(); + Node func = apply.getOperator(); Assert (d_funcToSignature.find(func) != d_funcToSignature.end()); - + Node sig = d_funcToSignature[func]; - + // substitute arguments in signature TNodeTNodeMap seen; unsigned index = 0; Node result = substituteArguments(sig, apply, index, seen); - Assert (result.getType().isBoolean()); + Assert (result.getType().isBoolean()); Assert (index == apply.getNumChildren()); // Debug("bv-abstraction") << "AbstractionModule::getInterpretation " << node << "\n"; // Debug("bv-abstraction") << " => " << result << "\n"; - return result; + return result; } Node AbstractionModule::substituteArguments(TNode signature, TNode apply, unsigned& index, TNodeTNodeMap& seen) { if (seen.find(signature) != seen.end()) { - return seen[signature]; + return seen[signature]; } - + if (signature.getKind() == kind::SKOLEM) { // return corresponding argument and increment counter seen[signature] = apply[index]; - return apply[index++]; + return apply[index++]; } if (signature.getNumChildren() == 0) { Assert (signature.getKind() != kind::VARIABLE && - signature.getKind() != kind::SKOLEM); + signature.getKind() != kind::SKOLEM); seen[signature] = signature; - return signature; + return signature; } - + NodeBuilder<> builder(signature.getKind()); if (signature.getMetaKind() == kind::metakind::PARAMETERIZED) { builder << signature.getOperator(); } - + for (unsigned i = 0; i < signature.getNumChildren(); ++i) { Node child = substituteArguments(signature[i], apply, index, seen); - builder << child; + builder << child; } - Node result = builder; + Node result = builder; seen[signature]= result; return result; @@ -625,20 +625,20 @@ Node AbstractionModule::simplifyConflict(TNode conflict) { if (Dump.isOn("bv-abstraction")) { NodeNodeMap seen; Node c = reverseAbstraction(conflict, seen); - Dump("bv-abstraction") << PushCommand(); + Dump("bv-abstraction") << PushCommand(); Dump("bv-abstraction") << AssertCommand(c.toExpr()); Dump("bv-abstraction") << CheckSatCommand(); - Dump("bv-abstraction") << PopCommand(); + Dump("bv-abstraction") << PopCommand(); } - Debug("bv-abstraction-dbg") << "AbstractionModule::simplifyConflict " << conflict << "\n"; + Debug("bv-abstraction-dbg") << "AbstractionModule::simplifyConflict " << conflict << "\n"; if (conflict.getKind() != kind::AND) - return conflict; + return conflict; std::vector<Node> conjuncts; for (unsigned i = 0; i < conflict.getNumChildren(); ++i) conjuncts.push_back(conflict[i]); - + theory::SubstitutionMap subst(new context::Context()); for (unsigned i = 0; i < conjuncts.size(); ++i) { TNode conjunct = conjuncts[i]; @@ -658,12 +658,12 @@ Node AbstractionModule::simplifyConflict(TNode conflict) { } else { continue; } - + Assert (!subst.hasSubstitution(s)); Assert (!t.isNull() && !s.isNull() && s!= t); - subst.addSubstitution(s, t); + subst.addSubstitution(s, t); for (unsigned k = 0; k < conjuncts.size(); k++) { conjuncts[k] = subst.apply(conjuncts[k]); @@ -671,28 +671,28 @@ Node AbstractionModule::simplifyConflict(TNode conflict) { } } Node new_conflict = Rewriter::rewrite(utils::mkAnd(conjuncts)); - + Debug("bv-abstraction") << "AbstractionModule::simplifyConflict conflict " << conflict <<"\n"; Debug("bv-abstraction") << " => " << new_conflict <<"\n"; if (Dump.isOn("bv-abstraction")) { - + NodeNodeMap seen; Node nc = reverseAbstraction(new_conflict, seen); - Dump("bv-abstraction") << PushCommand(); + Dump("bv-abstraction") << PushCommand(); Dump("bv-abstraction") << AssertCommand(nc.toExpr()); Dump("bv-abstraction") << CheckSatCommand(); - Dump("bv-abstraction") << PopCommand(); + Dump("bv-abstraction") << PopCommand(); } - - return new_conflict; + + return new_conflict; } void DebugPrintInstantiations(const std::vector< std::vector<ArgsVec> >& instantiations, const std::vector<TNode> functions) { // print header - Debug("bv-abstraction-dbg") <<"[ "; + Debug("bv-abstraction-dbg") <<"[ "; for (unsigned i = 0; i < functions.size(); ++i) { for (unsigned j = 1; j < functions[i].getNumChildren(); ++j) { Debug("bv-abstraction-dgb") << functions[i][j] <<" "; @@ -706,16 +706,16 @@ void DebugPrintInstantiations(const std::vector< std::vector<ArgsVec> >& instant const std::vector<ArgsVec>& inst = instantiations[i]; for (unsigned j = 0; j < inst.size(); ++j) { for (unsigned k = 0; k < inst[j].size(); ++k) { - Debug("bv-abstraction-dbg") << inst[j][k] << " "; + Debug("bv-abstraction-dbg") << inst[j][k] << " "; } - Debug("bv-abstraction-dbg") << " || "; + Debug("bv-abstraction-dbg") << " || "; } Debug("bv-abstraction-dbg") <<"]\n"; } } void AbstractionModule::generalizeConflict(TNode conflict, std::vector<Node>& lemmas) { - Debug("bv-abstraction") << "AbstractionModule::generalizeConflict " << conflict << "\n"; + Debug("bv-abstraction") << "AbstractionModule::generalizeConflict " << conflict << "\n"; std::vector<TNode> functions; // collect abstract functions @@ -737,11 +737,11 @@ void AbstractionModule::generalizeConflict(TNode conflict, std::vector<Node>& le // if (functions.size() >= 3) { // // dump conflict // NodeNodeMap seen; - // Node reversed = reverseAbstraction(conflict, seen); - // std::cout << "CONFLICT " << reversed << "\n"; + // Node reversed = reverseAbstraction(conflict, seen); + // std::cout << "CONFLICT " << reversed << "\n"; // } - + if (functions.size() == 0 || functions.size() > options::bvNumFunc()) { return; } @@ -751,31 +751,31 @@ void AbstractionModule::generalizeConflict(TNode conflict, std::vector<Node>& le SubstitutionMap skolem_subst(new context::Context()); SubstitutionMap reverse_skolem(new context::Context()); makeFreshSkolems(conflict, skolem_subst, reverse_skolem); - + Node skolemized_conflict = skolem_subst.apply(conflict); for (unsigned i = 0; i < functions.size(); ++i) { functions[i] = skolem_subst.apply(functions[i]); } - conflict = skolem_subst.apply(conflict); + conflict = skolem_subst.apply(conflict); LemmaInstantiatior inst(functions, d_argsTable, conflict); std::vector<Node> new_lemmas; - inst.generateInstantiations(new_lemmas); + inst.generateInstantiations(new_lemmas); for (unsigned i = 0; i < new_lemmas.size(); ++i) { TNode lemma = reverse_skolem.apply(new_lemmas[i]); if (d_addedLemmas.find(lemma) == d_addedLemmas.end()) { lemmas.push_back(lemma); - Debug("bv-abstraction-gen") << "adding lemma " << lemma << "\n"; + Debug("bv-abstraction-gen") << "adding lemma " << lemma << "\n"; storeLemma(lemma); if (Dump.isOn("bv-abstraction")) { NodeNodeMap seen; Node l = reverseAbstraction(lemma, seen); - Dump("bv-abstraction") << PushCommand(); + Dump("bv-abstraction") << PushCommand(); Dump("bv-abstraction") << AssertCommand(l.toExpr()); Dump("bv-abstraction") << CheckSatCommand(); - Dump("bv-abstraction") << PopCommand(); + Dump("bv-abstraction") << PopCommand(); } } } @@ -787,18 +787,18 @@ int AbstractionModule::LemmaInstantiatior::next(int val, int index) { return -1; } -/** +/** * Assumes the stack without top is consistent, and checks that the * full stack is consistent - * - * @param stack - * - * @return + * + * @param stack + * + * @return */ bool AbstractionModule::LemmaInstantiatior::isConsistent(const vector<int>& stack) { if (stack.empty()) return true; - + unsigned current = stack.size() - 1; TNode func = d_functions[current]; ArgsTableEntry& matches = d_argsTable.getEntry(func.getOperator()); @@ -807,12 +807,12 @@ bool AbstractionModule::LemmaInstantiatior::isConsistent(const vector<int>& stac for (unsigned k = 0; k < args.size(); ++k) { TNode s = func[k]; TNode t = args[k]; - + TNode s0 = s; while (d_subst.hasSubstitution(s0)) { s0 = d_subst.getSubstitution(s0); } - + TNode t0 = t; while (d_subst.hasSubstitution(t0)) { t0 = d_subst.getSubstitution(t0); @@ -824,7 +824,7 @@ bool AbstractionModule::LemmaInstantiatior::isConsistent(const vector<int>& stac else continue; } - + if(s0.getMetaKind() == kind::metakind::VARIABLE && t0.isConst()) { d_subst.addSubstitution(s0, t0); @@ -839,12 +839,12 @@ bool AbstractionModule::LemmaInstantiatior::isConsistent(const vector<int>& stac Assert (s0.getMetaKind() == kind::metakind::VARIABLE && t0.getMetaKind() == kind::metakind::VARIABLE); - + if (s0 != t0) { d_subst.addSubstitution(s0, t0); } } - return true; + return true; } bool AbstractionModule::LemmaInstantiatior::accept(const vector<int>& stack) { @@ -854,13 +854,13 @@ bool AbstractionModule::LemmaInstantiatior::accept(const vector<int>& stack) { void AbstractionModule::LemmaInstantiatior::mkLemma() { Node lemma = d_subst.apply(d_conflict); // Debug("bv-abstraction-gen") << "AbstractionModule::LemmaInstantiatior::mkLemma " << lemma <<"\n"; - d_lemmas.push_back(lemma); + d_lemmas.push_back(lemma); } void AbstractionModule::LemmaInstantiatior::backtrack(vector<int>& stack) { if (!isConsistent(stack)) return; - + if (accept(stack)) { mkLemma(); return; @@ -871,7 +871,7 @@ void AbstractionModule::LemmaInstantiatior::backtrack(vector<int>& stack) { d_ctx->push(); stack.push_back(x); backtrack(stack); - + d_ctx->pop(); stack.pop_back(); x = next(x, stack.size()); @@ -880,13 +880,13 @@ void AbstractionModule::LemmaInstantiatior::backtrack(vector<int>& stack) { void AbstractionModule::LemmaInstantiatior::generateInstantiations(std::vector<Node>& lemmas) { - Debug("bv-abstraction-gen") << "AbstractionModule::LemmaInstantiatior::generateInstantiations "; + Debug("bv-abstraction-gen") << "AbstractionModule::LemmaInstantiatior::generateInstantiations "; std::vector<int> stack; backtrack(stack); - Assert (d_ctx->getLevel() == 0); - Debug("bv-abstraction-gen") << "numLemmas=" << d_lemmas.size() <<"\n"; - lemmas.swap(d_lemmas); + Assert (d_ctx->getLevel() == 0); + Debug("bv-abstraction-gen") << "numLemmas=" << d_lemmas.size() <<"\n"; + lemmas.swap(d_lemmas); } void AbstractionModule::makeFreshSkolems(TNode node, SubstitutionMap& map, SubstitutionMap& reverse_map) { @@ -910,7 +910,7 @@ void AbstractionModule::makeFreshSkolems(TNode node, SubstitutionMap& map, Subst void AbstractionModule::makeFreshArgs(TNode func, std::vector<Node>& fresh_args) { Assert (fresh_args.size() == 0); Assert (func.getKind() == kind::APPLY_UF); - TNodeNodeMap d_map; + TNodeNodeMap d_map; for (unsigned i = 0; i < func.getNumChildren(); ++i) { TNode arg = func[i]; if (arg.isConst()) { @@ -918,9 +918,9 @@ void AbstractionModule::makeFreshArgs(TNode func, std::vector<Node>& fresh_args) continue; } Assert (arg.getMetaKind() == kind::metakind::VARIABLE); - TNodeNodeMap::iterator it = d_map.find(arg); + TNodeNodeMap::iterator it = d_map.find(arg); if (it != d_map.end()) { - fresh_args.push_back(it->second); + fresh_args.push_back(it->second); } else { Node skolem = utils::mkVar(utils::getSize(arg)); d_map[arg] = skolem; @@ -947,7 +947,7 @@ Node AbstractionModule::tryMatching(const std::vector<Node>& ss, const std::vect Debug("bv-abstraction-dbg") <<"\n"; } - + SubstitutionMap subst(new context::Context()); for (unsigned i = 0; i < ss.size(); ++i) { @@ -982,10 +982,10 @@ Node AbstractionModule::tryMatching(const std::vector<Node>& ss, const std::vect Assert (s0 != t0); subst.addSubstitution(s0, t0); } - + Node res = subst.apply(conflict); - Debug("bv-abstraction-dbg") << " Lemma: " << res <<"\n"; - return res; + Debug("bv-abstraction-dbg") << " Lemma: " << res <<"\n"; + return res; } void AbstractionModule::storeLemma(TNode lemma) { @@ -996,7 +996,7 @@ void AbstractionModule::storeLemma(TNode lemma) { atom = atom.getKind() == kind::NOT ? atom[0] : atom; Assert (atom.getKind() != kind::NOT); Assert (utils::isBVPredicate(atom)); - d_lemmaAtoms.insert(atom); + d_lemmaAtoms.insert(atom); } } else { lemma = lemma.getKind() == kind::NOT? lemma[0] : lemma; @@ -1009,9 +1009,9 @@ void AbstractionModule::storeLemma(TNode lemma) { bool AbstractionModule::isLemmaAtom(TNode node) const { Assert (node.getType().isBoolean()); node = node.getKind() == kind::NOT? node[0] : node; - + return d_inputAtoms.find(node) == d_inputAtoms.end() && - d_lemmaAtoms.find(node) != d_lemmaAtoms.end(); + d_lemmaAtoms.find(node) != d_lemmaAtoms.end(); } void AbstractionModule::addInputAtom(TNode atom) { @@ -1022,31 +1022,31 @@ void AbstractionModule::addInputAtom(TNode atom) { void AbstractionModule::ArgsTableEntry::addArguments(const ArgsVec& args) { Assert (args.size() == d_arity); - d_data.push_back(args); + d_data.push_back(args); } void AbstractionModule::ArgsTable::addEntry(TNode signature, const ArgsVec& args) { if (d_data.find(signature) == d_data.end()) { - d_data[signature] = ArgsTableEntry(args.size()); + d_data[signature] = ArgsTableEntry(args.size()); } ArgsTableEntry& entry = d_data[signature]; - entry.addArguments(args); + entry.addArguments(args); } bool AbstractionModule::ArgsTable::hasEntry(TNode signature) const { - return d_data.find(signature) != d_data.end(); + return d_data.find(signature) != d_data.end(); } AbstractionModule::ArgsTableEntry& AbstractionModule::ArgsTable::getEntry(TNode signature) { Assert (hasEntry(signature)); - return d_data.find(signature)->second; + return d_data.find(signature)->second; } -AbstractionModule::Statistics::Statistics() - : d_numFunctionsAbstracted("theory::bv::AbstractioModule::NumFunctionsAbstracted", 0) - , d_numArgsSkolemized("theory::bv::AbstractioModule::NumArgsSkolemized", 0) - , d_abstractionTime("theory::bv::AbstractioModule::AbstractionTime") +AbstractionModule::Statistics::Statistics(const std::string& name) + : d_numFunctionsAbstracted(name + "theory::bv::AbstractioModule::NumFunctionsAbstracted", 0) + , d_numArgsSkolemized(name + "theory::bv::AbstractioModule::NumArgsSkolemized", 0) + , d_abstractionTime(name + "theory::bv::AbstractioModule::AbstractionTime") { smtStatisticsRegistry()->registerStat(&d_numFunctionsAbstracted); smtStatisticsRegistry()->registerStat(&d_numArgsSkolemized); diff --git a/src/theory/bv/abstraction.h b/src/theory/bv/abstraction.h index 5d580f6ce..5d48b926e 100644 --- a/src/theory/bv/abstraction.h +++ b/src/theory/bv/abstraction.h @@ -38,11 +38,11 @@ class AbstractionModule { IntStat d_numFunctionsAbstracted; IntStat d_numArgsSkolemized; TimerStat d_abstractionTime; - Statistics(); + Statistics(const std::string& name); ~Statistics(); }; - + class ArgsTableEntry { std::vector<ArgsVec> d_data; unsigned d_arity; @@ -61,11 +61,11 @@ class AbstractionModule { unsigned getArity() { return d_arity; } unsigned getNumEntries() { return d_data.size(); } ArgsVec& getEntry(unsigned i ) { Assert (i < d_data.size()); return d_data[i]; } - }; + }; class ArgsTable { __gnu_cxx::hash_map<TNode, ArgsTableEntry, TNodeHashFunction > d_data; - bool hasEntry(TNode signature) const; + bool hasEntry(TNode signature) const; public: typedef __gnu_cxx::hash_map<TNode, ArgsTableEntry, TNodeHashFunction >::iterator iterator; ArgsTable() {} @@ -75,12 +75,12 @@ class AbstractionModule { iterator end() { return d_data.end(); } }; - /** + /** * Checks if one pattern is a generalization of the other - * - * @param s - * @param t - * + * + * @param s + * @param t + * * @return 1 if s :> t, 2 if s <: t, 0 if they equivalent and -1 if they are incomparable */ static int comparePatterns(TNode s, TNode t); @@ -93,7 +93,7 @@ class AbstractionModule { theory::SubstitutionMap d_subst; TNode d_conflict; std::vector<Node> d_lemmas; - + void backtrack(std::vector<int>& stack); int next(int val, int index); bool isConsistent(const std::vector<int>& stack); @@ -108,7 +108,7 @@ class AbstractionModule { , d_conflict(conflict) , d_lemmas() { - Debug("bv-abstraction-gen") << "LemmaInstantiator conflict:" << conflict << "\n"; + Debug("bv-abstraction-gen") << "LemmaInstantiator conflict:" << conflict << "\n"; // initializing the search space for (unsigned i = 0; i < functions.size(); ++i) { TNode func_op = functions[i].getOperator(); @@ -118,31 +118,31 @@ class AbstractionModule { } } - void generateInstantiations(std::vector<Node>& lemmas); - + void generateInstantiations(std::vector<Node>& lemmas); + }; - + typedef __gnu_cxx::hash_map<Node, std::vector<Node>, NodeHashFunction> NodeVecMap; typedef __gnu_cxx::hash_map<Node, TNode, NodeHashFunction> NodeTNodeMap; typedef __gnu_cxx::hash_map<TNode, TNode, TNodeHashFunction> TNodeTNodeMap; typedef __gnu_cxx::hash_map<Node, Node, NodeHashFunction> NodeNodeMap; typedef __gnu_cxx::hash_map<Node, TNode, NodeHashFunction> TNodeNodeMap; typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> TNodeSet; - typedef __gnu_cxx::hash_map<unsigned, Node> IntNodeMap; + typedef __gnu_cxx::hash_map<unsigned, Node> IntNodeMap; typedef __gnu_cxx::hash_map<unsigned, unsigned> IndexMap; typedef __gnu_cxx::hash_map<unsigned, std::vector<Node> > SkolemMap; typedef __gnu_cxx::hash_map<TNode, unsigned, TNodeHashFunction > SignatureMap; - - ArgsTable d_argsTable; + + ArgsTable d_argsTable; // mapping between signature and uninterpreted function symbol used to // abstract the signature NodeNodeMap d_signatureToFunc; - NodeNodeMap d_funcToSignature; + NodeNodeMap d_funcToSignature; - NodeNodeMap d_assertionToSignature; + NodeNodeMap d_assertionToSignature; SignatureMap d_signatures; - NodeNodeMap d_sigToGeneralization; + NodeNodeMap d_sigToGeneralization; TNodeSet d_skolems; // skolems maps @@ -165,7 +165,7 @@ class AbstractionModule { Node getGeneralizedSignature(Node node); Node getSignatureSkolem(TNode node); - unsigned getBitwidthIndex(unsigned bitwidth); + unsigned getBitwidthIndex(unsigned bitwidth); void resetSignatureIndex(); Node computeSignatureRec(TNode, NodeNodeMap&); void storeSignature(Node signature, TNode assertion); @@ -175,14 +175,14 @@ class AbstractionModule { // crazy instantiation methods void generateInstantiations(unsigned current, - std::vector<ArgsTableEntry>& matches, + std::vector<ArgsTableEntry>& matches, std::vector<std::vector<ArgsVec> >& instantiations, std::vector<std::vector<ArgsVec> >& new_instantiations); Node tryMatching(const std::vector<Node>& ss, const std::vector<TNode>& tt, TNode conflict); void makeFreshArgs(TNode func, std::vector<Node>& fresh_args); void makeFreshSkolems(TNode node, SubstitutionMap& map, SubstitutionMap& reverse_map); - + void skolemizeArguments(std::vector<Node>& assertions); Node reverseAbstraction(Node assertion, NodeNodeMap& seen); @@ -192,9 +192,9 @@ class AbstractionModule { void storeLemma(TNode lemma); Statistics d_statistics; - + public: - AbstractionModule() + AbstractionModule(const std::string& name) : d_argsTable() , d_signatureToFunc() , d_funcToSignature() @@ -207,34 +207,34 @@ public: , d_addedLemmas() , d_lemmaAtoms() , d_inputAtoms() - , d_statistics() + , d_statistics(name) {} - /** + /** * returns true if there are new uninterepreted functions symbols in the output - * - * @param assertions - * @param new_assertions - * - * @return + * + * @param assertions + * @param new_assertions + * + * @return */ bool applyAbstraction(const std::vector<Node>& assertions, std::vector<Node>& new_assertions); - /** - * Returns true if the node represents an abstraction predicate. - * - * @param node - * - * @return + /** + * Returns true if the node represents an abstraction predicate. + * + * @param node + * + * @return */ bool isAbstraction(TNode node); - /** - * Returns the interpretation of the abstraction predicate. - * - * @param node - * - * @return + /** + * Returns the interpretation of the abstraction predicate. + * + * @param node + * + * @return */ Node getInterpretation(TNode node); - Node simplifyConflict(TNode conflict); + Node simplifyConflict(TNode conflict); void generalizeConflict(TNode conflict, std::vector<Node>& lemmas); void addInputAtom(TNode atom); bool isLemmaAtom(TNode node) const; diff --git a/src/theory/bv/aig_bitblaster.cpp b/src/theory/bv/aig_bitblaster.cpp index 887daa1bd..37e9f4476 100644 --- a/src/theory/bv/aig_bitblaster.cpp +++ b/src/theory/bv/aig_bitblaster.cpp @@ -19,7 +19,7 @@ #include "options/bv_options.h" #include "prop/cnf_stream.h" #include "prop/sat_solver_factory.h" - +#include "smt/smt_statistics_registry.h" #ifdef CVC4_USE_ABC // Function is defined as static in ABC. Not sure how else to do this. @@ -140,10 +140,24 @@ AigBitblaster::AigBitblaster() , d_bbAtoms() , d_aigOutputNode(NULL) { - d_nullContext = new context::Context(); - d_satSolver = prop::SatSolverFactory::createMinisat(d_nullContext, "AigBitblaster"); - MinisatEmptyNotify* notify = new MinisatEmptyNotify(); - d_satSolver->setNotify(notify); + d_nullContext = new context::Context(); + switch(options::bvSatSolver()) { + case SAT_SOLVER_MINISAT: { + prop::BVSatSolverInterface* minisat = prop::SatSolverFactory::createMinisat(d_nullContext, + smtStatisticsRegistry(), + "AigBitblaster"); + MinisatEmptyNotify* notify = new MinisatEmptyNotify(); + minisat->setNotify(notify); + d_satSolver = minisat; + break; + } + case SAT_SOLVER_CRYPTOMINISAT: + d_satSolver = prop::SatSolverFactory::createCryptoMinisat(smtStatisticsRegistry(), + "AigBitblaster"); + break; + default: + Unreachable("Unknown SAT solver type"); + } } AigBitblaster::~AigBitblaster() { @@ -402,7 +416,7 @@ void AigBitblaster::assertToSatSolver(Cnf_Dat_t* pCnf) { prop::SatLiteral lit(sat_variables[index-1], int_lit < 0); clause.push_back(lit); } - d_satSolver->addClause(clause, false, RULE_INVALID); + d_satSolver->addClause(clause, false); } } diff --git a/src/theory/bv/bitblaster_template.h b/src/theory/bv/bitblaster_template.h index cfbadbf32..c6c0d6def 100644 --- a/src/theory/bv/bitblaster_template.h +++ b/src/theory/bv/bitblaster_template.h @@ -257,7 +257,7 @@ public: class EagerBitblaster : public TBitblaster<Node> { typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> TNodeSet; // sat solver used for bitblasting and associated CnfStream - prop::BVSatSolverInterface* d_satSolver; + prop::SatSolver* d_satSolver; BitblastingRegistrar* d_bitblastingRegistrar; context::Context* d_nullContext; prop::CnfStream* d_cnfStream; @@ -268,6 +268,8 @@ class EagerBitblaster : public TBitblaster<Node> { MinisatEmptyNotify d_notify; + MinisatEmptyNotify d_notify; + Node getModelFromSatSolver(TNode a, bool fullModel); bool isSharedTerm(TNode node); @@ -306,7 +308,7 @@ class AigBitblaster : public TBitblaster<Abc_Obj_t*> { static Abc_Ntk_t* abcAigNetwork; context::Context* d_nullContext; - prop::BVSatSolverInterface* d_satSolver; + prop::SatSolver* d_satSolver; TNodeAigMap d_aigCache; NodeAigMap d_bbAtoms; @@ -459,7 +461,7 @@ Node TBitblaster<T>::getTermModel(TNode node, bool fullModel) { if (Theory::isLeafOf(node, theory::THEORY_BV)) { // if it is a leaf may ask for fullModel - value = getModelFromSatSolver(node, fullModel); + value = getModelFromSatSolver(node, true); Debug("bv-equality-status")<< "TLazyBitblaster::getTermModel from VarValue" << node <<" => " << value <<"\n"; Assert ((fullModel && !value.isNull() && value.isConst()) || !fullModel); if (!value.isNull()) { diff --git a/src/theory/bv/bv_subtheory_algebraic.cpp b/src/theory/bv/bv_subtheory_algebraic.cpp index 00d337395..b7e973928 100644 --- a/src/theory/bv/bv_subtheory_algebraic.cpp +++ b/src/theory/bv/bv_subtheory_algebraic.cpp @@ -714,7 +714,8 @@ void AlgebraicSolver::collectModelInfo(TheoryModel* model, bool fullModel) { Assert (!value.isNull() || !fullModel); // may be a shared term that did not appear in the current assertions - if (!value.isNull()) { + // AJR: need to check whether already in map for cases where collectModelInfo is called multiple times in the same context + if (!value.isNull() && !d_modelMap->hasSubstitution(var)) { Debug("bitvector-model") << " " << var << " => " << value << "\n"; Assert (value.getKind() == kind::CONST_BITVECTOR); d_modelMap->addSubstitution(var, value); diff --git a/src/theory/bv/bv_subtheory_bitblast.cpp b/src/theory/bv/bv_subtheory_bitblast.cpp index b7619c4bb..6c6c13ee8 100644 --- a/src/theory/bv/bv_subtheory_bitblast.cpp +++ b/src/theory/bv/bv_subtheory_bitblast.cpp @@ -37,9 +37,9 @@ namespace bv { BitblastSolver::BitblastSolver(context::Context* c, TheoryBV* bv) : SubtheorySolver(c, bv), - d_bitblaster(new TLazyBitblaster(c, bv, "lazy")), + d_bitblaster(new TLazyBitblaster(c, bv, bv->getFullInstanceName() + "lazy")), d_bitblastQueue(c), - d_statistics(), + d_statistics(bv->getFullInstanceName()), d_validModelCache(c, true), d_lemmaAtomsQueue(c), d_useSatPropagation(options::bitvectorPropagate()), @@ -55,9 +55,9 @@ BitblastSolver::~BitblastSolver() { delete d_bitblaster; } -BitblastSolver::Statistics::Statistics() - : d_numCallstoCheck("theory::bv::BitblastSolver::NumCallsToCheck", 0) - , d_numBBLemmas("theory::bv::BitblastSolver::NumTimesLemmasBB", 0) +BitblastSolver::Statistics::Statistics(const std::string &instanceName) + : d_numCallstoCheck(instanceName + "theory::bv::BitblastSolver::NumCallsToCheck", 0) + , d_numBBLemmas(instanceName + "theory::bv::BitblastSolver::NumTimesLemmasBB", 0) { smtStatisticsRegistry()->registerStat(&d_numCallstoCheck); smtStatisticsRegistry()->registerStat(&d_numBBLemmas); @@ -68,8 +68,8 @@ BitblastSolver::Statistics::~Statistics() { } void BitblastSolver::setAbstraction(AbstractionModule* abs) { - d_abstractionModule = abs; - d_bitblaster->setAbstraction(abs); + d_abstractionModule = abs; + d_bitblaster->setAbstraction(abs); } void BitblastSolver::preRegister(TNode node) { @@ -117,7 +117,7 @@ void BitblastSolver::bitblastQueue() { bool BitblastSolver::check(Theory::Effort e) { Debug("bv-bitblast") << "BitblastSolver::check (" << e << ")\n"; - Assert(options::bitblastMode() == theory::bv::BITBLAST_MODE_LAZY); + Assert(options::bitblastMode() == theory::bv::BITBLAST_MODE_LAZY); ++(d_statistics.d_numCallstoCheck); @@ -135,10 +135,10 @@ bool BitblastSolver::check(Theory::Effort e) { // skip atoms that are the result of abstraction lemmas if (d_abstractionModule->isLemmaAtom(fact)) { d_lemmaAtomsQueue.push_back(fact); - continue; + continue; } } - + if (!d_bv->inConflict() && (!d_bv->wasPropagatedBySubtheory(fact) || d_bv->getPropagatingSubtheory(fact) != SUB_BITBLAST)) { // Some atoms have not been bit-blasted yet @@ -183,7 +183,7 @@ bool BitblastSolver::check(Theory::Effort e) { if (options::bvAbstraction() && e == Theory::EFFORT_FULL && d_lemmaAtomsQueue.size()) { - + // bit-blast lemma atoms while(!d_lemmaAtomsQueue.empty()) { TNode lemma_atom = d_lemmaAtomsQueue.front(); @@ -199,7 +199,7 @@ bool BitblastSolver::check(Theory::Effort e) { return false; } } - + Assert(!d_bv->inConflict()); bool ok = d_bitblaster->solve(); if (!ok) { @@ -210,9 +210,9 @@ bool BitblastSolver::check(Theory::Effort e) { ++(d_statistics.d_numBBLemmas); return false; } - + } - + return true; } @@ -228,9 +228,9 @@ void BitblastSolver::collectModelInfo(TheoryModel* m, bool fullModel) { Node BitblastSolver::getModelValue(TNode node) { if (d_bv->d_invalidateModelCache.get()) { - d_bitblaster->invalidateModelCache(); + d_bitblaster->invalidateModelCache(); } - d_bv->d_invalidateModelCache.set(false); + d_bv->d_invalidateModelCache.set(false); Node val = d_bitblaster->getTermModel(node, true); return val; } @@ -241,9 +241,9 @@ void BitblastSolver::setConflict(TNode conflict) { Node final_conflict = conflict; if (options::bitvectorQuickXplain() && conflict.getKind() == kind::AND) { - // std::cout << "Original conflict " << conflict.getNumChildren() << "\n"; + // std::cout << "Original conflict " << conflict.getNumChildren() << "\n"; final_conflict = d_quickXplain->minimizeConflict(conflict); - //std::cout << "Minimized conflict " << final_conflict.getNumChildren() << "\n"; + //std::cout << "Minimized conflict " << final_conflict.getNumChildren() << "\n"; } d_bv->setConflict(final_conflict); } @@ -256,4 +256,3 @@ void BitblastSolver::setProofLog( BitVectorProof * bvp ) { }/* namespace CVC4::theory::bv */ }/* namespace CVC4::theory */ }/* namespace CVC4 */ - diff --git a/src/theory/bv/bv_subtheory_bitblast.h b/src/theory/bv/bv_subtheory_bitblast.h index e9300138b..fe16d2702 100644 --- a/src/theory/bv/bv_subtheory_bitblast.h +++ b/src/theory/bv/bv_subtheory_bitblast.h @@ -37,7 +37,7 @@ class BitblastSolver : public SubtheorySolver { struct Statistics { IntStat d_numCallstoCheck; IntStat d_numBBLemmas; - Statistics(); + Statistics(const std::string &instanceName); ~Statistics(); }; /** Bitblaster */ @@ -71,8 +71,8 @@ public: Node getModelValue(TNode node); bool isComplete() { return true; } void bitblastQueue(); - void setAbstraction(AbstractionModule* module); - uint64_t computeAtomWeight(TNode atom); + void setAbstraction(AbstractionModule* module); + uint64_t computeAtomWeight(TNode atom); void setProofLog( BitVectorProof * bvp ); }; diff --git a/src/theory/bv/eager_bitblaster.cpp b/src/theory/bv/eager_bitblaster.cpp index 3b54e3794..53fb4f94b 100644 --- a/src/theory/bv/eager_bitblaster.cpp +++ b/src/theory/bv/eager_bitblaster.cpp @@ -47,15 +47,30 @@ EagerBitblaster::EagerBitblaster(TheoryBV* theory_bv) { d_bitblastingRegistrar = new BitblastingRegistrar(this); d_nullContext = new context::Context(); - - d_satSolver = prop::SatSolverFactory::createMinisat( - d_nullContext, smtStatisticsRegistry(), "EagerBitblaster"); + + switch(options::bvSatSolver()) { + case SAT_SOLVER_MINISAT: { + prop::BVSatSolverInterface* minisat = + prop::SatSolverFactory::createMinisat(d_nullContext, + smtStatisticsRegistry(), + "EagerBitblaster"); + MinisatEmptyNotify* notify = new MinisatEmptyNotify(); + minisat->setNotify(notify); + d_satSolver = minisat; + break; + } + case SAT_SOLVER_CRYPTOMINISAT: + d_satSolver = prop::SatSolverFactory::createCryptoMinisat(smtStatisticsRegistry(), + "EagerBitblaster"); + break; + default: + Unreachable("Unknown SAT solver type"); + } d_cnfStream = new prop::TseitinCnfStream( d_satSolver, d_bitblastingRegistrar, d_nullContext, options::proof(), "EagerBitblaster"); - d_satSolver->setNotify(&d_notify); d_bvp = NULL; } @@ -216,7 +231,7 @@ void EagerBitblaster::collectModelInfo(TheoryModel* m, bool fullModel) { // only shared terms could not have been bit-blasted Assert (hasBBTerm(var) || isSharedTerm(var)); - Node const_value = getModelFromSatSolver(var, fullModel); + Node const_value = getModelFromSatSolver(var, true); if(const_value != Node()) { Debug("bitvector-model") << "EagerBitblaster::collectModelInfo (assert (= " diff --git a/src/theory/bv/lazy_bitblaster.cpp b/src/theory/bv/lazy_bitblaster.cpp index c821b50cd..b549c329a 100644 --- a/src/theory/bv/lazy_bitblaster.cpp +++ b/src/theory/bv/lazy_bitblaster.cpp @@ -491,7 +491,7 @@ void TLazyBitblaster::collectModelInfo(TheoryModel* m, bool fullModel) { // only shared terms could not have been bit-blasted Assert (hasBBTerm(var) || isSharedTerm(var)); - Node const_value = getModelFromSatSolver(var, fullModel); + Node const_value = getModelFromSatSolver(var, true); Assert (const_value.isNull() || const_value.isConst()); if(const_value != Node()) { Debug("bitvector-model") << "TLazyBitblaster::collectModelInfo (assert (= " diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp index 2edadce72..fec93e033 100644 --- a/src/theory/bv/theory_bv.cpp +++ b/src/theory/bv/theory_bv.cpp @@ -44,25 +44,29 @@ namespace bv { TheoryBV::TheoryBV(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, - const LogicInfo& logicInfo) - : Theory(THEORY_BV, c, u, out, valuation, logicInfo), - d_context(c), - d_alreadyPropagatedSet(c), - d_sharedTermsSet(c), - d_subtheories(), - d_subtheoryMap(), - d_statistics(), - d_staticLearnCache(), - d_lemmasAdded(c, false), - d_conflict(c, false), - d_invalidateModelCache(c, true), - d_literalsToPropagate(c), - d_literalsToPropagateIndex(c, 0), - d_propagatedBy(c), - d_eagerSolver(NULL), - d_abstractionModule(new AbstractionModule()), - d_isCoreTheory(false), - d_calledPreregister(false) + const LogicInfo& logicInfo, std::string name) + : Theory(THEORY_BV, c, u, out, valuation, logicInfo, name), + d_context(c), + d_alreadyPropagatedSet(c), + d_sharedTermsSet(c), + d_subtheories(), + d_subtheoryMap(), + d_statistics(getFullInstanceName()), + d_staticLearnCache(), + d_BVDivByZero(), + d_BVRemByZero(), + d_funcToArgs(), + d_funcToSkolem(u), + d_lemmasAdded(c, false), + d_conflict(c, false), + d_invalidateModelCache(c, true), + d_literalsToPropagate(c), + d_literalsToPropagateIndex(c, 0), + d_propagatedBy(c), + d_eagerSolver(NULL), + d_abstractionModule(new AbstractionModule(getFullInstanceName())), + d_isCoreTheory(false), + d_calledPreregister(false) { if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { @@ -117,14 +121,14 @@ void TheoryBV::spendResource(unsigned ammount) throw(UnsafeInterruptException) { getOutputChannel().spendResource(ammount); } -TheoryBV::Statistics::Statistics(): - d_avgConflictSize("theory::bv::AvgBVConflictSize"), - d_solveSubstitutions("theory::bv::NumberOfSolveSubstitutions", 0), - d_solveTimer("theory::bv::solveTimer"), - d_numCallsToCheckFullEffort("theory::bv::NumberOfFullCheckCalls", 0), - d_numCallsToCheckStandardEffort("theory::bv::NumberOfStandardCheckCalls", 0), - d_weightComputationTimer("theory::bv::weightComputationTimer"), - d_numMultSlice("theory::bv::NumMultSliceApplied", 0) +TheoryBV::Statistics::Statistics(const std::string &name): + d_avgConflictSize(name + "theory::bv::AvgBVConflictSize"), + d_solveSubstitutions(name + "theory::bv::NumberOfSolveSubstitutions", 0), + d_solveTimer(name + "theory::bv::solveTimer"), + d_numCallsToCheckFullEffort(name + "theory::bv::NumberOfFullCheckCalls", 0), + d_numCallsToCheckStandardEffort(name + "theory::bv::NumberOfStandardCheckCalls", 0), + d_weightComputationTimer(name + "theory::bv::weightComputationTimer"), + d_numMultSlice(name + "theory::bv::NumMultSliceApplied", 0) { smtStatisticsRegistry()->registerStat(&d_avgConflictSize); smtStatisticsRegistry()->registerStat(&d_solveSubstitutions); @@ -175,30 +179,31 @@ Node TheoryBV::getBVDivByZero(Kind k, unsigned width) { } -void TheoryBV::collectNumerators(TNode term, TNodeSet& seen) { +void TheoryBV::collectFunctionSymbols(TNode term, TNodeSet& seen) { if (seen.find(term) != seen.end()) return; - if (term.getKind() == kind::BITVECTOR_ACKERMANIZE_UDIV) { - unsigned size = utils::getSize(term[0]); - if (d_BVDivByZeroAckerman.find(size) == d_BVDivByZeroAckerman.end()) { - d_BVDivByZeroAckerman[size] = TNodeSet(); - } - d_BVDivByZeroAckerman[size].insert(term[0]); - seen.insert(term); - } else if (term.getKind() == kind::BITVECTOR_ACKERMANIZE_UREM) { - unsigned size = utils::getSize(term[0]); - if (d_BVRemByZeroAckerman.find(size) == d_BVRemByZeroAckerman.end()) { - d_BVRemByZeroAckerman[size] = TNodeSet(); - } - d_BVRemByZeroAckerman[size].insert(term[0]); - seen.insert(term); + if (term.getKind() == kind::APPLY_UF) { + TNode func = term.getOperator(); + storeFunction(func, term); } for (unsigned i = 0; i < term.getNumChildren(); ++i) { - collectNumerators(term[i], seen); + collectFunctionSymbols(term[i], seen); } seen.insert(term); } +void TheoryBV::storeFunction(TNode func, TNode term) { + if (d_funcToArgs.find(func) == d_funcToArgs.end()) { + d_funcToArgs.insert(make_pair(func, NodeSet())); + } + NodeSet& set = d_funcToArgs[func]; + if (set.find(term) == set.end()) { + set.insert(term); + Node skolem = utils::mkVar(utils::getSize(term)); + d_funcToSkolem.addSubstitution(term, skolem); + } +} + void TheoryBV::mkAckermanizationAsssertions(std::vector<Node>& assertions) { Debug("bv-ackermanize") << "TheoryBV::mkAckermanizationAsssertions\n"; @@ -206,51 +211,44 @@ void TheoryBV::mkAckermanizationAsssertions(std::vector<Node>& assertions) { AlwaysAssert(!options::incrementalSolving()); TNodeSet seen; for (unsigned i = 0; i < assertions.size(); ++i) { - collectNumerators(assertions[i], seen); - } - - // process division UF - Debug("bv-ackermanize") << "Process division UF...\n"; - for (WidthToNumerators::const_iterator it = d_BVDivByZeroAckerman.begin(); it != d_BVDivByZeroAckerman.end(); ++it) { - const TNodeSet& numerators= it->second; - for (TNodeSet::const_iterator i = numerators.begin(); i != numerators.end(); ++i) { - TNodeSet::const_iterator j = i; - j++; - for (; j != numerators.end(); ++j) { - TNode arg1 = *i; - TNode arg2 = *j; - TNode acker1 = utils::mkNode(kind::BITVECTOR_ACKERMANIZE_UDIV, arg1); - TNode acker2 = utils::mkNode(kind::BITVECTOR_ACKERMANIZE_UDIV, arg2); - - Node arg_eq = utils::mkNode(kind::EQUAL, arg1, arg2); - Node acker_eq = utils::mkNode(kind::EQUAL, acker1, acker2); - Node lemma = utils::mkNode(kind::IMPLIES, arg_eq, acker_eq); - Debug("bv-ackermanize") << " " << lemma << "\n"; - assertions.push_back(lemma); - } - } + collectFunctionSymbols(assertions[i], seen); } - // process remainder UF - Debug("bv-ackermanize") << "Process remainder UF...\n"; - for (WidthToNumerators::const_iterator it = d_BVRemByZeroAckerman.begin(); it != d_BVRemByZeroAckerman.end(); ++it) { - const TNodeSet& numerators= it->second; - for (TNodeSet::const_iterator i = numerators.begin(); i != numerators.end(); ++i) { - TNodeSet::const_iterator j = i; - j++; - for (; j != numerators.end(); ++j) { - TNode arg1 = *i; - TNode arg2 = *j; - TNode acker1 = utils::mkNode(kind::BITVECTOR_ACKERMANIZE_UREM, arg1); - TNode acker2 = utils::mkNode(kind::BITVECTOR_ACKERMANIZE_UREM, arg2); - - Node arg_eq = utils::mkNode(kind::EQUAL, arg1, arg2); - Node acker_eq = utils::mkNode(kind::EQUAL, acker1, acker2); - Node lemma = utils::mkNode(kind::IMPLIES, arg_eq, acker_eq); - Debug("bv-ackermanize") << " " << lemma << "\n"; + + FunctionToArgs::const_iterator it = d_funcToArgs.begin(); + NodeManager* nm = NodeManager::currentNM(); + for (; it!= d_funcToArgs.end(); ++it) { + TNode func = it->first; + const NodeSet& args = it->second; + NodeSet::const_iterator it1 = args.begin(); + for ( ; it1 != args.end(); ++it1) { + for(NodeSet::const_iterator it2 = it1; it2 != args.end(); ++it2) { + TNode args1 = *it1; + TNode args2 = *it2; + + AlwaysAssert (args1.getKind() == kind::APPLY_UF && + args1.getOperator() == func); + AlwaysAssert (args2.getKind() == kind::APPLY_UF && + args2.getOperator() == func); + AlwaysAssert (args1.getNumChildren() == args2.getNumChildren()); + + std::vector<Node> eqs(args1.getNumChildren()); + + for (unsigned i = 0; i < args1.getNumChildren(); ++i) { + eqs[i] = nm->mkNode(kind::EQUAL, args1[i], args2[i]); + } + + Node args_eq = eqs.size() == 1 ? eqs[0] : nm->mkNode(kind::AND, eqs); + Node func_eq = nm->mkNode(kind::EQUAL, args1, args2); + Node lemma = nm->mkNode(kind::IMPLIES, args_eq, func_eq); assertions.push_back(lemma); } } } + + // replace applications of UF by skolems (FIXME for model building) + for(unsigned i = 0; i < assertions.size(); ++i) { + assertions[i] = d_funcToSkolem.apply(assertions[i]); + } } Node TheoryBV::expandDefinition(LogicRequest &logicRequest, Node node) { @@ -278,18 +276,18 @@ Node TheoryBV::expandDefinition(LogicRequest &logicRequest, Node node) { Node divTotalNumDen = nm->mkNode(node.getKind() == kind::BITVECTOR_UDIV ? kind::BITVECTOR_UDIV_TOTAL : kind::BITVECTOR_UREM_TOTAL, num, den); - if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { - // Ackermanize UF if using eager bit-blasting - Node ackerman_var = nm->mkNode(node.getKind() == kind::BITVECTOR_UDIV ? kind::BITVECTOR_ACKERMANIZE_UDIV : kind::BITVECTOR_ACKERMANIZE_UREM, num); - node = nm->mkNode(kind::ITE, den_eq_0, ackerman_var, divTotalNumDen); - return node; - } else { + // if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { + // // Ackermanize UF if using eager bit-blasting + // Node ackerman_var = nm->mkNode(node.getKind() == kind::BITVECTOR_UDIV ? kind::BITVECTOR_ACKERMANIZE_UDIV : kind::BITVECTOR_ACKERMANIZE_UREM, num); + // node = nm->mkNode(kind::ITE, den_eq_0, ackerman_var, divTotalNumDen); + // return node; + // } else { Node divByZero = getBVDivByZero(node.getKind(), width); Node divByZeroNum = nm->mkNode(kind::APPLY_UF, divByZero, num); node = nm->mkNode(kind::ITE, den_eq_0, divByZeroNum, divTotalNumDen); logicRequest.widenLogic(THEORY_UF); return node; - } + //} } break; @@ -331,7 +329,7 @@ void TheoryBV::sendConflict() { if (d_conflictNode.isNull()) { return; } else { - Debug("bitvector") << indent() << "TheoryBV::check(): conflict " << d_conflictNode; + Debug("bitvector") << indent() << "TheoryBV::check(): conflict " << d_conflictNode << std::endl; d_out->conflict(d_conflictNode); d_statistics.d_avgConflictSize.addEntry(d_conflictNode.getNumChildren()); d_conflictNode = Node::null(); @@ -707,7 +705,7 @@ Node TheoryBV::explain(TNode node) { // return the explanation Node explanation = utils::mkAnd(assumptions); Debug("bitvector::explain") << "TheoryBV::explain(" << node << ") => " << explanation << std::endl; - Debug("bitvector::explain") << "TheoryBV::explain done. \n"; + Debug("bitvector::explain") << "TheoryBV::explain done. \n"; return explanation; } @@ -725,6 +723,8 @@ void TheoryBV::addSharedTerm(TNode t) { EqualityStatus TheoryBV::getEqualityStatus(TNode a, TNode b) { + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) + return EQUALITY_UNKNOWN; Assert (options::bitblastMode() == theory::bv::BITBLAST_MODE_LAZY); for (unsigned i = 0; i < d_subtheories.size(); ++i) { EqualityStatus status = d_subtheories[i]->getEqualityStatus(a, b); diff --git a/src/theory/bv/theory_bv.h b/src/theory/bv/theory_bv.h index 0bbcba9b0..ba2a4fc2a 100644 --- a/src/theory/bv/theory_bv.h +++ b/src/theory/bv/theory_bv.h @@ -56,7 +56,8 @@ class TheoryBV : public Theory { public: TheoryBV(context::Context* c, context::UserContext* u, OutputChannel& out, - Valuation valuation, const LogicInfo& logicInfo); + Valuation valuation, const LogicInfo& logicInfo, + std::string name = ""); ~TheoryBV(); @@ -88,8 +89,8 @@ public: void presolve(); - bool applyAbstraction(const std::vector<Node>& assertions, std::vector<Node>& new_assertions); - + bool applyAbstraction(const std::vector<Node>& assertions, std::vector<Node>& new_assertions); + void setProofLog( BitVectorProof * bvp ); private: @@ -100,10 +101,10 @@ private: IntStat d_solveSubstitutions; TimerStat d_solveTimer; IntStat d_numCallsToCheckFullEffort; - IntStat d_numCallsToCheckStandardEffort; + IntStat d_numCallsToCheckStandardEffort; TimerStat d_weightComputationTimer; IntStat d_numMultSlice; - Statistics(); + Statistics(const std::string &name); ~Statistics(); }; @@ -121,12 +122,12 @@ private: */ Node getBVDivByZero(Kind k, unsigned width); - typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> TNodeSet; - void collectNumerators(TNode term, TNodeSet& seen); - + typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> TNodeSet; + void collectFunctionSymbols(TNode term, TNodeSet& seen); + void storeFunction(TNode func, TNode term); typedef __gnu_cxx::hash_set<Node, NodeHashFunction> NodeSet; NodeSet d_staticLearnCache; - + /** * Maps from bit-vector width to division-by-zero uninterpreted * function symbols. @@ -134,17 +135,15 @@ private: __gnu_cxx::hash_map<unsigned, Node> d_BVDivByZero; __gnu_cxx::hash_map<unsigned, Node> d_BVRemByZero; - /** - * Maps from bit-vector width to numerators - * of uninterpreted function symbol - */ - typedef __gnu_cxx::hash_map<unsigned, TNodeSet > WidthToNumerators; - WidthToNumerators d_BVDivByZeroAckerman; - WidthToNumerators d_BVRemByZeroAckerman; + typedef __gnu_cxx::hash_map<Node, NodeSet, NodeHashFunction> FunctionToArgs; + typedef __gnu_cxx::hash_map<Node, Node, NodeHashFunction> NodeToNode; + // for ackermanization + FunctionToArgs d_funcToArgs; + CVC4::theory::SubstitutionMap d_funcToSkolem; context::CDO<bool> d_lemmasAdded; - + // Are we in conflict? context::CDO<bool> d_conflict; @@ -167,17 +166,17 @@ private: typedef context::CDHashMap<Node, SubTheory, NodeHashFunction> PropagatedMap; PropagatedMap d_propagatedBy; - EagerBitblastSolver* d_eagerSolver; + EagerBitblastSolver* d_eagerSolver; AbstractionModule* d_abstractionModule; bool d_isCoreTheory; bool d_calledPreregister; - + bool wasPropagatedBySubtheory(TNode literal) const { - return d_propagatedBy.find(literal) != d_propagatedBy.end(); + return d_propagatedBy.find(literal) != d_propagatedBy.end(); } - + SubTheory getPropagatingSubtheory(TNode literal) const { - Assert(wasPropagatedBySubtheory(literal)); + Assert(wasPropagatedBySubtheory(literal)); PropagatedMap::const_iterator find = d_propagatedBy.find(literal); return (*find).second; } @@ -193,7 +192,7 @@ private: void addSharedTerm(TNode t); bool isSharedTerm(TNode t) { return d_sharedTermsSet.contains(t); } - + EqualityStatus getEqualityStatus(TNode a, TNode b); Node getModelValue(TNode var); @@ -214,7 +213,7 @@ private: void lemma(TNode node) { d_out->lemma(node, RULE_CONFLICT); d_lemmasAdded = true; } - void checkForLemma(TNode node); + void checkForLemma(TNode node); friend class LazyBitblaster; friend class TLazyBitblaster; diff --git a/src/theory/bv/theory_bv_rewrite_rules_operator_elimination.h b/src/theory/bv/theory_bv_rewrite_rules_operator_elimination.h index 152a335a5..2bcb6ca1b 100644 --- a/src/theory/bv/theory_bv_rewrite_rules_operator_elimination.h +++ b/src/theory/bv/theory_bv_rewrite_rules_operator_elimination.h @@ -349,7 +349,7 @@ Node RewriteRule<SdivEliminate>::apply(TNode node) { Node abs_a = utils::mkNode(kind::ITE, a_lt_0, utils::mkNode(kind::BITVECTOR_NEG, a), a); Node abs_b = utils::mkNode(kind::ITE, b_lt_0, utils::mkNode(kind::BITVECTOR_NEG, b), b); - Node a_udiv_b = utils::mkNode(kind::BITVECTOR_UDIV, abs_a, abs_b); + Node a_udiv_b = utils::mkNode(options::bitvectorDivByZeroConst() ? kind::BITVECTOR_UDIV_TOTAL : kind::BITVECTOR_UDIV, abs_a, abs_b); Node neg_result = utils::mkNode(kind::BITVECTOR_NEG, a_udiv_b); Node condition = utils::mkNode(kind::XOR, a_lt_0, b_lt_0); @@ -377,7 +377,7 @@ Node RewriteRule<SremEliminate>::apply(TNode node) { Node abs_a = utils::mkNode(kind::ITE, a_lt_0, utils::mkNode(kind::BITVECTOR_NEG, a), a); Node abs_b = utils::mkNode(kind::ITE, b_lt_0, utils::mkNode(kind::BITVECTOR_NEG, b), b); - Node a_urem_b = utils::mkNode(kind::BITVECTOR_UREM, abs_a, abs_b); + Node a_urem_b = utils::mkNode( options::bitvectorDivByZeroConst() ? kind::BITVECTOR_UREM_TOTAL : kind::BITVECTOR_UREM, abs_a, abs_b); Node neg_result = utils::mkNode(kind::BITVECTOR_NEG, a_urem_b); Node result = utils::mkNode(kind::ITE, a_lt_0, neg_result, a_urem_b); diff --git a/src/theory/datatypes/datatypes_sygus.cpp b/src/theory/datatypes/datatypes_sygus.cpp index 5bd6680f2..4514453db 100644 --- a/src/theory/datatypes/datatypes_sygus.cpp +++ b/src/theory/datatypes/datatypes_sygus.cpp @@ -402,6 +402,60 @@ void SygusSplit::registerSygusTypeConstructorArg( TypeNode tnn, const Datatype& } } +class ReqTrie { +public: + ReqTrie() : d_req_kind( UNDEFINED_KIND ){} + std::map< unsigned, ReqTrie > d_children; + Kind d_req_kind; + TypeNode d_req_type; + Node d_req_const; + void print( const char * c, int indent = 0 ){ + if( d_req_kind!=UNDEFINED_KIND ){ + Trace(c) << d_req_kind << " "; + }else if( !d_req_type.isNull() ){ + Trace(c) << d_req_type; + }else if( !d_req_const.isNull() ){ + Trace(c) << d_req_const; + }else{ + Trace(c) << "_"; + } + Trace(c) << std::endl; + for( std::map< unsigned, ReqTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){ + for( int i=0; i<=indent; i++ ) { Trace(c) << " "; } + Trace(c) << it->first << " : "; + it->second.print( c, indent+1 ); + } + } + bool satisfiedBy( quantifiers::TermDbSygus * tdb, TypeNode tn ){ + if( d_req_kind!=UNDEFINED_KIND ){ + int c = tdb->getKindArg( tn, d_req_kind ); + if( c!=-1 ){ + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + for( std::map< unsigned, ReqTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){ + if( it->first<dt[c].getNumArgs() ){ + TypeNode tnc = tdb->getArgType( dt[c], it->first ); + if( !it->second.satisfiedBy( tdb, tnc ) ){ + return false; + } + }else{ + return false; + } + } + return true; + }else{ + return false; + } + }else if( !d_req_const.isNull() ){ + return tdb->hasConst( tn, d_req_const ); + }else if( !d_req_type.isNull() ){ + return tn==d_req_type; + }else{ + return true; + } + } +}; + + //this function gets all easy redundant cases, before consulting rewriters bool SygusSplit::considerSygusSplitKind( const Datatype& dt, const Datatype& pdt, TypeNode tn, TypeNode tnp, Kind k, Kind parent, int arg ) { Assert( d_tds->hasKind( tn, k ) ); @@ -419,101 +473,168 @@ bool SygusSplit::considerSygusSplitKind( const Datatype& dt, const Datatype& pdt return arg==firstArg; } } - //push + //describes the shape of an alternate term to construct + // we check whether this term is in the sygus grammar below + ReqTrie rt; + bool rt_valid = false; + + //construct rt by cases if( parent==NOT || parent==BITVECTOR_NOT || parent==UMINUS || parent==BITVECTOR_NEG ){ - //negation normal form - if( parent==k && isArgDatatype( dt[c], 0, pdt ) ){ - return false; - } - Kind nk = UNDEFINED_KIND; - Kind reqk = UNDEFINED_KIND; //required kind for all children - if( parent==NOT ){ - if( k==AND ) { - nk = OR;reqk = NOT; - }else if( k==OR ){ - nk = AND;reqk = NOT; - }else if( k==IFF ) { - nk = XOR; - }else if( k==XOR ) { - nk = IFF; - } - } - if( parent==BITVECTOR_NOT ){ - if( k==BITVECTOR_AND ) { - nk = BITVECTOR_OR;reqk = BITVECTOR_NOT; - }else if( k==BITVECTOR_OR ){ - nk = BITVECTOR_AND;reqk = BITVECTOR_NOT; - }else if( k==BITVECTOR_XNOR ) { - nk = BITVECTOR_XOR; - }else if( k==BITVECTOR_XOR ) { - nk = BITVECTOR_XNOR; - } - } - if( parent==UMINUS ){ - if( k==PLUS ){ - nk = PLUS;reqk = UMINUS; - } - } - if( parent==BITVECTOR_NEG ){ - if( k==PLUS ){ - nk = PLUS;reqk = BITVECTOR_NEG; - } - } - if( nk!=UNDEFINED_KIND ){ - Trace("sygus-split-debug") << "Push " << parent << " over " << k << " to " << nk; - if( reqk!=UNDEFINED_KIND ){ - Trace("sygus-split-debug") << ", reqk = " << reqk; + rt_valid = true; + //negation normal form + if( parent==k ){ + rt.d_req_type = d_tds->getArgType( dt[c], 0 ); + }else{ + Kind reqk = UNDEFINED_KIND; //required kind for all children + std::map< unsigned, Kind > reqkc; //required kind for some children + if( parent==NOT ){ + if( k==AND ) { + rt.d_req_kind = OR;reqk = NOT; + }else if( k==OR ){ + rt.d_req_kind = AND;reqk = NOT; + }else if( k==IFF ) { + rt.d_req_kind = XOR; + }else if( k==XOR ) { + rt.d_req_kind = IFF; + }else if( k==ITE ){ + rt.d_req_kind = ITE;reqkc[1] = NOT;reqkc[2] = NOT; + rt.d_children[0].d_req_type = d_tds->getArgType( dt[c], 0 ); + }else if( k==LEQ || k==GT ){ + // (not (~ x y)) -----> (~ (+ y 1) x) + rt.d_req_kind = k; + rt.d_children[0].d_req_kind = PLUS; + rt.d_children[0].d_children[0].d_req_type = d_tds->getArgType( dt[c], 1 ); + rt.d_children[0].d_children[1].d_req_const = NodeManager::currentNM()->mkConst( Rational( 1 ) ); + rt.d_children[1].d_req_type = d_tds->getArgType( dt[c], 0 ); + //TODO: other possibilities? + }else if( k==LT || k==GEQ ){ + // (not (~ x y)) -----> (~ y (+ x 1)) + rt.d_req_kind = k; + rt.d_children[0].d_req_type = d_tds->getArgType( dt[c], 1 ); + rt.d_children[1].d_req_kind = PLUS; + rt.d_children[1].d_children[0].d_req_type = d_tds->getArgType( dt[c], 0 ); + rt.d_children[1].d_children[1].d_req_const = NodeManager::currentNM()->mkConst( Rational( 1 ) ); + }else{ + rt_valid = false; + } + }else if( parent==BITVECTOR_NOT ){ + if( k==BITVECTOR_AND ) { + rt.d_req_kind = BITVECTOR_OR;reqk = BITVECTOR_NOT; + }else if( k==BITVECTOR_OR ){ + rt.d_req_kind = BITVECTOR_AND;reqk = BITVECTOR_NOT; + }else if( k==BITVECTOR_XNOR ) { + rt.d_req_kind = BITVECTOR_XOR; + }else if( k==BITVECTOR_XOR ) { + rt.d_req_kind = BITVECTOR_XNOR; + }else{ + rt_valid = false; + } + }else if( parent==UMINUS ){ + if( k==PLUS ){ + rt.d_req_kind = PLUS;reqk = UMINUS; + }else{ + rt_valid = false; + } + }else if( parent==BITVECTOR_NEG ){ + if( k==PLUS ){ + rt.d_req_kind = PLUS;reqk = BITVECTOR_NEG; + }else{ + rt_valid = false; + } } - Trace("sygus-split-debug") << "?" << std::endl; - int pcr = d_tds->getKindArg( tnp, nk ); - if( pcr!=-1 ){ - Assert( pcr<(int)pdt.getNumConstructors() ); - if( reqk!=UNDEFINED_KIND ){ + if( rt_valid && ( reqk!=UNDEFINED_KIND || !reqkc.empty() ) ){ + int pcr = d_tds->getKindArg( tnp, rt.d_req_kind ); + if( pcr!=-1 ){ + Assert( pcr<(int)pdt.getNumConstructors() ); //must have same number of arguments if( pdt[pcr].getNumArgs()==dt[c].getNumArgs() ){ - bool success = true; - std::map< int, TypeNode > childTypes; for( unsigned i=0; i<pdt[pcr].getNumArgs(); i++ ){ - TypeNode tna = d_tds->getArgType( pdt[pcr], i ); - Assert( datatypes::DatatypesRewriter::isTypeDatatype( tna ) ); - if( reqk!=UNDEFINED_KIND ){ - //child must have a NOT - int nindex = d_tds->getKindArg( tna, reqk ); - if( nindex!=-1 ){ - const Datatype& adt = ((DatatypeType)(tn).toType()).getDatatype(); - if( d_tds->getArgType( dt[c], i )!=d_tds->getArgType( adt[nindex], 0 ) ){ - Trace("sygus-split-debug") << "...arg " << i << " type mismatch." << std::endl; - success = false; - break; - } - }else{ - Trace("sygus-split-debug") << "...argument " << i << " does not have " << reqk << "." << std::endl; - success = false; - break; + Kind rk = reqk; + if( reqk==UNDEFINED_KIND ){ + std::map< unsigned, Kind >::iterator itr = reqkc.find( i ); + if( itr!=reqkc.end() ){ + rk = itr->second; } - }else{ - childTypes[i] = tna; } - } - if( success ){ - Trace("sygus-split-debug") << "...success" << std::endl; - return false; + if( rk!=UNDEFINED_KIND ){ + rt.d_children[i].d_req_kind = rk; + rt.d_children[i].d_children[0].d_req_type = d_tds->getArgType( dt[c], i ); + } } }else{ - Trace("sygus-split-debug") << "...#arg mismatch." << std::endl; + rt_valid = false; } }else{ - return !isTypeMatch( pdt[pcr], dt[c] ); + rt_valid = false; } - }else{ - Trace("sygus-split-debug") << "...operator not available." << std::endl; } } + }else if( k==MINUS || k==BITVECTOR_SUB ){ + if( parent==EQUAL || + parent==MINUS || parent==BITVECTOR_SUB || + parent==LEQ || parent==LT || parent==GEQ || parent==GT ){ + int oarg = arg==0 ? 1 : 0; + // (~ x (- y z)) ----> (~ (+ x z) y) + // (~ (- y z) x) ----> (~ y (+ x z)) + rt.d_req_kind = parent; + rt.d_children[arg].d_req_type = d_tds->getArgType( dt[c], 0 ); + rt.d_children[oarg].d_req_kind = k==MINUS ? PLUS : BITVECTOR_PLUS; + rt.d_children[oarg].d_children[0].d_req_type = d_tds->getArgType( pdt[pc], oarg ); + rt.d_children[oarg].d_children[1].d_req_type = d_tds->getArgType( dt[c], 1 ); + rt_valid = true; + }else if( parent==PLUS || parent==BITVECTOR_PLUS ){ + // (+ x (- y z)) -----> (- (+ x y) z) + // (+ (- y z) x) -----> (- (+ x y) z) + rt.d_req_kind = parent==PLUS ? MINUS : BITVECTOR_SUB; + int oarg = arg==0 ? 1 : 0; + rt.d_children[0].d_req_kind = parent; + rt.d_children[0].d_children[0].d_req_type = d_tds->getArgType( pdt[pc], oarg ); + rt.d_children[0].d_children[1].d_req_type = d_tds->getArgType( dt[c], 0 ); + rt.d_children[1].d_req_type = d_tds->getArgType( dt[c], 1 ); + rt_valid = true; + } + }else if( k==ITE ){ + if( parent!=ITE ){ + // (o X (ite y z w) X') -----> (ite y (o X z X') (o X w X')) + rt.d_req_kind = ITE; + rt.d_children[0].d_req_type = d_tds->getArgType( dt[c], 0 ); + unsigned n_args = pdt[pc].getNumArgs(); + for( unsigned r=1; r<=2; r++ ){ + rt.d_children[r].d_req_kind = parent; + for( unsigned q=0; q<n_args; q++ ){ + if( (int)q==arg ){ + rt.d_children[r].d_children[q].d_req_type = d_tds->getArgType( dt[c], r ); + }else{ + rt.d_children[r].d_children[q].d_req_type = d_tds->getArgType( pdt[pc], q ); + } + } + } + rt_valid = true; + //TODO: this increases term size but is probably a good idea + } + }else if( k==NOT ){ + if( parent==ITE ){ + // (ite (not y) z w) -----> (ite y w z) + rt.d_req_kind = ITE; + rt.d_children[0].d_req_type = d_tds->getArgType( dt[c], 0 ); + rt.d_children[1].d_req_type = d_tds->getArgType( pdt[pc], 2 ); + rt.d_children[2].d_req_type = d_tds->getArgType( pdt[pc], 1 ); + } } - if( parent==MINUS || parent==BITVECTOR_SUB ){ - - + Trace("sygus-consider-split") << "Consider sygus split kind " << k << ", parent = " << parent << ", arg = " << arg << "?" << std::endl; + if( rt_valid ){ + rt.print("sygus-consider-split"); + //check if it meets the requirements + if( rt.satisfiedBy( d_tds, tnp ) ){ + Trace("sygus-consider-split") << "...success!" << std::endl; + //do not need to consider the kind in the search since there are ways to construct equivalent terms + return false; + }else{ + Trace("sygus-consider-split") << "...failed." << std::endl; + } + Trace("sygus-consider-split") << std::endl; } + //must consider this kind in the search return true; } @@ -616,6 +737,7 @@ bool SygusSplit::isGenericRedundant( TypeNode tn, Node g, bool active ) { d_gen_terms[tn][gr] = g; d_gen_terms_inactive[tn][gr] = g; Trace("sygus-gnf-debug") << "...not redundant." << std::endl; + Trace("sygus-nf-reg") << "*** Sygus (generic) normal form : normal form of " << g << " is " << gr << std::endl; }else{ Trace("sygus-gnf-debug") << "...redundant." << std::endl; Trace("sygus-nf") << "* Sygus normal form : simplify since " << g << " and " << itg->second << " both rewrite to " << gr << std::endl; @@ -849,6 +971,7 @@ bool SygusSymBreak::processCurrentProgram( Node a, TypeNode at, int depth, Node }else{ d_redundant[at][prog] = false; red = false; + Trace("sygus-nf-reg") << "*** Sygus normal form : normal form of " << prog << " is " << progr << std::endl; } }else{ rep_prog = itnp->second; @@ -858,6 +981,7 @@ bool SygusSymBreak::processCurrentProgram( Node a, TypeNode at, int depth, Node d_redundant[at].erase( rep_prog ); d_redundant[at][prog] = false; red = false; + Trace("sygus-nf-reg") << "*** Sygus normal form : normal form of " << prog << " is " << progr << " (redundant but smaller than " << rep_prog << ") " << std::endl; }else{ Assert( prog!=itnp->second ); d_redundant[at][prog] = true; @@ -1090,6 +1214,7 @@ bool SygusSymBreak::processCurrentProgram( Node a, TypeNode at, int depth, Node } }else{ red = it->second; + Trace("sygus-nf-debug") << "Already processed, redundant : " << red << std::endl; } if( red ){ if( std::find( d_lemmas_reported[at][prog].begin(), d_lemmas_reported[at][prog].end(), a )==d_lemmas_reported[at][prog].end() ){ diff --git a/src/theory/datatypes/kinds b/src/theory/datatypes/kinds index d035f0fa7..3338e5f31 100644 --- a/src/theory/datatypes/kinds +++ b/src/theory/datatypes/kinds @@ -105,7 +105,7 @@ typerule RECORD_UPDATE ::CVC4::theory::datatypes::RecordUpdateTypeRule operator DT_SIZE 1 "datatypes size" typerule DT_SIZE ::CVC4::theory::datatypes::DtSizeTypeRule -operator DT_HEIGHT_BOUND 1 "datatypes height bound" +operator DT_HEIGHT_BOUND 2 "datatypes height bound" typerule DT_HEIGHT_BOUND ::CVC4::theory::datatypes::DtHeightBoundTypeRule endtheory diff --git a/src/theory/datatypes/theory_datatypes.cpp b/src/theory/datatypes/theory_datatypes.cpp index eb8b23973..a2f995935 100644 --- a/src/theory/datatypes/theory_datatypes.cpp +++ b/src/theory/datatypes/theory_datatypes.cpp @@ -27,9 +27,11 @@ #include "theory/datatypes/datatypes_rewriter.h" #include "theory/datatypes/theory_datatypes_type_rules.h" #include "theory/quantifiers_engine.h" +#include "theory/quantifiers/term_database.h" #include "theory/theory_model.h" #include "theory/type_enumerator.h" #include "theory/valuation.h" +#include "options/theory_options.h" using namespace std; using namespace CVC4::kind; @@ -54,8 +56,7 @@ TheoryDatatypes::TheoryDatatypes(Context* c, UserContext* u, OutputChannel& out, //d_consEqc( c ), d_conflict( c, false ), d_collectTermsCache( c ), - d_consTerms( c ), - d_selTerms( c ), + d_functionTerms( c ), d_singleton_eq( u ), d_lemmas_produced_c( u ) { @@ -81,6 +82,8 @@ TheoryDatatypes::~TheoryDatatypes() { Assert(current != NULL); delete current; } + delete d_sygus_split; + delete d_sygus_sym_break; } void TheoryDatatypes::setMasterEqualityEngine(eq::EqualityEngine* eq) { @@ -91,9 +94,7 @@ TheoryDatatypes::EqcInfo* TheoryDatatypes::getOrMakeEqcInfo( TNode n, bool doMak if( !hasEqcInfo( n ) ){ if( doMake ){ //add to labels - NodeList* lbl = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false, - ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) ); - d_labels.insertDataFromContextMemory( n, lbl ); + d_labels[ n ] = 0; std::map< Node, EqcInfo* >::iterator eqc_i = d_eqc_info.find( n ); EqcInfo* ei; @@ -106,10 +107,10 @@ TheoryDatatypes::EqcInfo* TheoryDatatypes::getOrMakeEqcInfo( TNode n, bool doMak if( n.getKind()==APPLY_CONSTRUCTOR ){ ei->d_constructor = n; } + //add to selectors - NodeList* sel = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false, - ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) ); - d_selector_apps.insertDataFromContextMemory( n, sel ); + d_selector_apps[n] = 0; + return ei; }else{ return NULL; @@ -178,6 +179,7 @@ void TheoryDatatypes::check(Effort e) { Trace("datatypes-debug") << "Check for splits " << e << endl; do { d_addedFact = false; + bool added_split = false; std::map< TypeNode, Node > rec_singletons; eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); while( !eqcs_i.isFinished() ){ @@ -190,7 +192,7 @@ void TheoryDatatypes::check(Effort e) { if( !hasLabel( eqc, n ) ){ Trace("datatypes-debug") << "No constructor..." << std::endl; const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); - Trace("datatypes-debug") << "Datatype " << dt << " is " << dt.isFinite() << " " << dt.isUFinite() << " " << dt.isRecursiveSingleton() << std::endl; + Trace("datatypes-debug") << "Datatype " << dt << " is " << dt.isFinite() << " " << dt.isInterpretedFinite() << " " << dt.isRecursiveSingleton() << std::endl; bool continueProc = true; if( dt.isRecursiveSingleton() ){ Trace("datatypes-debug") << "Check recursive singleton..." << std::endl; @@ -251,7 +253,7 @@ void TheoryDatatypes::check(Effort e) { if( consIndex==-1 ){ consIndex = j; } - if( options::finiteModelFind() ? !dt[ j ].isUFinite() : !dt[ j ].isFinite() ) { + if( !dt[ j ].isInterpretedFinite() ) { if( !eqc || !eqc->d_selectors ){ needSplit = false; } @@ -311,7 +313,10 @@ void TheoryDatatypes::check(Effort e) { //doSendLemma( lemma ); d_out->lemma( lemma, false, false, true ); } - return; + added_split = true; + if( !options::dtBlastSplits() ){ + return; + } } }else{ Trace("dt-split-debug") << "Do not split constructor for " << n << " : " << n.getType() << " " << dt.getNumConstructors() << std::endl; @@ -323,6 +328,9 @@ void TheoryDatatypes::check(Effort e) { } ++eqcs_i; } + if( added_split ){ + return; + } Trace("datatypes-debug") << "Flush pending facts..." << std::endl; flushPendingFacts(); /* @@ -870,33 +878,36 @@ void TheoryDatatypes::merge( Node t1, Node t2 ){ //merge labels - NodeListMap::iterator lbl_i = d_labels.find( t2 ); + NodeIntMap::iterator lbl_i = d_labels.find( t2 ); if( lbl_i != d_labels.end() ){ Trace("datatypes-debug") << " merge labels from " << eqc2 << " " << t2 << std::endl; - NodeList* lbl = (*lbl_i).second; - for( NodeList::const_iterator j = lbl->begin(); j != lbl->end(); ++j ){ - Node tt = (*j).getKind()==kind::NOT ? (*j)[0] : (*j); + int n_label = (*lbl_i).second; + for( int i=0; i<n_label; i++ ){ + Assert( i<(int)d_labels_data[ t2 ].size() ); + Node t = d_labels_data[ t2 ][i]; + Node tt = t.getKind()==kind::NOT ? t[0] : t; Node t_arg; int tindex = DatatypesRewriter::isTester( tt, t_arg ); Assert( tindex!=-1 ); - addTester( tindex, *j, eqc1, t1, t_arg ); + addTester( tindex, t, eqc1, t1, t_arg ); if( d_conflict ){ Trace("datatypes-debug") << " conflict!" << std::endl; return; } } + } //merge selectors if( !eqc1->d_selectors && eqc2->d_selectors ){ eqc1->d_selectors = true; checkInst = true; } - NodeListMap::iterator sel_i = d_selector_apps.find( t2 ); + NodeIntMap::iterator sel_i = d_selector_apps.find( t2 ); if( sel_i != d_selector_apps.end() ){ Trace("datatypes-debug") << " merge selectors from " << eqc2 << " " << t2 << std::endl; - NodeList* sel = (*sel_i).second; - for( NodeList::const_iterator j = sel->begin(); j != sel->end(); ++j ){ - addSelector( *j, eqc1, t1, eqc2->d_constructor.get().isNull() ); + int n_sel = (*sel_i).second; + for( int j=0; j<n_sel; j++ ){ + addSelector( d_selector_apps_data[t2][j], eqc1, t1, eqc2->d_constructor.get().isNull() ); } } if( checkInst ){ @@ -927,11 +938,11 @@ bool TheoryDatatypes::hasLabel( EqcInfo* eqc, Node n ){ } Node TheoryDatatypes::getLabel( Node n ) { - NodeListMap::iterator lbl_i = d_labels.find( n ); + NodeIntMap::iterator lbl_i = d_labels.find( n ); if( lbl_i != d_labels.end() ){ - NodeList* lbl = (*lbl_i).second; - if( !(*lbl).empty() && (*lbl)[ (*lbl).size() - 1 ].getKind()!=kind::NOT ){ - return (*lbl)[ (*lbl).size() - 1 ]; + unsigned n_lbl = (*lbl_i).second; + if( n_lbl>0 && d_labels_data[n][ n_lbl-1 ].getKind()!=kind::NOT ){ + return d_labels_data[n][ n_lbl-1 ]; } } return Node::null(); @@ -954,9 +965,9 @@ int TheoryDatatypes::getLabelIndex( EqcInfo* eqc, Node n ){ } bool TheoryDatatypes::hasTester( Node n ) { - NodeListMap::iterator lbl_i = d_labels.find( n ); + NodeIntMap::iterator lbl_i = d_labels.find( n ); if( lbl_i != d_labels.end() ){ - return !(*(*lbl_i).second).empty(); + return (*lbl_i).second>0; }else{ return false; } @@ -969,13 +980,14 @@ void TheoryDatatypes::getPossibleCons( EqcInfo* eqc, Node n, std::vector< bool > if( lindex!=-1 ){ pcons[ lindex ] = true; }else{ - NodeListMap::iterator lbl_i = d_labels.find( n ); + NodeIntMap::iterator lbl_i = d_labels.find( n ); if( lbl_i != d_labels.end() ){ - NodeList* lbl = (*lbl_i).second; - for( NodeList::const_iterator i = lbl->begin(); i != lbl->end(); i++ ) { - Assert( (*i).getKind()==NOT ); - //pcons[ Datatype::indexOf( (*i)[0].getOperator().toExpr() ) ] = false; - int tindex = DatatypesRewriter::isTester( (*i)[0] ); + int n_lbl = (*lbl_i).second; + for( int i=0; i<n_lbl; i++ ){ + Node t = d_labels_data[n][i]; + Assert( t.getKind()==NOT ); + //pcons[ Datatype::indexOf( t[0].getOperator().toExpr() ) ] = false; + int tindex = DatatypesRewriter::isTester( t[0] ); Assert( tindex!=-1 ); pcons[ tindex ] = false; } @@ -984,11 +996,11 @@ void TheoryDatatypes::getPossibleCons( EqcInfo* eqc, Node n, std::vector< bool > } void TheoryDatatypes::getSelectorsForCons( Node r, std::map< int, bool >& sels ) { - NodeListMap::iterator sel_i = d_selector_apps.find( r ); + NodeIntMap::iterator sel_i = d_selector_apps.find( r ); if( sel_i != d_selector_apps.end() ){ - NodeList* sel = (*sel_i).second; - for( NodeList::const_iterator j = sel->begin(); j != sel->end(); j++ ){ - int sindex = Datatype::indexOf( (*j).getOperator().toExpr() ); + int n_sel = (*sel_i).second; + for( int j=0; j<n_sel; j++ ){ + int sindex = Datatype::indexOf( d_selector_apps_data[r][j].getOperator().toExpr() ); sels[sindex] = true; } } @@ -1055,12 +1067,13 @@ void TheoryDatatypes::addTester( int ttindex, Node t, EqcInfo* eqc, Node n, Node } }else{ //otherwise, scan list of labels - NodeListMap::iterator lbl_i = d_labels.find( n ); + NodeIntMap::iterator lbl_i = d_labels.find( n ); Assert( lbl_i != d_labels.end() ); - NodeList* lbl = (*lbl_i).second; - for( NodeList::const_iterator i = lbl->begin(); i != lbl->end(); i++ ) { - Assert( (*i).getKind()==NOT ); - j = *i; + int n_lbl = (*lbl_i).second; + for( int i=0; i<n_lbl; i++ ){ + Node ti = d_labels_data[n][i]; + Assert( ti.getKind()==NOT ); + j = ti; jt = j[0]; //int jtindex = Datatype::indexOf( jt.getOperator().toExpr() ); int jtindex = DatatypesRewriter::isTester( jt ); @@ -1076,16 +1089,24 @@ void TheoryDatatypes::addTester( int ttindex, Node t, EqcInfo* eqc, Node n, Node } if( !makeConflict ){ Debug("datatypes-labels") << "Add to labels " << t << std::endl; - lbl->push_back( t ); + //lbl->push_back( t ); + d_labels[n] = n_lbl + 1; + if( n_lbl<(int)d_labels_data[n].size() ){ + d_labels_data[n][n_lbl] = t; + }else{ + d_labels_data[n].push_back( t ); + } + n_lbl++; + const Datatype& dt = ((DatatypeType)(t_arg.getType()).toType()).getDatatype(); - Debug("datatypes-labels") << "Labels at " << lbl->size() << " / " << dt.getNumConstructors() << std::endl; + Debug("datatypes-labels") << "Labels at " << n_lbl << " / " << dt.getNumConstructors() << std::endl; if( tpolarity ){ instantiate( eqc, n ); }else{ //check if we have reached the maximum number of testers // in this case, add the positive tester //this should not be done for sygus, since cases may be limited - if( lbl->size()==dt.getNumConstructors()-1 && !dt.isSygus() ){ + if( n_lbl==(int)dt.getNumConstructors()-1 && !dt.isSygus() ){ std::vector< bool > pcons; getPossibleCons( eqc, n, pcons ); int testerIndex = -1; @@ -1099,11 +1120,14 @@ void TheoryDatatypes::addTester( int ttindex, Node t, EqcInfo* eqc, Node n, Node //we must explain why each term in the set of testers for this equivalence class is equal std::vector< Node > eq_terms; NodeBuilder<> nb(kind::AND); - for( NodeList::const_iterator i = lbl->begin(); i != lbl->end(); i++ ) { - nb << (*i); - Assert( (*i).getKind()==NOT ); + //for( NodeList::const_iterator i = lbl->begin(); i != lbl->end(); i++ ) { + + for( int i=0; i<n_lbl; i++ ){ + Node ti = d_labels_data[n][i]; + nb << ti; + Assert( ti.getKind()==NOT ); Node t_arg2; - DatatypesRewriter::isTester( (*i)[0], t_arg2 ); + DatatypesRewriter::isTester( ti[0], t_arg2 ); //Assert( tindex!=-1 ); if( std::find( eq_terms.begin(), eq_terms.end(), t_arg2 )==eq_terms.end() ){ eq_terms.push_back( t_arg2 ); @@ -1140,19 +1164,26 @@ void TheoryDatatypes::addTester( int ttindex, Node t, EqcInfo* eqc, Node n, Node void TheoryDatatypes::addSelector( Node s, EqcInfo* eqc, Node n, bool assertFacts ) { Trace("dt-collapse-sel") << "Add selector : " << s << " to eqc(" << n << ")" << std::endl; //check to see if it is redundant - NodeListMap::iterator sel_i = d_selector_apps.find( n ); + NodeIntMap::iterator sel_i = d_selector_apps.find( n ); Assert( sel_i != d_selector_apps.end() ); if( sel_i != d_selector_apps.end() ){ - NodeList* sel = (*sel_i).second; - for( NodeList::const_iterator j = sel->begin(); j != sel->end(); ++j ){ - Node ss = *j; + int n_sel = (*sel_i).second; + for( int j=0; j<n_sel; j++ ){ + Node ss = d_selector_apps_data[n][j]; if( s.getOperator()==ss.getOperator() && ( s.getKind()!=DT_HEIGHT_BOUND || s[1]==ss[1] ) ){ Trace("dt-collapse-sel") << "...redundant." << std::endl; return; } } //add it to the vector - sel->push_back( s ); + //sel->push_back( s ); + d_selector_apps[n] = n_sel + 1; + if( n_sel<(int)d_selector_apps_data[n].size() ){ + d_selector_apps_data[n][n_sel] = s; + }else{ + d_selector_apps_data[n].push_back( s ); + } + eqc->d_selectors = true; } if( assertFacts && !eqc->d_constructor.get().isNull() ){ @@ -1165,19 +1196,19 @@ void TheoryDatatypes::addConstructor( Node c, EqcInfo* eqc, Node n ){ Trace("datatypes-debug") << "Add constructor : " << c << " to eqc(" << n << ")" << std::endl; Assert( eqc->d_constructor.get().isNull() ); //check labels - NodeListMap::iterator lbl_i = d_labels.find( n ); + NodeIntMap::iterator lbl_i = d_labels.find( n ); if( lbl_i != d_labels.end() ){ size_t constructorIndex = Datatype::indexOf(c.getOperator().toExpr()); - NodeList* lbl = (*lbl_i).second; - for( NodeList::const_iterator i = lbl->begin(); i != lbl->end(); i++ ) { - if( (*i).getKind()==NOT ){ - int tindex = DatatypesRewriter::isTester( (*i)[0] ); + int n_lbl = (*lbl_i).second; + for( int i=0; i<n_lbl; i++ ){ + Node t = d_labels_data[n][i]; + if( t.getKind()==NOT ){ + int tindex = DatatypesRewriter::isTester( t[0] ); Assert( tindex!=-1 ); if( tindex==(int)constructorIndex ){ - Node n = *i; std::vector< TNode > assumptions; - explain( *i, assumptions ); - explainEquality( c, (*i)[0][0], true, assumptions ); + explain( t, assumptions ); + explainEquality( c, t[0][0], true, assumptions ); d_conflictNode = mkAnd( assumptions ); Trace("dt-conflict") << "CONFLICT: Tester merge eq conflict : " << d_conflictNode << std::endl; d_out->conflict( d_conflictNode ); @@ -1188,12 +1219,13 @@ void TheoryDatatypes::addConstructor( Node c, EqcInfo* eqc, Node n ){ } } //check selectors - NodeListMap::iterator sel_i = d_selector_apps.find( n ); + NodeIntMap::iterator sel_i = d_selector_apps.find( n ); if( sel_i != d_selector_apps.end() ){ - NodeList* sel = (*sel_i).second; - for( NodeList::const_iterator j = sel->begin(); j != sel->end(); ++j ){ + int n_sel = (*sel_i).second; + for( int j=0; j<n_sel; j++ ){ + Node s = d_selector_apps_data[n][j]; //collapse the selector - collapseSelector( *j, c ); + collapseSelector( s, c ); } } eqc->d_constructor.set( c ); @@ -1277,61 +1309,114 @@ EqualityStatus TheoryDatatypes::getEqualityStatus(TNode a, TNode b){ return EQUALITY_FALSE_IN_MODEL; } -void TheoryDatatypes::computeCareGraph(){ - Trace("dt-cg") << "Compute graph for dt..." << std::endl; - vector< pair<TNode, TNode> > currentPairs; - for( unsigned r=0; r<2; r++ ){ - unsigned functionTerms = r==0 ? d_consTerms.size() : d_selTerms.size(); - for( unsigned i=0; i<functionTerms; i++ ){ - TNode f1 = r==0 ? d_consTerms[i] : d_selTerms[i]; - Assert(d_equalityEngine.hasTerm(f1)); - for( unsigned j=i+1; j<functionTerms; j++ ){ - TNode f2 = r==0 ? d_consTerms[j] : d_selTerms[j]; - Trace("dt-cg-debug") << "dt-cg(" << r << "): " << f1 << " and " << f2 << " " << (f1.getOperator()==f2.getOperator()) << " " << areEqual( f1, f2 ) << std::endl; - Assert(d_equalityEngine.hasTerm(f2)); - if( f1.getOperator()==f2.getOperator() && - ( ( f1.getKind()!=DT_SIZE && f1.getKind()!=DT_HEIGHT_BOUND ) || f1[0].getType()==f2[0].getType() ) && - !areEqual( f1, f2 ) ){ - Trace("dt-cg") << "Check " << f1 << " and " << f2 << std::endl; - bool somePairIsDisequal = false; - currentPairs.clear(); - for (unsigned k = 0; k < f1.getNumChildren(); ++ k) { - TNode x = f1[k]; - TNode y = f2[k]; - Assert(d_equalityEngine.hasTerm(x)); - Assert(d_equalityEngine.hasTerm(y)); - //need to consider types for parametric selectors - if( x.getType()!=y.getType() || areDisequal(x, y) ){ - somePairIsDisequal = true; - break; - }else if( !d_equalityEngine.areEqual( x, y ) ){ - Trace("dt-cg") << "Arg #" << k << " is " << x << " " << y << std::endl; - if( d_equalityEngine.isTriggerTerm(x, THEORY_DATATYPES) && d_equalityEngine.isTriggerTerm(y, THEORY_DATATYPES) ){ - TNode x_shared = d_equalityEngine.getTriggerTermRepresentative(x, THEORY_DATATYPES); - TNode y_shared = d_equalityEngine.getTriggerTermRepresentative(y, THEORY_DATATYPES); - Trace("dt-cg") << "Arg #" << k << " shared term is " << x_shared << " " << y_shared << std::endl; - EqualityStatus eqStatus = d_valuation.getEqualityStatus(x_shared, y_shared); - Trace("dt-cg") << "...eq status is " << eqStatus << std::endl; - if( eqStatus==EQUALITY_FALSE_AND_PROPAGATED || eqStatus==EQUALITY_FALSE || eqStatus==EQUALITY_FALSE_IN_MODEL ){ - somePairIsDisequal = true; - break; - }else{ - currentPairs.push_back(make_pair(x_shared, y_shared)); - } + + +void TheoryDatatypes::addCarePairs( quantifiers::TermArgTrie * t1, quantifiers::TermArgTrie * t2, unsigned arity, unsigned depth, unsigned& n_pairs ){ + if( depth==arity ){ + if( t2!=NULL ){ + Node f1 = t1->getNodeData(); + Node f2 = t2->getNodeData(); + if( !areEqual( f1, f2 ) ){ + Trace("dt-cg") << "Check " << f1 << " and " << f2 << std::endl; + vector< pair<TNode, TNode> > currentPairs; + for (unsigned k = 0; k < f1.getNumChildren(); ++ k) { + TNode x = f1[k]; + TNode y = f2[k]; + Assert( d_equalityEngine.hasTerm(x) ); + Assert( d_equalityEngine.hasTerm(y) ); + Assert( !areDisequal( x, y ) ); + if( !d_equalityEngine.areEqual( x, y ) ){ + Trace("dt-cg") << "Arg #" << k << " is " << x << " " << y << std::endl; + if( d_equalityEngine.isTriggerTerm(x, THEORY_DATATYPES) && d_equalityEngine.isTriggerTerm(y, THEORY_DATATYPES) ){ + TNode x_shared = d_equalityEngine.getTriggerTermRepresentative(x, THEORY_DATATYPES); + TNode y_shared = d_equalityEngine.getTriggerTermRepresentative(y, THEORY_DATATYPES); + Trace("dt-cg") << "Arg #" << k << " shared term is " << x_shared << " " << y_shared << std::endl; + EqualityStatus eqStatus = d_valuation.getEqualityStatus(x_shared, y_shared); + Trace("dt-cg") << "...eq status is " << eqStatus << std::endl; + if( eqStatus==EQUALITY_FALSE_AND_PROPAGATED || eqStatus==EQUALITY_FALSE || eqStatus==EQUALITY_FALSE_IN_MODEL ){ + //an argument is disequal, we are done + return; + }else{ + currentPairs.push_back(make_pair(x_shared, y_shared)); } } } - if (!somePairIsDisequal) { - for (unsigned c = 0; c < currentPairs.size(); ++ c) { - Trace("dt-cg-pair") << "Pair : " << currentPairs[c].first << " " << currentPairs[c].second << std::endl; - addCarePair(currentPairs[c].first, currentPairs[c].second); - } + } + for (unsigned c = 0; c < currentPairs.size(); ++ c) { + Trace("dt-cg-pair") << "Pair : " << currentPairs[c].first << " " << currentPairs[c].second << std::endl; + addCarePair(currentPairs[c].first, currentPairs[c].second); + n_pairs++; + } + } + } + }else{ + if( t2==NULL ){ + if( depth<(arity-1) ){ + //add care pairs internal to each child + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + addCarePairs( &it->second, NULL, arity, depth+1, n_pairs ); + } + } + //add care pairs based on each pair of non-disequal arguments + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + std::map< TNode, quantifiers::TermArgTrie >::iterator it2 = it; + ++it2; + for( ; it2 != t1->d_data.end(); ++it2 ){ + if( !areDisequal(it->first, it2->first) ){ + addCarePairs( &it->second, &it2->second, arity, depth+1, n_pairs ); + } + } + } + }else{ + //add care pairs based on product of indices, non-disequal arguments + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it2 = t2->d_data.begin(); it2 != t2->d_data.end(); ++it2 ){ + if( !areDisequal(it->first, it2->first) ){ + addCarePairs( &it->second, &it2->second, arity, depth+1, n_pairs ); } } } } } - Trace("dt-cg") << "Done Compute graph for dt." << std::endl; +} + +void TheoryDatatypes::computeCareGraph(){ + unsigned n_pairs = 0; + Trace("dt-cg-summary") << "Compute graph for dt..." << d_functionTerms.size() << " " << d_sharedTerms.size() << std::endl; + Trace("dt-cg") << "Build indices..." << std::endl; + std::map< TypeNode, std::map< Node, quantifiers::TermArgTrie > > index; + std::map< Node, unsigned > arity; + //populate indices + unsigned functionTerms = d_functionTerms.size(); + for( unsigned i=0; i<functionTerms; i++ ){ + TNode f1 = d_functionTerms[i]; + Assert(d_equalityEngine.hasTerm(f1)); + Trace("dt-cg-debug") << "...build for " << f1 << std::endl; + //break into index based on operator, and type of first argument (since some operators are parametric) + Node op = f1.getOperator(); + TypeNode tn = f1[0].getType(); + std::vector< TNode > reps; + bool has_trigger_arg = false; + for( unsigned j=0; j<f1.getNumChildren(); j++ ){ + reps.push_back( d_equalityEngine.getRepresentative( f1[j] ) ); + if( d_equalityEngine.isTriggerTerm( f1[j], THEORY_DATATYPES ) ){ + has_trigger_arg = true; + } + } + //only may contribute to care pairs if has at least one trigger argument + if( has_trigger_arg ){ + index[tn][op].addTerm( f1, reps ); + arity[op] = reps.size(); + } + } + //for each index + for( std::map< TypeNode, std::map< Node, quantifiers::TermArgTrie > >::iterator iti = index.begin(); iti != index.end(); ++iti ){ + for( std::map< Node, quantifiers::TermArgTrie >::iterator itii = iti->second.begin(); itii != iti->second.end(); ++itii ){ + Trace("dt-cg") << "Process index " << itii->first << ", " << iti->first << "..." << std::endl; + addCarePairs( &itii->second, NULL, arity[ itii->first ], 0, n_pairs ); + } + } + Trace("dt-cg-summary") << "...done, # pairs = " << n_pairs << std::endl; } void TheoryDatatypes::collectModelInfo( TheoryModel* m, bool fullModel ){ @@ -1443,7 +1528,7 @@ void TheoryDatatypes::collectModelInfo( TheoryModel* m, bool fullModel ){ if( neqc.isNull() ){ for( unsigned i=0; i<pcons.size(); i++ ){ //must try the infinite ones first - bool cfinite = options::finiteModelFind() ? dt[ i ].isUFinite() : dt[ i ].isFinite(); + bool cfinite = dt[ i ].isInterpretedFinite(); if( pcons[i] && (r==1)==cfinite ){ neqc = DatatypesRewriter::getInstCons( eqc, dt, i ); //for( unsigned j=0; j<neqc.getNumChildren(); j++ ){ @@ -1543,16 +1628,18 @@ Node TheoryDatatypes::getSingletonLemma( TypeNode tn, bool pol ) { void TheoryDatatypes::collectTerms( Node n ) { if( d_collectTermsCache.find( n )==d_collectTermsCache.end() ){ d_collectTermsCache[n] = true; - for( int i=0; i<(int)n.getNumChildren(); i++ ) { - collectTerms( n[i] ); - } + //for( int i=0; i<(int)n.getNumChildren(); i++ ) { + // collectTerms( n[i] ); + //} if( n.getKind() == APPLY_CONSTRUCTOR ){ Debug("datatypes") << " Found constructor " << n << endl; - d_consTerms.push_back( n ); + if( n.getNumChildren()>0 ){ + d_functionTerms.push_back( n ); + } }else{ if( n.getKind() == APPLY_SELECTOR_TOTAL || n.getKind() == DT_SIZE || n.getKind() == DT_HEIGHT_BOUND ){ - d_selTerms.push_back( n ); + d_functionTerms.push_back( n ); //we must also record which selectors exist Trace("dt-collapse-sel") << " Found selector " << n << endl; Node rep = getRepresentative( n[0] ); @@ -2040,21 +2127,19 @@ void TheoryDatatypes::printModelDebug( const char* c ){ if( hasLabel( ei, eqc ) ){ Trace( c ) << getLabel( eqc ); }else{ - NodeListMap::iterator lbl_i = d_labels.find( eqc ); + NodeIntMap::iterator lbl_i = d_labels.find( eqc ); if( lbl_i != d_labels.end() ){ - NodeList* lbl = (*lbl_i).second; - for( NodeList::const_iterator j = lbl->begin(); j != lbl->end(); j++ ){ - Trace( c ) << *j << " "; + for( int j=0; j<(*lbl_i).second; j++ ){ + Trace( c ) << d_labels_data[eqc][j] << " "; } } } Trace( c ) << std::endl; Trace( c ) << " Selectors : " << ( ei->d_selectors ? "yes, " : "no " ); - NodeListMap::iterator sel_i = d_selector_apps.find( eqc ); + NodeIntMap::iterator sel_i = d_selector_apps.find( eqc ); if( sel_i != d_selector_apps.end() ){ - NodeList* sel = (*sel_i).second; - for( NodeList::const_iterator j = sel->begin(); j != sel->end(); j++ ){ - Trace( c ) << *j << " "; + for( int j=0; j<(*sel_i).second; j++ ){ + Trace( c ) << d_selector_apps_data[eqc][j] << " "; } } Trace( c ) << std::endl; diff --git a/src/theory/datatypes/theory_datatypes.h b/src/theory/datatypes/theory_datatypes.h index 4bf04e08c..5722e7822 100644 --- a/src/theory/datatypes/theory_datatypes.h +++ b/src/theory/datatypes/theory_datatypes.h @@ -32,12 +32,17 @@ namespace CVC4 { namespace theory { + +namespace quantifiers{ + class TermArgTrie; +} + namespace datatypes { class TheoryDatatypes : public Theory { private: typedef context::CDChunkList<Node> NodeList; - typedef context::CDHashMap<Node, NodeList*, NodeHashFunction> NodeListMap; + typedef context::CDHashMap< Node, int, NodeHashFunction> NodeIntMap; typedef context::CDHashMap< Node, bool, NodeHashFunction > BoolMap; typedef context::CDHashMap< Node, Node, NodeHashFunction > NodeMap; @@ -157,9 +162,11 @@ private: * NOT is_[constructor_1]( t )...NOT is_[constructor_n]( t ) followed by * is_[constructor_(n+1)]( t ), each of which is a unique tester. */ - NodeListMap d_labels; + NodeIntMap d_labels; + std::map< Node, std::vector< Node > > d_labels_data; /** selector apps for eqch equivalence class */ - NodeListMap d_selector_apps; + NodeIntMap d_selector_apps; + std::map< Node, std::vector< Node > > d_selector_apps_data; /** constructor terms */ //BoolMap d_consEqc; /** Are we in conflict */ @@ -176,10 +183,8 @@ private: std::vector< Node > d_pending; std::map< Node, Node > d_pending_exp; std::vector< Node > d_pending_merge; - /** All the constructor terms that the theory has seen */ - context::CDList<TNode> d_consTerms; - /** All the selector terms that the theory has seen */ - context::CDList<TNode> d_selTerms; + /** All the function terms that the theory has seen */ + context::CDList<TNode> d_functionTerms; /** counter for forcing assignments (ensures fairness) */ unsigned d_dtfCounter; /** expand definition skolem functions */ @@ -216,6 +221,7 @@ private: TNode getEqcConstructor( TNode r ); protected: + void addCarePairs( quantifiers::TermArgTrie * t1, quantifiers::TermArgTrie * t2, unsigned arity, unsigned depth, unsigned& n_pairs ); /** compute care graph */ void computeCareGraph(); diff --git a/src/theory/datatypes/theory_datatypes_type_rules.h b/src/theory/datatypes/theory_datatypes_type_rules.h index 5a3645691..9c8387958 100644 --- a/src/theory/datatypes/theory_datatypes_type_rules.h +++ b/src/theory/datatypes/theory_datatypes_type_rules.h @@ -299,11 +299,10 @@ public: throw TypeCheckingExceptionPrivate(n, "datatype height bound must be non-negative"); } } - return nodeManager->integerType(); + return nodeManager->booleanType(); } };/* class DtHeightBoundTypeRule */ - }/* CVC4::theory::datatypes namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/datatypes/type_enumerator.cpp b/src/theory/datatypes/type_enumerator.cpp index 6c1155237..c0539743f 100644 --- a/src/theory/datatypes/type_enumerator.cpp +++ b/src/theory/datatypes/type_enumerator.cpp @@ -172,7 +172,7 @@ Node DatatypesEnumerator::getTermEnum( TypeNode tn, unsigned i ){ Debug("dt-enum") << "datatype is " << d_type << std::endl; Debug("dt-enum") << "properties : " << d_datatype.isCodatatype() << " " << d_datatype.isRecursiveSingleton(); Debug("dt-enum") << " " << d_datatype.isFinite() << std::endl; - Debug("dt-enum") << " " << d_datatype.isUFinite() << std::endl; + Debug("dt-enum") << " " << d_datatype.isInterpretedFinite() << std::endl; if( d_datatype.isCodatatype() && hasCyclesDt( d_datatype ) ){ //start with uninterpreted constant diff --git a/src/theory/datatypes/type_enumerator.h b/src/theory/datatypes/type_enumerator.h index bbfd951b3..8473b5d69 100644 --- a/src/theory/datatypes/type_enumerator.h +++ b/src/theory/datatypes/type_enumerator.h @@ -159,7 +159,7 @@ public: } if( d_ctor>=d_has_debruijn+d_datatype.getNumConstructors() ){ //try next size limit as long as new terms were generated at last size, or other cases - if( prevSize==d_size_limit || ( d_size_limit==0 && d_datatype.isCodatatype() ) || ( options::finiteModelFind() ? !d_datatype.isUFinite() : !d_datatype.isFinite() ) ){ + if( prevSize==d_size_limit || ( d_size_limit==0 && d_datatype.isCodatatype() ) || !d_datatype.isInterpretedFinite() ){ d_size_limit++; d_ctor = d_zeroCtor; for( unsigned i=0; i<d_sel_sum.size(); i++ ){ diff --git a/src/theory/logic_info.cpp b/src/theory/logic_info.cpp index 04cac7ae5..6ac1c5e32 100644 --- a/src/theory/logic_info.cpp +++ b/src/theory/logic_info.cpp @@ -270,7 +270,7 @@ std::string LogicInfo::getLogicString() const { if(d_theories[THEORY_FP]) { ss << "FP"; ++seen; - } + } if(d_theories[THEORY_DATATYPES]) { ss << "DT"; ++seen; @@ -296,7 +296,10 @@ std::string LogicInfo::getLogicString() const { ss << "FS"; ++seen; } - + if(d_theories[THEORY_SEP]) { + ss << "SEP"; + ++seen; + } if(seen != d_sharingTheories) { Unhandled("can't extract a logic string from LogicInfo; at least one " "active theory is unknown to LogicInfo::getLogicString() !"); @@ -439,6 +442,10 @@ void LogicInfo::setLogicString(std::string logicString) throw(IllegalArgumentExc enableTheory(THEORY_SETS); p += 2; } + if(!strncmp(p, "SEP", 3)) { + enableTheory(THEORY_SEP); + p += 3; + } } } if(*p != '\0') { diff --git a/src/theory/quantifiers/alpha_equivalence.cpp b/src/theory/quantifiers/alpha_equivalence.cpp index 80066d690..a00d6d8a1 100644..100755 --- a/src/theory/quantifiers/alpha_equivalence.cpp +++ b/src/theory/quantifiers/alpha_equivalence.cpp @@ -30,17 +30,30 @@ struct sortTypeOrder { }; Node AlphaEquivalenceNode::registerNode( AlphaEquivalenceNode* aen, QuantifiersEngine* qe, Node q, std::vector< Node >& tt, std::vector< int >& arg_index ) { + std::map< Node, bool > visited; while( !tt.empty() ){ if( tt.size()==arg_index.size()+1 ){ Node t = tt.back(); - Node op = t.hasOperator() ? t.getOperator() : t; - arg_index.push_back( 0 ); + Node op; + if( t.hasOperator() ){ + if( visited.find( t )==visited.end() ){ + visited[t] = true; + op = t.getOperator(); + arg_index.push_back( 0 ); + }else{ + op = t; + arg_index.push_back( -1 ); + } + }else{ + op = t; + arg_index.push_back( 0 ); + } Trace("aeq-debug") << op << " "; aen = &(aen->d_children[op][t.getNumChildren()]); }else{ Node t = tt.back(); int i = arg_index.back(); - if( i==(int)t.getNumChildren() ){ + if( i==-1 || i==(int)t.getNumChildren() ){ tt.pop_back(); arg_index.pop_back(); }else{ @@ -56,9 +69,9 @@ Node AlphaEquivalenceNode::registerNode( AlphaEquivalenceNode* aen, QuantifiersE }else{ if( q.getNumChildren()==2 ){ //lemma ( q <=> d_quant ) - Trace("quant-ae") << "Alpha equivalent : " << std::endl; - Trace("quant-ae") << " " << q << std::endl; - Trace("quant-ae") << " " << aen->d_quant << std::endl; + Trace("alpha-eq") << "Alpha equivalent : " << std::endl; + Trace("alpha-eq") << " " << q << std::endl; + Trace("alpha-eq") << " " << aen->d_quant << std::endl; lem = q.iffNode( aen->d_quant ); }else{ //do not reduce annotated quantified formulas based on alpha equivalence diff --git a/src/theory/quantifiers/alpha_equivalence.h b/src/theory/quantifiers/alpha_equivalence.h index 8e7556eb6..8e7556eb6 100644..100755 --- a/src/theory/quantifiers/alpha_equivalence.h +++ b/src/theory/quantifiers/alpha_equivalence.h diff --git a/src/theory/quantifiers/ambqi_builder.cpp b/src/theory/quantifiers/ambqi_builder.cpp index 5192da7de..97116dee4 100644..100755 --- a/src/theory/quantifiers/ambqi_builder.cpp +++ b/src/theory/quantifiers/ambqi_builder.cpp @@ -787,7 +787,7 @@ void AbsMbqiBuilder::processBuildModel(TheoryModel* m, bool fullModel) { Trace("ambqi-model-debug") << "Initial terms: " << std::endl; for( size_t i=0; i<fm->d_uf_terms[f].size(); i++ ){ Node n = fm->d_uf_terms[f][i]; - if( !n.getAttribute(NoMatchAttribute()) ){ + if( d_qe->getTermDatabase()->isTermActive( n ) ){ Trace("ambqi-model-debug") << " " << n << " -> " << fm->getRepresentativeId( n ) << std::endl; fapps.push_back( n ); } diff --git a/src/theory/quantifiers/ambqi_builder.h b/src/theory/quantifiers/ambqi_builder.h index 3669d38b7..3669d38b7 100644..100755 --- a/src/theory/quantifiers/ambqi_builder.h +++ b/src/theory/quantifiers/ambqi_builder.h diff --git a/src/theory/quantifiers/anti_skolem.cpp b/src/theory/quantifiers/anti_skolem.cpp index c8d18aced..c8d18aced 100644..100755 --- a/src/theory/quantifiers/anti_skolem.cpp +++ b/src/theory/quantifiers/anti_skolem.cpp diff --git a/src/theory/quantifiers/anti_skolem.h b/src/theory/quantifiers/anti_skolem.h index 721371159..721371159 100644..100755 --- a/src/theory/quantifiers/anti_skolem.h +++ b/src/theory/quantifiers/anti_skolem.h diff --git a/src/theory/quantifiers/bounded_integers.cpp b/src/theory/quantifiers/bounded_integers.cpp index d32ef59a1..7184624da 100644..100755 --- a/src/theory/quantifiers/bounded_integers.cpp +++ b/src/theory/quantifiers/bounded_integers.cpp @@ -28,7 +28,7 @@ using namespace CVC4::theory::quantifiers; using namespace CVC4::kind; -BoundedIntegers::RangeModel::RangeModel(BoundedIntegers * bi, Node r, context::Context* c, context::Context* u, bool isProxy) : d_bi(bi), +BoundedIntegers::IntRangeModel::IntRangeModel(BoundedIntegers * bi, Node r, context::Context* c, context::Context* u, bool isProxy) : d_bi(bi), d_range(r), d_curr_max(-1), d_lit_to_range(u), d_range_assertions(c), d_has_range(c,false), d_curr_range(c,-1), d_ranges_proxied(u) { if( options::fmfBoundIntLazy() ){ d_proxy_range = isProxy ? r : NodeManager::currentNM()->mkSkolem( "pbir", r.getType() ); @@ -40,7 +40,7 @@ BoundedIntegers::RangeModel::RangeModel(BoundedIntegers * bi, Node r, context::C } } -void BoundedIntegers::RangeModel::initialize() { +void BoundedIntegers::IntRangeModel::initialize() { //add initial split lemma Node ltr = NodeManager::currentNM()->mkNode( LT, d_proxy_range, NodeManager::currentNM()->mkConst( Rational(0) ) ); ltr = Rewriter::rewrite( ltr ); @@ -55,7 +55,7 @@ void BoundedIntegers::RangeModel::initialize() { d_bi->addLiteralFromRange(ltr_lit, d_range); } -void BoundedIntegers::RangeModel::assertNode(Node n) { +void BoundedIntegers::IntRangeModel::assertNode(Node n) { bool pol = n.getKind()!=NOT; Node nlit = n.getKind()==NOT ? n[0] : n; if( d_lit_to_range.find( nlit )!=d_lit_to_range.end() ){ @@ -93,7 +93,7 @@ void BoundedIntegers::RangeModel::assertNode(Node n) { } } -void BoundedIntegers::RangeModel::allocateRange() { +void BoundedIntegers::IntRangeModel::allocateRange() { d_curr_max++; int newBound = d_curr_max; Trace("bound-int-proc") << "Allocate range bound " << newBound << " for " << d_range << std::endl; @@ -110,7 +110,7 @@ void BoundedIntegers::RangeModel::allocateRange() { d_bi->addLiteralFromRange(ltr_lit, d_range); } -Node BoundedIntegers::RangeModel::getNextDecisionRequest() { +Node BoundedIntegers::IntRangeModel::getNextDecisionRequest() { //request the current cardinality as a decision literal, if not already asserted for( NodeIntMap::iterator it = d_lit_to_range.begin(); it != d_lit_to_range.end(); ++it ){ int i = (*it).second; @@ -129,7 +129,7 @@ Node BoundedIntegers::RangeModel::getNextDecisionRequest() { return Node::null(); } -bool BoundedIntegers::RangeModel::proxyCurrentRange() { +bool BoundedIntegers::IntRangeModel::proxyCurrentRange() { //Trace("model-engine") << "Range(" << d_range << ") currently is " << d_curr_max.get() << std::endl; if( d_range!=d_proxy_range ){ //int curr = d_curr_range.get(); @@ -148,11 +148,24 @@ bool BoundedIntegers::RangeModel::proxyCurrentRange() { } + + + BoundedIntegers::BoundedIntegers(context::Context* c, QuantifiersEngine* qe) : QuantifiersModule(qe), d_assertions(c){ } +BoundedIntegers::~BoundedIntegers() { + for( std::map< Node, RangeModel * >::iterator it = d_rms.begin(); it != d_rms.end(); ++it ){ + delete it->second; + } +} + +void BoundedIntegers::presolve() { + d_bnd_it.clear(); +} + bool BoundedIntegers::isBound( Node f, Node v ) { return std::find( d_set[f].begin(), d_set[f].end(), v )!=d_set[f].end(); } @@ -172,62 +185,79 @@ bool BoundedIntegers::hasNonBoundVar( Node f, Node b ) { return false; } -void BoundedIntegers::processLiteral( Node f, Node lit, bool pol, +void BoundedIntegers::processLiteral( Node q, Node lit, bool pol, + std::map< Node, unsigned >& bound_lit_type_map, std::map< int, std::map< Node, Node > >& bound_lit_map, - std::map< int, std::map< Node, bool > >& bound_lit_pol_map ) { - if( lit.getKind()==GEQ && lit[0].getType().isInteger() ){ - std::map< Node, Node > msum; - if (QuantArith::getMonomialSumLit( lit, msum )){ - Trace("bound-int-debug") << "Literal (polarity = " << pol << ") " << lit << " is monomial sum : " << std::endl; - QuantArith::debugPrintMonomialSum( msum, "bound-int-debug" ); - for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ - if ( !it->first.isNull() && it->first.getKind()==BOUND_VARIABLE && !isBound( f, it->first ) ){ - Node veq; - if( QuantArith::isolate( it->first, msum, veq, GEQ )!=0 ){ - Node n1 = veq[0]; - Node n2 = veq[1]; - if(pol){ - //flip - n1 = veq[1]; - n2 = veq[0]; - if( n1.getKind()==BOUND_VARIABLE ){ - n2 = QuantArith::offset( n2, 1 ); + std::map< int, std::map< Node, bool > >& bound_lit_pol_map, + std::map< int, std::map< Node, Node > >& bound_int_range_term ) { + if( lit.getKind()==GEQ ){ + if( lit[0].getType().isInteger() ){ + std::map< Node, Node > msum; + if( QuantArith::getMonomialSumLit( lit, msum ) ){ + Trace("bound-int-debug") << "Literal (polarity = " << pol << ") " << lit << " is monomial sum : " << std::endl; + QuantArith::debugPrintMonomialSum( msum, "bound-int-debug" ); + for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ + if ( !it->first.isNull() && it->first.getKind()==BOUND_VARIABLE && !isBound( q, it->first ) ){ + Node veq; + if( QuantArith::isolate( it->first, msum, veq, GEQ )!=0 ){ + Node n1 = veq[0]; + Node n2 = veq[1]; + if(pol){ + //flip + n1 = veq[1]; + n2 = veq[0]; + if( n1.getKind()==BOUND_VARIABLE ){ + n2 = QuantArith::offset( n2, 1 ); + }else{ + n1 = QuantArith::offset( n1, -1 ); + } + veq = NodeManager::currentNM()->mkNode( GEQ, n1, n2 ); + } + Trace("bound-int-debug") << "Isolated for " << it->first << " : (" << n1 << " >= " << n2 << ")" << std::endl; + Node t = n1==it->first ? n2 : n1; + if( !hasNonBoundVar( q, t ) ) { + Trace("bound-int-debug") << "The bound is relevant." << std::endl; + int loru = n1==it->first ? 0 : 1; + bound_lit_type_map[it->first] = BOUND_INT_RANGE; + bound_int_range_term[loru][it->first] = t; + bound_lit_map[loru][it->first] = lit; + bound_lit_pol_map[loru][it->first] = pol; }else{ - n1 = QuantArith::offset( n1, -1 ); + Trace("bound-int-debug") << "The term " << t << " has non-bound variable." << std::endl; } - veq = NodeManager::currentNM()->mkNode( GEQ, n1, n2 ); - } - Trace("bound-int-debug") << "Isolated for " << it->first << " : (" << n1 << " >= " << n2 << ")" << std::endl; - Node t = n1==it->first ? n2 : n1; - if( !hasNonBoundVar( f, t ) ) { - Trace("bound-int-debug") << "The bound is relevant." << std::endl; - int loru = n1==it->first ? 0 : 1; - d_bounds[loru][f][it->first] = t; - bound_lit_map[loru][it->first] = lit; - bound_lit_pol_map[loru][it->first] = pol; - }else{ - Trace("bound-int-debug") << "The term " << t << " has non-bound variable." << std::endl; } } } } } + }else if( lit.getKind()==MEMBER ){ + //TODO: enable this when sets models are fixed + /* + if( !pol && lit[0].getKind()==BOUND_VARIABLE && !isBound( q, lit[0] ) && !lit[1].hasBoundVar() ){ + Trace("bound-int-debug") << "Literal (polarity = " << pol << ") " << lit << " is membership." << std::endl; + bound_lit_type_map[lit[0]] = BOUND_SET_MEMBER; + bound_lit_map[0][lit[0]] = lit; + bound_lit_pol_map[0][lit[0]] = pol; + } + */ }else if( lit.getKind()==LEQ || lit.getKind()==LT || lit.getKind()==GT ) { Message() << "BoundedIntegers : Bad kind for literal : " << lit << std::endl; } } -void BoundedIntegers::process( Node f, Node n, bool pol, +void BoundedIntegers::process( Node q, Node n, bool pol, + std::map< Node, unsigned >& bound_lit_type_map, std::map< int, std::map< Node, Node > >& bound_lit_map, - std::map< int, std::map< Node, bool > >& bound_lit_pol_map ){ + std::map< int, std::map< Node, bool > >& bound_lit_pol_map, + std::map< int, std::map< Node, Node > >& bound_int_range_term ){ if( (n.getKind()==OR && pol) || (n.getKind()==AND && !pol) ){ for( unsigned i=0; i<n.getNumChildren(); i++) { - process( f, n[i], pol, bound_lit_map, bound_lit_pol_map ); + process( q, n[i], pol, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term ); } }else if( n.getKind()==NOT ){ - process( f, n[0], !pol, bound_lit_map, bound_lit_pol_map ); + process( q, n[0], !pol, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term ); }else { - processLiteral( f, n, pol, bound_lit_map, bound_lit_pol_map ); + processLiteral( q, n, pol, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term ); } } @@ -258,58 +288,99 @@ void BoundedIntegers::addLiteralFromRange( Node lit, Node r ) { } } +void BoundedIntegers::setBoundedVar( Node q, Node v, unsigned bound_type ) { + d_bound_type[q][v] = bound_type; + d_set_nums[q][v] = d_set[q].size(); + d_set[q].push_back( v ); + Trace("bound-int-var") << "Bound variable #" << d_set_nums[q][v] << " : " << v << std::endl; +} + void BoundedIntegers::registerQuantifier( Node f ) { Trace("bound-int") << "Register quantifier " << f << std::endl; - bool hasIntType = false; - int finiteTypes = 0; - std::map< Node, int > numMap; - for( unsigned i=0; i<f[0].getNumChildren(); i++) { - numMap[f[0][i]] = i; - if( f[0][i].getType().isInteger() ){ - hasIntType = true; - } - else if( f[0][i].getType().isSort() || f[0][i].getType().getCardinality().isFinite() ){ - finiteTypes++; - } - } - if( hasIntType ){ - bool success; - do{ - std::map< int, std::map< Node, Node > > bound_lit_map; - std::map< int, std::map< Node, bool > > bound_lit_pol_map; - success = false; - process( f, f[1], true, bound_lit_map, bound_lit_pol_map ); - for( std::map< Node, Node >::iterator it = d_bounds[0][f].begin(); it != d_bounds[0][f].end(); ++it ){ - Node v = it->first; - if( !isBound(f,v) ){ - if( d_bounds[1][f].find(v)!=d_bounds[1][f].end() ){ - d_set[f].push_back(v); - d_set_nums[f].push_back(numMap[v]); + + bool success; + do{ + std::map< Node, unsigned > bound_lit_type_map; + std::map< int, std::map< Node, Node > > bound_lit_map; + std::map< int, std::map< Node, bool > > bound_lit_pol_map; + std::map< int, std::map< Node, Node > > bound_int_range_term; + success = false; + process( f, f[1], true, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term ); + //for( std::map< Node, Node >::iterator it = d_bounds[0][f].begin(); it != d_bounds[0][f].end(); ++it ){ + for( std::map< Node, unsigned >::iterator it = bound_lit_type_map.begin(); it != bound_lit_type_map.end(); ++it ){ + Node v = it->first; + if( !isBound( f, v ) ){ + bool setBoundVar = false; + if( it->second==BOUND_INT_RANGE ){ + //must have both + if( bound_lit_map[0].find( v )!=bound_lit_map[0].end() && bound_lit_map[1].find( v )!=bound_lit_map[1].end() ){ + setBoundedVar( f, v, BOUND_INT_RANGE ); + setBoundVar = true; success = true; - //set Attributes on literals for( unsigned b=0; b<2; b++ ){ - Assert( bound_lit_map[b].find( v )!=bound_lit_map[b].end() ); + //set the bounds + Assert( bound_int_range_term[b].find( v )!=bound_int_range_term[b].end() ); + d_bounds[b][f][v] = bound_int_range_term[b][v]; + } + Node r = NodeManager::currentNM()->mkNode( MINUS, d_bounds[1][f][v], d_bounds[0][f][v] ); + d_range[f][v] = Rewriter::rewrite( r ); + Trace("bound-int") << "Variable " << v << " is bound because of int range literals " << bound_lit_map[0][v] << " and " << bound_lit_map[1][v] << std::endl; + } + }else if( it->second==BOUND_SET_MEMBER ){ + setBoundedVar( f, v, BOUND_SET_MEMBER ); + setBoundVar = true; + d_setm_range[f][v] = bound_lit_map[0][v][1]; + Trace("bound-int") << "Variable " << v << " is bound because of set membership literal " << bound_lit_map[0][v] << std::endl; + } + if( setBoundVar ){ + //set Attributes on literals + for( unsigned b=0; b<2; b++ ){ + if( bound_lit_map[b].find( v )!=bound_lit_map[b].end() ){ Assert( bound_lit_pol_map[b].find( v )!=bound_lit_pol_map[b].end() ); BoundIntLitAttribute bila; bound_lit_map[b][v].setAttribute( bila, bound_lit_pol_map[b][v] ? 1 : 0 ); + }else{ + Assert( it->second!=BOUND_INT_RANGE ); } - Trace("bound-int") << "Variable " << v << " is bound because of literals " << bound_lit_map[0][v] << " and " << bound_lit_map[1][v] << std::endl; } } } - }while( success ); - Trace("bound-int") << "Bounds are : " << std::endl; - for( unsigned i=0; i<d_set[f].size(); i++) { - Node v = d_set[f][i]; - Node r = NodeManager::currentNM()->mkNode( MINUS, d_bounds[1][f][v], d_bounds[0][f][v] ); - d_range[f][v] = Rewriter::rewrite( r ); + } + }while( success ); + + Trace("bound-int") << "Bounds are : " << std::endl; + for( unsigned i=0; i<d_set[f].size(); i++) { + Node v = d_set[f][i]; + if( d_bound_type[f][v]==BOUND_INT_RANGE ){ Trace("bound-int") << " " << d_bounds[0][f][v] << " <= " << v << " <= " << d_bounds[1][f][v] << " (range is " << d_range[f][v] << ")" << std::endl; + }else if( d_bound_type[f][v]==BOUND_SET_MEMBER ){ + Trace("bound-int") << " " << v << " in " << d_setm_range[f][v] << std::endl; + } + } + + bool bound_success = true; + for( unsigned i=0; i<f[0].getNumChildren(); i++) { + if( d_bound_type[f].find( f[0][i] )==d_bound_type[f].end() ){ + TypeNode tn = f[0][i].getType(); + if( !tn.isSort() && !getTermDatabase()->mayComplete( tn ) ){ + Trace("bound-int-warn") << "Warning : Bounded Integers : Due to quantification on " << f[0][i] << ", could not find bounds for " << f << std::endl; + bound_success = false; + break; + } } - if( d_set[f].size()==(f[0].getNumChildren()-finiteTypes) ){ - d_bound_quants.push_back( f ); - for( unsigned i=0; i<d_set[f].size(); i++) { - Node v = d_set[f][i]; - Node r = d_range[f][v]; + } + + if( bound_success ){ + d_bound_quants.push_back( f ); + for( unsigned i=0; i<d_set[f].size(); i++) { + Node v = d_set[f][i]; + if( d_bound_type[f][v]==BOUND_INT_RANGE || d_bound_type[f][v]==BOUND_SET_MEMBER ){ + Node r; + if( d_bound_type[f][v]==BOUND_INT_RANGE ){ + r = d_range[f][v]; + }else if( d_bound_type[f][v]==BOUND_SET_MEMBER ){ + r = NodeManager::currentNM()->mkNode( CARD, d_setm_range[f][v] ); + } bool isProxy = false; if( r.hasBoundVar() ){ //introduce a new bound @@ -319,18 +390,15 @@ void BoundedIntegers::registerQuantifier( Node f ) { r = new_range; isProxy = true; } - if( r.getKind()!=CONST_RATIONAL ){ + if( !r.isConst() ){ if( std::find(d_ranges.begin(), d_ranges.end(), r)==d_ranges.end() ){ - Trace("bound-int") << "For " << v << ", bounded Integer Module will try to minimize : " << r << " " << r.getKind() << std::endl; + Trace("bound-int") << "For " << v << ", bounded Integer Module will try to minimize : " << r << std::endl; d_ranges.push_back( r ); - d_rms[r] = new RangeModel(this, r, d_quantEngine->getSatContext(), d_quantEngine->getUserContext(), isProxy ); + d_rms[r] = new IntRangeModel( this, r, d_quantEngine->getSatContext(), d_quantEngine->getUserContext(), isProxy ); d_rms[r]->initialize(); } } } - }else{ - Trace("bound-int-warn") << "Warning : Bounded Integers : Could not find bounds for " << f << std::endl; - //Message() << "Bound integers : Cannot infer bounds of " << f << std::endl; } } } @@ -376,39 +444,28 @@ Node BoundedIntegers::getNextDecisionRequest() { return Node::null(); } +unsigned BoundedIntegers::getBoundVarType( Node q, Node v ) { + std::map< Node, unsigned >::iterator it = d_bound_type[q].find( v ); + if( it==d_bound_type[q].end() ){ + return BOUND_NONE; + }else{ + return it->second; + } +} + void BoundedIntegers::getBounds( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ) { l = d_bounds[0][f][v]; u = d_bounds[1][f][v]; if( d_nground_range[f].find(v)!=d_nground_range[f].end() ){ - //must create substitution + //get the substitution std::vector< Node > vars; std::vector< Node > subs; - Trace("bound-int-rsi") << "Get bound value in model of variable " << v << std::endl; - for( unsigned i=0; i<d_set[f].size(); i++) { - if( d_set[f][i]!=v ){ - Trace("bound-int-rsi") << "Look up the value for " << d_set[f][i] << " " << rsi->d_var_order[d_set_nums[f][i]] << std::endl; - Trace("bound-int-rsi") << "term : " << rsi->getTerm(rsi->d_var_order[d_set_nums[f][i]]) << std::endl; - vars.push_back(d_set[f][i]); - subs.push_back(rsi->getTerm(rsi->d_var_order[d_set_nums[f][i]])); - }else{ - break; - } - } - Trace("bound-int-rsi") << "Do substitution..." << std::endl; - //check if it has been instantiated - if (!vars.empty() && !d_bnd_it[f][v].hasInstantiated(subs)){ - //must add the lemma - Node nn = d_nground_range[f][v]; - nn = nn.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); - Node lem = NodeManager::currentNM()->mkNode( LEQ, nn, d_range[f][v] ); - Trace("bound-int-lemma") << "*** Add lemma to minimize instantiated non-ground term " << lem << std::endl; - d_quantEngine->getOutputChannel().lemma(lem, false, true); - l = Node::null(); - u = Node::null(); - return; - }else{ + if( getRsiSubsitution( f, v, vars, subs, rsi ) ){ u = u.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); l = l.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + }else{ + u = Node::null(); + l = Node::null(); } } } @@ -416,12 +473,86 @@ void BoundedIntegers::getBounds( Node f, Node v, RepSetIterator * rsi, Node & l, void BoundedIntegers::getBoundValues( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ) { getBounds( f, v, rsi, l, u ); Trace("bound-int-rsi") << "Get value in model for..." << l << " and " << u << std::endl; - l = d_quantEngine->getModel()->getCurrentModelValue( l ); - u = d_quantEngine->getModel()->getCurrentModelValue( u ); + if( !l.isNull() ){ + l = d_quantEngine->getModel()->getCurrentModelValue( l ); + } + if( !u.isNull() ){ + u = d_quantEngine->getModel()->getCurrentModelValue( u ); + } Trace("bound-int-rsi") << "Value is " << l << " ... " << u << std::endl; return; } -bool BoundedIntegers::isGroundRange(Node f, Node v) { - return isBoundVar(f,v) && !getLowerBound(f,v).hasBoundVar() && !getUpperBound(f,v).hasBoundVar(); +bool BoundedIntegers::isGroundRange( Node q, Node v ) { + if( isBoundVar(q,v) ){ + if( d_bound_type[q][v]==BOUND_INT_RANGE ){ + return !getLowerBound(q,v).hasBoundVar() && !getUpperBound(q,v).hasBoundVar(); + }else if( d_bound_type[q][v]==BOUND_SET_MEMBER ){ + return !d_setm_range[q][v].hasBoundVar(); + } + } + return false; } + +Node BoundedIntegers::getSetRange( Node q, Node v, RepSetIterator * rsi ) { + Node sr = d_setm_range[q][v]; + if( d_nground_range[q].find(v)!=d_nground_range[q].end() ){ + //get the substitution + std::vector< Node > vars; + std::vector< Node > subs; + if( getRsiSubsitution( q, v, vars, subs, rsi ) ){ + sr = sr.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + }else{ + sr = Node::null(); + } + } + return sr; +} + +Node BoundedIntegers::getSetRangeValue( Node q, Node v, RepSetIterator * rsi ) { + Node sr = getSetRange( q, v, rsi ); + if( !sr.isNull() ){ + Trace("bound-int-rsi") << "Get value in model for..." << sr << std::endl; + sr = d_quantEngine->getModel()->getCurrentModelValue( sr ); + Trace("bound-int-rsi") << "Value is " << sr << std::endl; + } + return sr; +} + +bool BoundedIntegers::getRsiSubsitution( Node q, Node v, std::vector< Node >& vars, std::vector< Node >& subs, RepSetIterator * rsi ) { + + Trace("bound-int-rsi") << "Get bound value in model of variable " << v << std::endl; + Assert( d_set_nums[q].find( v )!=d_set_nums[q].end() ); + int vindex = d_set_nums[q][v]; + Assert( d_set_nums[q][v]==vindex ); + Trace("bound-int-rsi-debug") << " index order is " << vindex << std::endl; + //must take substitution for all variables that are iterating at higher level + for( int i=0; i<vindex; i++) { + Assert( d_set_nums[q][d_set[q][i]]==i ); + Trace("bound-int-rsi") << "Look up the value for " << d_set[q][i] << " " << i << std::endl; + int v = rsi->getVariableOrder( i ); + Assert( q[0][v]==d_set[q][i] ); + Node t = rsi->getCurrentTerm( v ); + Trace("bound-int-rsi") << "term : " << t << std::endl; + vars.push_back( d_set[q][i] ); + subs.push_back( t ); + } + + //check if it has been instantiated + if( !vars.empty() && !d_bnd_it[q][v].hasInstantiated(subs) ){ + if( d_bound_type[q][v]==BOUND_INT_RANGE ){ + //must add the lemma + Node nn = d_nground_range[q][v]; + nn = nn.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + Node lem = NodeManager::currentNM()->mkNode( LEQ, nn, d_range[q][v] ); + Trace("bound-int-lemma") << "*** Add lemma to minimize instantiated non-ground term " << lem << std::endl; + d_quantEngine->getOutputChannel().lemma(lem, false, true); + }else{ + //TODO : sets + } + return false; + }else{ + return true; + } +} + diff --git a/src/theory/quantifiers/bounded_integers.h b/src/theory/quantifiers/bounded_integers.h index 7d15097bd..ab4bcba96 100644..100755 --- a/src/theory/quantifiers/bounded_integers.h +++ b/src/theory/quantifiers/bounded_integers.h @@ -39,31 +39,57 @@ class BoundedIntegers : public QuantifiersModule typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap; typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; typedef context::CDHashMap<int, bool> IntBoolMap; +public: + enum { + BOUND_FINITE, + BOUND_INT_RANGE, + BOUND_SET_MEMBER, + BOUND_NONE + }; private: //for determining bounds bool isBound( Node f, Node v ); bool hasNonBoundVar( Node f, Node b ); - std::map< Node, std::map< Node, Node > > d_bounds[2]; + //bound type + std::map< Node, std::map< Node, unsigned > > d_bound_type; std::map< Node, std::vector< Node > > d_set; - std::map< Node, std::vector< int > > d_set_nums; + std::map< Node, std::map< Node, int > > d_set_nums; + //integer lower/upper bounds + std::map< Node, std::map< Node, Node > > d_bounds[2]; std::map< Node, std::map< Node, Node > > d_range; std::map< Node, std::map< Node, Node > > d_nground_range; + //set membership range + std::map< Node, std::map< Node, Node > > d_setm_range; void hasFreeVar( Node f, Node n ); void process( Node f, Node n, bool pol, + std::map< Node, unsigned >& bound_lit_type_map, std::map< int, std::map< Node, Node > >& bound_lit_map, - std::map< int, std::map< Node, bool > >& bound_lit_pol_map ); + std::map< int, std::map< Node, bool > >& bound_lit_pol_map, + std::map< int, std::map< Node, Node > >& bound_int_range_term ); void processLiteral( Node f, Node lit, bool pol, + std::map< Node, unsigned >& bound_lit_type_map, std::map< int, std::map< Node, Node > >& bound_lit_map, - std::map< int, std::map< Node, bool > >& bound_lit_pol_map ); + std::map< int, std::map< Node, bool > >& bound_lit_pol_map, + std::map< int, std::map< Node, Node > >& bound_int_range_term ); std::vector< Node > d_bound_quants; private: class RangeModel { + public: + RangeModel(){} + virtual ~RangeModel(){} + virtual void initialize() = 0; + virtual void assertNode(Node n) = 0; + virtual Node getNextDecisionRequest() = 0; + virtual bool proxyCurrentRange() = 0; + }; + class IntRangeModel : public RangeModel { private: BoundedIntegers * d_bi; void allocateRange(); Node d_proxy_range; public: - RangeModel(BoundedIntegers * bi, Node r, context::Context* c, context::Context* u, bool isProxy); + IntRangeModel( BoundedIntegers * bi, Node r, context::Context* c, context::Context* u, bool isProxy); + virtual ~IntRangeModel(){} Node d_range; int d_curr_max; std::map< int, Node > d_range_literal; @@ -108,27 +134,36 @@ private: std::map< Node, std::map< Node, BoundInstTrie > > d_bnd_it; private: void addLiteralFromRange( Node lit, Node r ); + + void setBoundedVar( Node f, Node v, unsigned bound_type ); public: BoundedIntegers( context::Context* c, QuantifiersEngine* qe ); - ~BoundedIntegers() throw() {} - + virtual ~BoundedIntegers(); + + void presolve(); bool needsCheck( Theory::Effort e ); void check( Theory::Effort e, unsigned quant_e ); void registerQuantifier( Node f ); void assertNode( Node n ); Node getNextDecisionRequest(); - bool isBoundVar( Node f, Node v ) { return std::find( d_set[f].begin(), d_set[f].end(), v )!=d_set[f].end(); } - unsigned getNumBoundVars( Node f ) { return d_set[f].size(); } - Node getBoundVar( Node f, int i ) { return d_set[f][i]; } - int getBoundVarNum( Node f, int i ) { return d_set_nums[f][i]; } - Node getLowerBound( Node f, Node v ){ return d_bounds[0][f][v]; } - Node getUpperBound( Node f, Node v ){ return d_bounds[1][f][v]; } + bool isBoundVar( Node q, Node v ) { return std::find( d_set[q].begin(), d_set[q].end(), v )!=d_set[q].end(); } + unsigned getBoundVarType( Node q, Node v ); + unsigned getNumBoundVars( Node q ) { return d_set[q].size(); } + Node getBoundVar( Node q, int i ) { return d_set[q][i]; } + //for integer range + Node getLowerBound( Node q, Node v ){ return d_bounds[0][q][v]; } + Node getUpperBound( Node q, Node v ){ return d_bounds[1][q][v]; } void getBounds( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ); void getBoundValues( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ); bool isGroundRange(Node f, Node v); + //for set range + Node getSetRange( Node q, Node v, RepSetIterator * rsi ); + Node getSetRangeValue( Node q, Node v, RepSetIterator * rsi ); /** Identify this module */ std::string identify() const { return "BoundedIntegers"; } +private: + bool getRsiSubsitution( Node q, Node v, std::vector< Node >& vars, std::vector< Node >& subs, RepSetIterator * rsi ); }; } diff --git a/src/theory/quantifiers/candidate_generator.cpp b/src/theory/quantifiers/candidate_generator.cpp index 43f5ee2fd..a0d9bda0f 100644..100755 --- a/src/theory/quantifiers/candidate_generator.cpp +++ b/src/theory/quantifiers/candidate_generator.cpp @@ -28,7 +28,7 @@ using namespace CVC4::theory; using namespace CVC4::theory::inst; bool CandidateGenerator::isLegalCandidate( Node n ){ - return ( !n.getAttribute(NoMatchAttribute()) && ( !options::cbqi() || !quantifiers::TermDb::hasInstConstAttr(n) ) ); + return d_qe->getTermDatabase()->isTermActive( n ) && ( !options::cbqi() || !quantifiers::TermDb::hasInstConstAttr(n) ); } void CandidateGeneratorQueue::addCandidate( Node n ) { @@ -59,7 +59,7 @@ Node CandidateGeneratorQueue::getNextCandidate(){ } CandidateGeneratorQE::CandidateGeneratorQE( QuantifiersEngine* qe, Node pat ) : - d_qe( qe ), d_term_iter( -1 ){ +CandidateGenerator( qe ), d_term_iter( -1 ){ d_op = qe->getTermDatabase()->getMatchOperator( pat ); Assert( !d_op.isNull() ); d_op_arity = pat.getNumChildren(); @@ -186,7 +186,7 @@ Node CandidateGeneratorQE::getNextCandidate(){ } CandidateGeneratorQELitEq::CandidateGeneratorQELitEq( QuantifiersEngine* qe, Node mpat ) : - d_match_pattern( mpat ), d_qe( qe ){ + CandidateGenerator( qe ), d_match_pattern( mpat ){ Assert( mpat.getKind()==EQUAL ); for( unsigned i=0; i<2; i++ ){ if( !quantifiers::TermDb::hasInstConstAttr(mpat[i]) ){ @@ -225,7 +225,7 @@ Node CandidateGeneratorQELitEq::getNextCandidate(){ CandidateGeneratorQELitDeq::CandidateGeneratorQELitDeq( QuantifiersEngine* qe, Node mpat ) : - d_match_pattern( mpat ), d_qe( qe ){ +CandidateGenerator( qe ), d_match_pattern( mpat ){ Assert( d_match_pattern.getKind()==EQUAL || d_match_pattern.getKind()==IFF ); d_match_pattern_type = d_match_pattern[0].getType(); @@ -259,7 +259,7 @@ Node CandidateGeneratorQELitDeq::getNextCandidate(){ CandidateGeneratorQEAll::CandidateGeneratorQEAll( QuantifiersEngine* qe, Node mpat ) : - d_match_pattern( mpat ), d_qe( qe ){ + CandidateGenerator( qe ), d_match_pattern( mpat ){ d_match_pattern_type = mpat.getType(); Assert( mpat.getKind()==INST_CONSTANT ); d_f = quantifiers::TermDb::getInstConstAttr( mpat ); diff --git a/src/theory/quantifiers/candidate_generator.h b/src/theory/quantifiers/candidate_generator.h index 18ef6a086..4fc6969fc 100644..100755 --- a/src/theory/quantifiers/candidate_generator.h +++ b/src/theory/quantifiers/candidate_generator.h @@ -33,8 +33,10 @@ namespace inst { /** base class for generating candidates for matching */ class CandidateGenerator { +protected: + QuantifiersEngine* d_qe; public: - CandidateGenerator(){} + CandidateGenerator( QuantifiersEngine* qe ) : d_qe( qe ){} virtual ~CandidateGenerator(){} /** Get candidates functions. These set up a context to get all match candidates. @@ -54,7 +56,7 @@ public: virtual void resetInstantiationRound() = 0; public: /** legal candidate */ - static bool isLegalCandidate( Node n ); + bool isLegalCandidate( Node n ); };/* class CandidateGenerator */ /** candidate generator queue (for manual candidate generation) */ @@ -63,7 +65,7 @@ private: std::vector< Node > d_candidates; int d_candidate_index; public: - CandidateGeneratorQueue() : d_candidate_index( 0 ){} + CandidateGeneratorQueue( QuantifiersEngine* qe ) : CandidateGenerator( qe ), d_candidate_index( 0 ){} ~CandidateGeneratorQueue() throw() {} void addCandidate( Node n ); @@ -80,8 +82,6 @@ class CandidateGeneratorQE : public CandidateGenerator private: //operator you are looking for Node d_op; - //instantiator pointer - QuantifiersEngine* d_qe; //the equality class iterator unsigned d_op_arity; std::vector< quantifiers::TermArgTrie* > d_tindex; @@ -122,8 +122,6 @@ private: Node d_match_pattern; Node d_match_gterm; bool d_do_mgt; - //einstantiator pointer - QuantifiersEngine* d_qe; public: CandidateGeneratorQELitEq( QuantifiersEngine* qe, Node mpat ); ~CandidateGeneratorQELitEq() throw() {} @@ -142,8 +140,6 @@ private: Node d_match_pattern; //type of disequality TypeNode d_match_pattern_type; - //einstantiator pointer - QuantifiersEngine* d_qe; public: CandidateGeneratorQELitDeq( QuantifiersEngine* qe, Node mpat ); ~CandidateGeneratorQELitDeq() throw() {} @@ -161,8 +157,6 @@ private: //equality you are trying to match equalities for Node d_match_pattern; TypeNode d_match_pattern_type; - //einstantiator pointer - QuantifiersEngine* d_qe; // quantifier/index for the variable we are matching Node d_f; unsigned d_index; diff --git a/src/theory/quantifiers/ce_guided_instantiation.cpp b/src/theory/quantifiers/ce_guided_instantiation.cpp index d9059a3e6..71bf7c426 100644..100755 --- a/src/theory/quantifiers/ce_guided_instantiation.cpp +++ b/src/theory/quantifiers/ce_guided_instantiation.cpp @@ -21,6 +21,8 @@ #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/term_database.h" #include "theory/theory_engine.h" +#include "prop/prop_engine.h" +#include "theory/bv/theory_bv_rewriter.h" using namespace CVC4::kind; using namespace std; @@ -46,7 +48,7 @@ void CegConjecture::assign( Node q ) { Assert( q.getKind()==FORALL ); d_assert_quant = q; //register with single invocation if applicable - if( d_qe->getTermDatabase()->isQAttrSygus( d_assert_quant ) && options::cegqiSingleInv() ){ + if( d_qe->getTermDatabase()->isQAttrSygus( d_assert_quant ) && options::cegqiSingleInvMode()!=CEGQI_SI_MODE_NONE ){ d_ceg_si->initialize( q ); if( q!=d_ceg_si->d_quant ){ //Node red_lem = NodeManager::currentNM()->mkNode( OR, q.negate(), d_cegqi_si->d_quant ); @@ -55,12 +57,15 @@ void CegConjecture::assign( Node q ) { } } d_quant = q; + Assert( d_candidates.empty() ); + std::vector< Node > vars; for( unsigned i=0; i<q[0].getNumChildren(); i++ ){ + vars.push_back( q[0][i] ); d_candidates.push_back( NodeManager::currentNM()->mkSkolem( "e", q[0][i].getType() ) ); } Trace("cegqi") << "Base quantified formula is : " << q << std::endl; //construct base instantiation - d_base_inst = Rewriter::rewrite( d_qe->getInstantiation( q, d_candidates ) ); + d_base_inst = Rewriter::rewrite( d_qe->getInstantiation( q, vars, d_candidates ) ); Trace("cegqi") << "Base instantiation is : " << d_base_inst << std::endl; if( d_qe->getTermDatabase()->isQAttrSygus( d_assert_quant ) ){ CegInstantiation::collectDisjuncts( d_base_inst, d_base_disj ); @@ -130,14 +135,18 @@ Node CegConjecture::getLiteral( QuantifiersEngine * qe, int i ) { qe->getOutputChannel().lemma( lem ); qe->getOutputChannel().requirePhase( lit, true ); - if( getCegqiFairMode()==CEGQI_FAIR_DT_HEIGHT_PRED ){ + if( getCegqiFairMode()==CEGQI_FAIR_DT_HEIGHT_PRED || getCegqiFairMode()==CEGQI_FAIR_DT_SIZE_PRED ){ //implies height bounds on each candidate variable std::vector< Node > lem_c; for( unsigned j=0; j<d_candidates.size(); j++ ){ - lem_c.push_back( NodeManager::currentNM()->mkNode( DT_HEIGHT_BOUND, d_candidates[j], c ) ); + if( getCegqiFairMode()==CEGQI_FAIR_DT_HEIGHT_PRED ){ + lem_c.push_back( NodeManager::currentNM()->mkNode( DT_HEIGHT_BOUND, d_candidates[j], c ) ); + }else{ + //lem_c.push_back( NodeManager::currentNM()->mkNode( DT_SIZE_BOUND, d_candidates[j], c ) ); + } } Node hlem = NodeManager::currentNM()->mkNode( OR, lit.negate(), lem_c.size()==1 ? lem_c[0] : NodeManager::currentNM()->mkNode( AND, lem_c ) ); - Trace("cegqi-lemma") << "Cegqi::Lemma : Fairness expansion (dt-height-pred) : " << hlem << std::endl; + Trace("cegqi-lemma") << "Cegqi::Lemma : Fairness expansion (pred) : " << hlem << std::endl; qe->getOutputChannel().lemma( hlem ); } return lit; @@ -257,8 +266,45 @@ void CegInstantiation::check( Theory::Effort e, unsigned quant_e ) { } } +void CegInstantiation::preRegisterQuantifier( Node q ) { + if( options::sygusDirectEval() ){ + if( q.getNumChildren()==3 && q[2].getKind()==INST_PATTERN_LIST && q[2][0].getKind()==INST_PATTERN ){ + //check whether it is an evaluation axiom + Node pat = q[2][0][0]; + if( pat.getKind()==APPLY_UF ){ + TypeNode tn = pat[0].getType(); + if( datatypes::DatatypesRewriter::isTypeDatatype(tn) ){ + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + if( dt.isSygus() ){ + //do unfolding if it induces Boolean structure, + //do direct evaluation if it does not induce Boolean structure, + // the reasoning is unfolding over these terms does not lead to helpful conflict analysis, and introduces many shared terms + bool directEval = true; + TypeNode ptn = pat.getType(); + if( ptn.isBoolean() || ptn.isBitVector() ){ + directEval = false; + }else{ + unsigned cindex = Datatype::indexOf(pat[0].getOperator().toExpr() ); + Node base = d_quantEngine->getTermDatabaseSygus()->getGenericBase( tn, dt, cindex ); + Trace("cegqi-debug") << "Generic base term for " << pat[0] << " is " << base << std::endl; + if( base.getKind()==ITE ){ + directEval = false; + } + } + if( directEval ){ + //take ownership of this quantified formula (will use direct evaluation instead of unfolding instantiation) + d_quantEngine->setOwner( q, this ); + d_eval_axioms[q] = true; + } + } + } + } + } + } +} + void CegInstantiation::registerQuantifier( Node q ) { - if( d_quantEngine->getOwner( q )==this ){ + if( d_quantEngine->getOwner( q )==this && d_eval_axioms.find( q )==d_eval_axioms.end() ){ if( !d_conj->isAssigned() ){ Trace("cegqi") << "Register conjecture : " << q << std::endl; d_conj->assign( q ); @@ -278,7 +324,7 @@ void CegInstantiation::registerQuantifier( Node q ) { if( it!=d_uf_measure.end() ){ mc.push_back( NodeManager::currentNM()->mkNode( APPLY_UF, it->second, d_conj->d_candidates[j] ) ); } - }else if( d_conj->getCegqiFairMode()==CEGQI_FAIR_DT_HEIGHT_PRED ){ + }else if( d_conj->getCegqiFairMode()==CEGQI_FAIR_DT_HEIGHT_PRED || d_conj->getCegqiFairMode()==CEGQI_FAIR_DT_SIZE_PRED ){ //measure term is a fresh constant mc.push_back( NodeManager::currentNM()->mkSkolem( "K", NodeManager::currentNM()->integerType() ) ); } @@ -291,6 +337,8 @@ void CegInstantiation::registerQuantifier( Node q ) { }else{ Assert( d_conj->d_quant==q ); } + }else{ + Trace("cegqi-debug") << "Register quantifier : " << q << std::endl; } } @@ -317,7 +365,7 @@ Node CegInstantiation::getNextDecisionRequest() { Trace("cegqi-debug") << "CEGQI : Decide next on : " << req_dec[i] << "..." << std::endl; return req_dec[i]; }else{ - Trace("cegqi-debug") << "CEGQI : " << req_dec[i] << " already has value " << value << std::endl; + Trace("cegqi-debug2") << "CEGQI : " << req_dec[i] << " already has value " << value << std::endl; } } @@ -350,6 +398,11 @@ void CegInstantiation::checkCegConjecture( CegConjecture * conj ) { Trace("cegqi-engine-debug") << conj->d_candidates[i] << " "; } Trace("cegqi-engine-debug") << std::endl; + Trace("cegqi-engine-debug") << " * Candidate ce skolems : "; + for( unsigned i=0; i<conj->d_ce_sk.size(); i++ ){ + Trace("cegqi-engine-debug") << conj->d_ce_sk[i] << " "; + } + Trace("cegqi-engine-debug") << std::endl; if( conj->getCegqiFairMode()!=CEGQI_FAIR_NONE ){ Trace("cegqi-engine") << " * Current term size : " << conj->d_curr_lit.get() << std::endl; } @@ -374,6 +427,24 @@ void CegInstantiation::checkCegConjecture( CegConjecture * conj ) { } std::vector< Node > model_values; if( getModelValues( conj, conj->d_candidates, model_values ) ){ + if( options::sygusDirectEval() ){ + std::vector< Node > eager_eval_lem; + for( unsigned j=0; j<conj->d_candidates.size(); j++ ){ + d_quantEngine->getTermDatabaseSygus()->registerModelValue( conj->d_candidates[j], model_values[j], eager_eval_lem ); + } + if( !eager_eval_lem.empty() ){ + for( unsigned j=0; j<eager_eval_lem.size(); j++ ){ + Node lem = eager_eval_lem[j]; + if( d_quantEngine->getTheoryEngine()->isTheoryEnabled(THEORY_BV) ){ + //FIXME: hack to incorporate hacks from BV for division by zero + lem = bv::TheoryBVRewriter::eliminateBVSDiv( lem ); + } + Trace("cegqi-lemma") << "Cegqi::Lemma : evaluation : " << lem << std::endl; + d_quantEngine->addLemma( lem ); + } + return; + } + } //check if we must apply fairness lemmas if( conj->getCegqiFairMode()==CEGQI_FAIR_UF_DT_SIZE ){ std::vector< Node > lems; @@ -390,6 +461,7 @@ void CegInstantiation::checkCegConjecture( CegConjecture * conj ) { } } //must get a counterexample to the value of the current candidate + Assert( conj->d_candidates.size()==model_values.size() ); Node inst = conj->d_base_inst.substitute( conj->d_candidates.begin(), conj->d_candidates.end(), model_values.begin(), model_values.end() ); //check whether we will run CEGIS on inner skolem variables bool sk_refine = ( !conj->isGround() || conj->d_refine_count==0 ); @@ -422,10 +494,30 @@ void CegInstantiation::checkCegConjecture( CegConjecture * conj ) { Node lem = NodeManager::currentNM()->mkNode( OR, ic ); lem = Rewriter::rewrite( lem ); d_last_inst_si = false; + //eagerly unfold applications of evaluation function + if( options::sygusDirectEval() ){ + Trace("cegqi-eager") << "pre-unfold counterexample : " << lem << std::endl; + std::map< Node, Node > visited_n; + lem = getEagerUnfold( lem, visited_n ); + } + Trace("cegqi-lemma") << "Cegqi::Lemma : counterexample : " << lem << std::endl; - d_quantEngine->addLemma( lem ); - ++(d_statistics.d_cegqi_lemmas_ce); - Trace("cegqi-engine") << " ...find counterexample." << std::endl; + if( d_quantEngine->addLemma( lem ) ){ + ++(d_statistics.d_cegqi_lemmas_ce); + Trace("cegqi-engine") << " ...find counterexample." << std::endl; + }else{ + //this may happen if we eagerly unfold, simplify to true + if( !options::sygusDirectEval() ){ + Trace("cegqi-engine") << " ...FAILED to add candidate!" << std::endl; + }else{ + Trace("cegqi-engine-debug") << " ...FAILED to add candidate!" << std::endl; + } + if( conj->d_refine_count==0 ){ + //immediately go to refine candidate + checkCegConjecture( conj ); + return; + } + } } }else{ @@ -589,6 +681,74 @@ void CegInstantiation::getMeasureLemmas( Node n, Node v, std::vector< Node >& le } } +Node CegInstantiation::getEagerUnfold( Node n, std::map< Node, Node >& visited ) { + std::map< Node, Node >::iterator itv = visited.find( n ); + if( itv==visited.end() ){ + Trace("cegqi-eager-debug") << "getEagerUnfold " << n << std::endl; + Node ret; + if( n.getKind()==APPLY_UF ){ + TypeNode tn = n[0].getType(); + Trace("cegqi-eager-debug") << "check " << n[0].getType() << std::endl; + if( datatypes::DatatypesRewriter::isTypeDatatype(tn) ){ + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + if( dt.isSygus() ){ + Trace("cegqi-eager") << "Unfold eager : " << n << std::endl; + Node bTerm = d_quantEngine->getTermDatabaseSygus()->sygusToBuiltin( n[0], tn ); + Trace("cegqi-eager") << "Built-in term : " << bTerm << std::endl; + std::vector< Node > vars; + std::vector< Node > subs; + Node var_list = Node::fromExpr( dt.getSygusVarList() ); + Assert( var_list.getNumChildren()+1==n.getNumChildren() ); + for( unsigned j=0; j<var_list.getNumChildren(); j++ ){ + vars.push_back( var_list[j] ); + } + for( unsigned j=1; j<n.getNumChildren(); j++ ){ + Node nc = getEagerUnfold( n[j], visited ); + if( var_list[j-1].getType().isBoolean() ){ + //TODO: remove this case when boolean term conversion is eliminated + Node c = NodeManager::currentNM()->mkConst(BitVector(1u, 1u)); + subs.push_back( nc.eqNode( c ) ); + }else{ + subs.push_back( nc ); + } + Assert( subs[j-1].getType()==var_list[j-1].getType() ); + } + Assert( vars.size()==subs.size() ); + bTerm = bTerm.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + Trace("cegqi-eager") << "Built-in term after subs : " << bTerm << std::endl; + Trace("cegqi-eager-debug") << "Types : " << bTerm.getType() << " " << n.getType() << std::endl; + Assert( n.getType()==bTerm.getType() ); + ret = bTerm; + } + } + } + if( ret.isNull() ){ + if( n.getKind()!=FORALL ){ + bool childChanged = false; + std::vector< Node > children; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + Node nc = getEagerUnfold( n[i], visited ); + childChanged = childChanged || n[i]!=nc; + children.push_back( nc ); + } + if( childChanged ){ + if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ + children.insert( children.begin(), n.getOperator() ); + } + ret = NodeManager::currentNM()->mkNode( n.getKind(), children ); + } + } + if( ret.isNull() ){ + ret = n; + } + } + visited[n] = ret; + return ret; + }else{ + return itv->second; + } +} + void CegInstantiation::printSynthSolution( std::ostream& out ) { if( d_conj->isAssigned() ){ Trace("cegqi-debug") << "Printing synth solution..." << std::endl; diff --git a/src/theory/quantifiers/ce_guided_instantiation.h b/src/theory/quantifiers/ce_guided_instantiation.h index 57dc31850..c8b41c035 100644..100755 --- a/src/theory/quantifiers/ce_guided_instantiation.h +++ b/src/theory/quantifiers/ce_guided_instantiation.h @@ -126,6 +126,8 @@ private: CegConjecture * d_conj; /** last instantiation by single invocation module? */ bool d_last_inst_si; + /** evaluation axioms */ + std::map< Node, bool > d_eval_axioms; private: //for enforcing fairness /** measure functions */ std::map< TypeNode, Node > d_uf_measure; @@ -139,6 +141,8 @@ private: //for enforcing fairness std::map< Node, std::map< int, Node > > d_size_term_lemma; /** get measure lemmas */ void getMeasureLemmas( Node n, Node v, std::vector< Node >& lems ); + /** get eager unfolding */ + Node getEagerUnfold( Node n, std::map< Node, Node >& visited ); private: /** check conjecture */ void checkCegConjecture( CegConjecture * conj ); @@ -156,6 +160,7 @@ public: /* Call during quantifier engine's check */ void check( Theory::Effort e, unsigned quant_e ); /* Called for new quantifiers */ + void preRegisterQuantifier( Node q ); void registerQuantifier( Node q ); void assertNode( Node n ); Node getNextDecisionRequest(); diff --git a/src/theory/quantifiers/ce_guided_single_inv.cpp b/src/theory/quantifiers/ce_guided_single_inv.cpp index 33856d226..3177739ac 100644..100755 --- a/src/theory/quantifiers/ce_guided_single_inv.cpp +++ b/src/theory/quantifiers/ce_guided_single_inv.cpp @@ -111,6 +111,7 @@ void CegConjectureSingleInv::getInitialSingleInvLemma( std::vector< Node >& lems void CegConjectureSingleInv::initialize( Node q ) { Assert( d_quant.isNull() ); + Assert( options::cegqiSingleInvMode()!=CEGQI_SI_MODE_NONE ); //initialize data d_quant = q; //process @@ -121,6 +122,7 @@ void CegConjectureSingleInv::initialize( Node q ) { std::map< Node, std::map< Node, std::vector< Node > > > prog_invoke; std::vector< Node > progs; std::map< Node, std::map< Node, bool > > contains; + bool is_syntax_restricted = false; for( unsigned i=0; i<q[0].getNumChildren(); i++ ){ progs.push_back( q[0][i] ); //check whether all types have ITE @@ -131,161 +133,173 @@ void CegConjectureSingleInv::initialize( Node q ) { d_has_ites = false; } } + Assert( datatypes::DatatypesRewriter::isTypeDatatype(tn) ); + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + Assert( dt.isSygus() ); + if( !dt.getSygusAllowAll() ){ + is_syntax_restricted = true; + } } - Node qq = q[1]; - if( q[1].getKind()==NOT && q[1][0].getKind()==FORALL ){ - qq = q[1][0][1]; - }else{ - qq = TermDb::simpleNegate( qq ); - } - //remove the deep embedding - std::map< Node, Node > visited; - std::vector< TypeNode > types; - std::vector< Node > order_vars; - std::map< Node, Node > single_inv_app_map; - int type_valid = 0; - qq = removeDeepEmbedding( qq, progs, types, type_valid, visited ); - Trace("cegqi-si-debug") << "- Remove deep embedding, got : " << qq << ", type valid = " << type_valid << std::endl; + //abort if not aggressive bool singleInvocation = true; - if( type_valid==0 ){ - //process the single invocation-ness of the property - d_sip->init( types, qq ); - Trace("cegqi-si") << "- Partitioned to single invocation parts : " << std::endl; - d_sip->debugPrint( "cegqi-si" ); - //map from program to bound variables - for( unsigned j=0; j<progs.size(); j++ ){ - Node prog = progs[j]; - std::map< Node, Node >::iterator it_nsi = d_nsi_op_map.find( prog ); - if( it_nsi!=d_nsi_op_map.end() ){ - Node op = it_nsi->second; - std::map< Node, Node >::iterator it_fov = d_sip->d_func_fo_var.find( op ); - if( it_fov!=d_sip->d_func_fo_var.end() ){ - Node pv = it_fov->second; - Assert( d_sip->d_func_inv.find( op )!=d_sip->d_func_inv.end() ); - Node inv = d_sip->d_func_inv[op]; - single_inv_app_map[prog] = inv; - Trace("cegqi-si") << " " << pv << ", " << inv << " is associated with program " << prog << std::endl; - d_prog_to_sol_index[prog] = order_vars.size(); - order_vars.push_back( pv ); + if( options::cegqiSingleInvMode()==CEGQI_SI_MODE_USE && is_syntax_restricted ){ + singleInvocation = false; + Trace("cegqi-si") << "...grammar is restricted, do not use single invocation techniques." << std::endl; + }else{ + Node qq = q[1]; + if( q[1].getKind()==NOT && q[1][0].getKind()==FORALL ){ + qq = q[1][0][1]; + }else{ + qq = TermDb::simpleNegate( qq ); + } + //remove the deep embedding + std::map< Node, Node > visited; + std::vector< TypeNode > types; + std::vector< Node > order_vars; + std::map< Node, Node > single_inv_app_map; + int type_valid = 0; + qq = removeDeepEmbedding( qq, progs, types, type_valid, visited ); + Trace("cegqi-si-debug") << "- Remove deep embedding, got : " << qq << ", type valid = " << type_valid << std::endl; + if( type_valid==0 ){ + //process the single invocation-ness of the property + d_sip->init( types, qq ); + Trace("cegqi-si") << "- Partitioned to single invocation parts : " << std::endl; + d_sip->debugPrint( "cegqi-si" ); + //map from program to bound variables + for( unsigned j=0; j<progs.size(); j++ ){ + Node prog = progs[j]; + std::map< Node, Node >::iterator it_nsi = d_nsi_op_map.find( prog ); + if( it_nsi!=d_nsi_op_map.end() ){ + Node op = it_nsi->second; + std::map< Node, Node >::iterator it_fov = d_sip->d_func_fo_var.find( op ); + if( it_fov!=d_sip->d_func_fo_var.end() ){ + Node pv = it_fov->second; + Assert( d_sip->d_func_inv.find( op )!=d_sip->d_func_inv.end() ); + Node inv = d_sip->d_func_inv[op]; + single_inv_app_map[prog] = inv; + Trace("cegqi-si") << " " << pv << ", " << inv << " is associated with program " << prog << std::endl; + d_prog_to_sol_index[prog] = order_vars.size(); + order_vars.push_back( pv ); + } + }else{ + //does not mention the function } - }else{ - //does not mention the function } - } - //reorder the variables - Assert( d_sip->d_func_vars.size()==order_vars.size() ); - d_sip->d_func_vars.clear(); - d_sip->d_func_vars.insert( d_sip->d_func_vars.begin(), order_vars.begin(), order_vars.end() ); + //reorder the variables + Assert( d_sip->d_func_vars.size()==order_vars.size() ); + d_sip->d_func_vars.clear(); + d_sip->d_func_vars.insert( d_sip->d_func_vars.begin(), order_vars.begin(), order_vars.end() ); - //check if it is single invocation - if( !d_sip->d_conjuncts[1].empty() ){ - singleInvocation = false; - if( options::cegqiSingleInvPartial() ){ - //this enables partially single invocation techniques - d_nsingle_inv = d_sip->getNonSingleInvocation(); - d_nsingle_inv = TermDb::simpleNegate( d_nsingle_inv ); - d_full_inv = d_sip->getFullSpecification(); - d_full_inv = TermDb::simpleNegate( d_full_inv ); - singleInvocation = true; - }else if( options::sygusInvTemplMode() != SYGUS_INV_TEMPL_MODE_NONE ){ - //if we are doing invariant templates, then construct the template - std::map< Node, bool > has_inv; - std::map< Node, std::vector< Node > > inv_pre_post[2]; - for( unsigned i=0; i<d_sip->d_conjuncts[2].size(); i++ ){ - std::vector< Node > disjuncts; - Node func; - int pol = -1; - Trace("cegqi-inv") << "INV process " << d_sip->d_conjuncts[2][i] << std::endl; - d_sip->extractInvariant( d_sip->d_conjuncts[2][i], func, pol, disjuncts ); - if( pol>=0 ){ - Assert( d_nsi_op_map_to_prog.find( func )!=d_nsi_op_map_to_prog.end() ); - Node prog = d_nsi_op_map_to_prog[func]; - Trace("cegqi-inv") << "..." << ( pol==0 ? "pre" : "post" ) << "-condition for " << prog << "." << std::endl; - Node c = disjuncts.empty() ? d_qe->getTermDatabase()->d_false : ( disjuncts.size()==1 ? disjuncts[0] : NodeManager::currentNM()->mkNode( OR, disjuncts ) ); - c = pol==0 ? TermDb::simpleNegate( c ) : c; - Trace("cegqi-inv-debug") << "...extracted : " << c << std::endl; - inv_pre_post[pol][prog].push_back( c ); - has_inv[prog] = true; - }else{ - Trace("cegqi-inv") << "...no status." << std::endl; + //check if it is single invocation + if( !d_sip->d_conjuncts[1].empty() ){ + singleInvocation = false; + if( options::cegqiSingleInvPartial() ){ + //this enables partially single invocation techniques + d_nsingle_inv = d_sip->getNonSingleInvocation(); + d_nsingle_inv = TermDb::simpleNegate( d_nsingle_inv ); + d_full_inv = d_sip->getFullSpecification(); + d_full_inv = TermDb::simpleNegate( d_full_inv ); + singleInvocation = true; + }else if( options::sygusInvTemplMode() != SYGUS_INV_TEMPL_MODE_NONE ){ + //if we are doing invariant templates, then construct the template + std::map< Node, bool > has_inv; + std::map< Node, std::vector< Node > > inv_pre_post[2]; + for( unsigned i=0; i<d_sip->d_conjuncts[2].size(); i++ ){ + std::vector< Node > disjuncts; + Node func; + int pol = -1; + Trace("cegqi-inv") << "INV process " << d_sip->d_conjuncts[2][i] << std::endl; + d_sip->extractInvariant( d_sip->d_conjuncts[2][i], func, pol, disjuncts ); + if( pol>=0 ){ + Assert( d_nsi_op_map_to_prog.find( func )!=d_nsi_op_map_to_prog.end() ); + Node prog = d_nsi_op_map_to_prog[func]; + Trace("cegqi-inv") << "..." << ( pol==0 ? "pre" : "post" ) << "-condition for " << prog << "." << std::endl; + Node c = disjuncts.empty() ? d_qe->getTermDatabase()->d_false : ( disjuncts.size()==1 ? disjuncts[0] : NodeManager::currentNM()->mkNode( OR, disjuncts ) ); + c = pol==0 ? TermDb::simpleNegate( c ) : c; + Trace("cegqi-inv-debug") << "...extracted : " << c << std::endl; + inv_pre_post[pol][prog].push_back( c ); + has_inv[prog] = true; + }else{ + Trace("cegqi-inv") << "...no status." << std::endl; + } } - } - Trace("cegqi-inv") << "Constructing invariant templates..." << std::endl; - //now, contruct the template for the invariant(s) - std::map< Node, Node > prog_templ; - for( std::map< Node, bool >::iterator iti = has_inv.begin(); iti != has_inv.end(); ++iti ){ - Node prog = iti->first; - Trace("cegqi-inv") << "...for " << prog << "..." << std::endl; - Trace("cegqi-inv") << " args : "; + Trace("cegqi-inv") << "Constructing invariant templates..." << std::endl; + //now, contruct the template for the invariant(s) + std::map< Node, Node > prog_templ; + for( std::map< Node, bool >::iterator iti = has_inv.begin(); iti != has_inv.end(); ++iti ){ + Node prog = iti->first; + Trace("cegqi-inv") << "...for " << prog << "..." << std::endl; + Trace("cegqi-inv") << " args : "; + for( unsigned j=0; j<d_sip->d_si_vars.size(); j++ ){ + std::stringstream ss; + ss << "i_" << j; + Node v = NodeManager::currentNM()->mkBoundVar( ss.str(), d_sip->d_si_vars[j].getType() ); + d_prog_templ_vars[prog].push_back( v ); + Trace("cegqi-inv") << v << " "; + } + Trace("cegqi-inv") << std::endl; + Node pre = inv_pre_post[0][prog].empty() ? NodeManager::currentNM()->mkConst( false ) : + ( inv_pre_post[0][prog].size()==1 ? inv_pre_post[0][prog][0] : NodeManager::currentNM()->mkNode( OR, inv_pre_post[0][prog] ) ); + d_trans_pre[prog] = pre.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), d_prog_templ_vars[prog].begin(), d_prog_templ_vars[prog].end() ); + Node post = inv_pre_post[1][prog].empty() ? NodeManager::currentNM()->mkConst( true ) : + ( inv_pre_post[1][prog].size()==1 ? inv_pre_post[1][prog][0] : NodeManager::currentNM()->mkNode( AND, inv_pre_post[1][prog] ) ); + d_trans_post[prog] = post.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), d_prog_templ_vars[prog].begin(), d_prog_templ_vars[prog].end() ); + Trace("cegqi-inv") << " precondition : " << d_trans_pre[prog] << std::endl; + Trace("cegqi-inv") << " postcondition : " << d_trans_post[prog] << std::endl; + Node invariant = single_inv_app_map[prog]; + invariant = invariant.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), d_prog_templ_vars[prog].begin(), d_prog_templ_vars[prog].end() ); + Trace("cegqi-inv") << " invariant : " << invariant << std::endl; + //construct template + Node templ; + if( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_PRE ){ + //templ = 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], invariant ); + }else{ + Assert( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_POST ); + //templ = 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], invariant ); + } + visited.clear(); + templ = addDeepEmbedding( templ, visited ); + Trace("cegqi-inv") << " template : " << templ << std::endl; + prog_templ[prog] = templ; + } + Node bd = d_sip->d_conjuncts[2].size()==1 ? d_sip->d_conjuncts[2][0] : NodeManager::currentNM()->mkNode( AND, d_sip->d_conjuncts[2] ); + visited.clear(); + bd = addDeepEmbedding( bd, visited ); + Trace("cegqi-inv") << " body : " << bd << std::endl; + bd = substituteInvariantTemplates( bd, prog_templ, d_prog_templ_vars ); + Trace("cegqi-inv-debug") << " templ-subs body : " << bd << std::endl; + //make inner existential + std::vector< Node > new_var_bv; for( unsigned j=0; j<d_sip->d_si_vars.size(); j++ ){ std::stringstream ss; - ss << "i_" << j; - Node v = NodeManager::currentNM()->mkBoundVar( ss.str(), d_sip->d_si_vars[j].getType() ); - d_prog_templ_vars[prog].push_back( v ); - Trace("cegqi-inv") << v << " "; + ss << "ss_" << j; + new_var_bv.push_back( NodeManager::currentNM()->mkBoundVar( ss.str(), d_sip->d_si_vars[j].getType() ) ); } - Trace("cegqi-inv") << std::endl; - Node pre = inv_pre_post[0][prog].empty() ? NodeManager::currentNM()->mkConst( false ) : - ( inv_pre_post[0][prog].size()==1 ? inv_pre_post[0][prog][0] : NodeManager::currentNM()->mkNode( OR, inv_pre_post[0][prog] ) ); - d_trans_pre[prog] = pre.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), d_prog_templ_vars[prog].begin(), d_prog_templ_vars[prog].end() ); - Node post = inv_pre_post[1][prog].empty() ? NodeManager::currentNM()->mkConst( true ) : - ( inv_pre_post[1][prog].size()==1 ? inv_pre_post[1][prog][0] : NodeManager::currentNM()->mkNode( AND, inv_pre_post[1][prog] ) ); - d_trans_post[prog] = post.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), d_prog_templ_vars[prog].begin(), d_prog_templ_vars[prog].end() ); - Trace("cegqi-inv") << " precondition : " << d_trans_pre[prog] << std::endl; - Trace("cegqi-inv") << " postcondition : " << d_trans_post[prog] << std::endl; - Node invariant = single_inv_app_map[prog]; - invariant = invariant.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), d_prog_templ_vars[prog].begin(), d_prog_templ_vars[prog].end() ); - Trace("cegqi-inv") << " invariant : " << invariant << std::endl; - //construct template - Node templ; - if( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_PRE ){ - //templ = 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], invariant ); - }else{ - Assert( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_POST ); - //templ = 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], invariant ); + bd = bd.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), new_var_bv.begin(), new_var_bv.end() ); + Assert( q[1].getKind()==NOT && q[1][0].getKind()==FORALL ); + for( unsigned j=0; j<q[1][0][0].getNumChildren(); j++ ){ + new_var_bv.push_back( q[1][0][0][j] ); } - visited.clear(); - templ = addDeepEmbedding( templ, visited ); - Trace("cegqi-inv") << " template : " << templ << std::endl; - prog_templ[prog] = templ; - } - Node bd = d_sip->d_conjuncts[2].size()==1 ? d_sip->d_conjuncts[2][0] : NodeManager::currentNM()->mkNode( AND, d_sip->d_conjuncts[2] ); - visited.clear(); - bd = addDeepEmbedding( bd, visited ); - Trace("cegqi-inv") << " body : " << bd << std::endl; - bd = substituteInvariantTemplates( bd, prog_templ, d_prog_templ_vars ); - Trace("cegqi-inv-debug") << " templ-subs body : " << bd << std::endl; - //make inner existential - std::vector< Node > new_var_bv; - for( unsigned j=0; j<d_sip->d_si_vars.size(); j++ ){ - std::stringstream ss; - ss << "ss_" << j; - new_var_bv.push_back( NodeManager::currentNM()->mkBoundVar( ss.str(), d_sip->d_si_vars[j].getType() ) ); - } - bd = bd.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), new_var_bv.begin(), new_var_bv.end() ); - Assert( q[1].getKind()==NOT && q[1][0].getKind()==FORALL ); - for( unsigned j=0; j<q[1][0][0].getNumChildren(); j++ ){ - new_var_bv.push_back( q[1][0][0][j] ); - } - if( !new_var_bv.empty() ){ - Node bvl = NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, new_var_bv ); - bd = NodeManager::currentNM()->mkNode( FORALL, bvl, bd ).negate(); + if( !new_var_bv.empty() ){ + Node bvl = NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, new_var_bv ); + bd = NodeManager::currentNM()->mkNode( FORALL, bvl, bd ).negate(); + } + //make outer universal + bd = NodeManager::currentNM()->mkNode( FORALL, q[0], bd ); + bd = Rewriter::rewrite( bd ); + Trace("cegqi-inv") << " rtempl-subs body : " << bd << std::endl; + d_quant = bd; } - //make outer universal - bd = NodeManager::currentNM()->mkNode( FORALL, q[0], bd ); - bd = Rewriter::rewrite( bd ); - Trace("cegqi-inv") << " rtempl-subs body : " << bd << std::endl; - d_quant = bd; + }else{ + //we are fully single invocation } }else{ - //we are fully single invocation + Trace("cegqi-si") << "...property is not single invocation, involves functions with different argument sorts." << std::endl; + singleInvocation = false; } - }else{ - Trace("cegqi-si") << "...property is not single invocation, involves functions with different argument sorts." << std::endl; - singleInvocation = false; } if( singleInvocation ){ d_single_inv = d_sip->getSingleInvocation(); @@ -534,6 +548,7 @@ bool CegConjectureSingleInv::doAddInstantiation( std::vector< Node >& subs ){ return false; }else{ Trace("cegqi-engine") << siss.str() << std::endl; + Assert( d_single_inv_var.size()==subs.size() ); Node lem = d_single_inv[1].substitute( d_single_inv_var.begin(), d_single_inv_var.end(), subs.begin(), subs.end() ); if( d_qe->getTermDatabase()->containsVtsTerm( lem ) ){ Trace("cegqi-engine-debug") << "Rewrite based on vts symbols..." << std::endl; @@ -595,6 +610,7 @@ bool CegConjectureSingleInv::check( std::vector< Node >& lems ) { for( unsigned i=0; i<d_sip->d_all_vars.size(); i++ ){ subs.push_back( NodeManager::currentNM()->mkSkolem( "kv", d_sip->d_all_vars[i].getType(), "created for verifying nsi" ) ); } + Assert( d_sip->d_all_vars.size()==subs.size() ); inst = inst.substitute( d_sip->d_all_vars.begin(), d_sip->d_all_vars.end(), subs.begin(), subs.end() ); Trace("cegqi-nsi") << "NSI : verification : " << inst << std::endl; Trace("cegqi-lemma") << "Cegqi::Lemma : verification lemma : " << inst << std::endl; @@ -751,6 +767,7 @@ Node CegConjectureSingleInv::getSolution( unsigned sol_index, TypeNode stn, int& std::sort( indices.begin(), indices.end(), ssii ); Trace("csi-sol") << "Construct solution" << std::endl; s = constructSolution( indices, sol_index, 0 ); + Assert( vars.size()==d_sol->d_varList.size() ); s = s.substitute( vars.begin(), vars.end(), d_sol->d_varList.begin(), d_sol->d_varList.end() ); } d_orig_solution = s; @@ -826,8 +843,8 @@ Node CegConjectureSingleInv::reconstructToSyntax( Node s, TypeNode stn, int& rec } bool CegConjectureSingleInv::needsCheck() { - if( options::cegqiSingleInvMultiInstAbort() ){ - if( !hasITEs() ){ + if( options::cegqiSingleInvMode()==CEGQI_SI_MODE_ALL_ABORT ){ + if( !d_has_ites ){ return d_inst.empty(); } } @@ -922,6 +939,7 @@ void SingleInvocationPartition::process( Node n ) { std::vector< Node > funcs; //normalize the invocations if( !terms.empty() ){ + Assert( terms.size()==subs.size() ); cr = cr.substitute( terms.begin(), terms.end(), subs.begin(), subs.end() ); } std::vector< Node > children; @@ -940,6 +958,7 @@ void SingleInvocationPartition::process( Node n ) { } Trace("si-prt") << std::endl; cr = children.size()==1 ? children[0] : NodeManager::currentNM()->mkNode( OR, children ); + Assert( terms.size()==subs.size() ); cr = cr.substitute( terms.begin(), terms.end(), subs.begin(), subs.end() ); Trace("si-prt-debug") << "...normalized invocations to " << cr << std::endl; //now must check if it has other bound variables @@ -974,6 +993,7 @@ void SingleInvocationPartition::process( Node n ) { } } } + Assert( terms.size()==subs.size() ); cr = cr.substitute( terms.begin(), terms.end(), subs.begin(), subs.end() ); } cr = Rewriter::rewrite( cr ); @@ -982,6 +1002,7 @@ void SingleInvocationPartition::process( Node n ) { TermDb::getBoundVars( cr, d_all_vars ); if( singleInvocation ){ //replace with single invocation formulation + Assert( si_terms.size()==si_subs.size() ); cr = cr.substitute( si_terms.begin(), si_terms.end(), si_subs.begin(), si_subs.end() ); cr = Rewriter::rewrite( cr ); Trace("si-prt") << ".....si version=" << cr << std::endl; diff --git a/src/theory/quantifiers/ce_guided_single_inv.h b/src/theory/quantifiers/ce_guided_single_inv.h index 4d2f9a0e5..4d2f9a0e5 100644..100755 --- a/src/theory/quantifiers/ce_guided_single_inv.h +++ b/src/theory/quantifiers/ce_guided_single_inv.h diff --git a/src/theory/quantifiers/ce_guided_single_inv_ei.cpp b/src/theory/quantifiers/ce_guided_single_inv_ei.cpp index 6394fca3d..6394fca3d 100644..100755 --- a/src/theory/quantifiers/ce_guided_single_inv_ei.cpp +++ b/src/theory/quantifiers/ce_guided_single_inv_ei.cpp diff --git a/src/theory/quantifiers/ce_guided_single_inv_ei.h b/src/theory/quantifiers/ce_guided_single_inv_ei.h index 42e0b0820..42e0b0820 100644..100755 --- a/src/theory/quantifiers/ce_guided_single_inv_ei.h +++ b/src/theory/quantifiers/ce_guided_single_inv_ei.h diff --git a/src/theory/quantifiers/ce_guided_single_inv_sol.cpp b/src/theory/quantifiers/ce_guided_single_inv_sol.cpp index 240c2ed12..240c2ed12 100644..100755 --- a/src/theory/quantifiers/ce_guided_single_inv_sol.cpp +++ b/src/theory/quantifiers/ce_guided_single_inv_sol.cpp diff --git a/src/theory/quantifiers/ce_guided_single_inv_sol.h b/src/theory/quantifiers/ce_guided_single_inv_sol.h index cb6f6bc41..cb6f6bc41 100644..100755 --- a/src/theory/quantifiers/ce_guided_single_inv_sol.h +++ b/src/theory/quantifiers/ce_guided_single_inv_sol.h diff --git a/src/theory/quantifiers/ceg_instantiator.cpp b/src/theory/quantifiers/ceg_instantiator.cpp index da488ea98..cd263e90c 100644..100755 --- a/src/theory/quantifiers/ceg_instantiator.cpp +++ b/src/theory/quantifiers/ceg_instantiator.cpp @@ -22,6 +22,9 @@ #include "theory/quantifiers/term_database.h" #include "theory/theory_engine.h" +#include "theory/bv/theory_bv_utils.h" +#include "util/bitvector.h" + //#define MBP_STRICT_ASSERTIONS using namespace std; @@ -466,6 +469,36 @@ bool CegInstantiator::doAddInstantiation( SolvedForm& sf, SolvedForm& ssf, std:: } } } + /* TODO: algebraic reasoning for bitvector instantiation + else if( pvtn.isBitVector() ){ + if( atom.getKind()==BITVECTOR_ULT || atom.getKind()==BITVECTOR_ULE ){ + for( unsigned t=0; t<2; t++ ){ + if( atom[t]==pv ){ + computeProgVars( atom[1-t] ); + if( d_inelig.find( atom[1-t] )==d_inelig.end() ){ + //only ground terms TODO: more + if( d_prog_var[atom[1-t]].empty() ){ + Node veq_c; + Node uval; + if( ( !pol && atom.getKind()==BITVECTOR_ULT ) || ( pol && atom.getKind()==BITVECTOR_ULE ) ){ + uval = atom[1-t]; + }else{ + uval = NodeManager::currentNM()->mkNode( (atom.getKind()==BITVECTOR_ULT)==(t==1) ? BITVECTOR_PLUS : BITVECTOR_SUB, atom[1-t], + bv::utils::mkConst(pvtn.getConst<BitVectorSize>(), 1) ); + } + if( subs_proc[uval].find( veq_c )==subs_proc[uval].end() ){ + subs_proc[uval][veq_c] = true; + if( doAddInstantiationInc( uval, pv, veq_c, 0, sf, ssf, vars, btyp, theta, i, effort, cons, curr_var ) ){ + return true; + } + } + } + } + } + } + } + } + */ } } } @@ -685,7 +718,7 @@ bool CegInstantiator::doAddInstantiation( SolvedForm& sf, SolvedForm& ssf, std:: //[5] resort to using value in model // do so if we are in effort=1, or if the variable is boolean, or if we are solving for a subfield of a datatype - if( ( effort>0 || pvtn.isBoolean() || !curr_var.empty() ) && d_qe->getTermDatabase()->isClosedEnumerableType( pvtn ) ){ + if( ( effort>0 || pvtn.isBoolean() || pvtn.isBitVector() || !curr_var.empty() ) && d_qe->getTermDatabase()->isClosedEnumerableType( pvtn ) ){ Node mv = getModelValue( pv ); Node pv_coeff_m; Trace("cbqi-inst-debug") << "[5] " << i << "...try model value " << mv << std::endl; @@ -781,6 +814,7 @@ bool CegInstantiator::doAddInstantiationInc( Node n, Node pv, Node pv_coeff, int } } if( success ){ + Trace("cbqi-inst-debug2") << "Adding to vectors..." << std::endl; vars.push_back( pv ); btyp.push_back( bt ); sf.push_back( pv, n, pv_coeff ); @@ -800,8 +834,10 @@ bool CegInstantiator::doAddInstantiationInc( Node n, Node pv, Node pv_coeff, int curr_var.pop_back(); is_cv = true; } + Trace("cbqi-inst-debug2") << "Recurse..." << std::endl; success = doAddInstantiation( sf, ssf, vars, btyp, new_theta, curr_var.empty() ? i+1 : i, effort, cons, curr_var ); if( !success ){ + Trace("cbqi-inst-debug2") << "Removing from vectors..." << std::endl; if( is_cv ){ curr_var.push_back( pv ); } @@ -814,6 +850,7 @@ bool CegInstantiator::doAddInstantiationInc( Node n, Node pv, Node pv_coeff, int if( success ){ return true; }else{ + Trace("cbqi-inst-debug2") << "Revert substitutions..." << std::endl; //revert substitution information for( std::map< int, Node >::iterator it = prev_subs.begin(); it != prev_subs.end(); it++ ){ sf.d_subs[it->first] = it->second; @@ -1035,7 +1072,13 @@ Node CegInstantiator::applySubstitution( TypeNode tn, Node n, std::vector< Node if( !it->second.isNull() ){ c_coeff = NodeManager::currentNM()->mkNode( MULT, c_coeff, it->second ); } - Node c = NodeManager::currentNM()->mkNode( MULT, c_coeff, msum_term[it->first] ); + Assert( !c_coeff.isNull() ); + Node c; + if( msum_term[it->first].isNull() ){ + c = c_coeff; + }else{ + c = NodeManager::currentNM()->mkNode( MULT, c_coeff, msum_term[it->first] ); + } children.push_back( c ); Trace("cegqi-si-apply-subs-debug") << "Add child : " << c << std::endl; } @@ -1594,6 +1637,7 @@ int CegInstantiator::solve_arith( Node pv, Node atom, Node& veq_c, Node& val, No realPart = real_part.empty() ? d_zero : ( real_part.size()==1 ? real_part[0] : NodeManager::currentNM()->mkNode( PLUS, real_part ) ); Assert( d_out->isEligibleForInstantiation( realPart ) ); //re-isolate + Trace("cbqi-inst-debug") << "Re-isolate..." << std::endl; ires = QuantArith::isolate( pv, msum, veq_c, val, atom.getKind() ); Trace("cbqi-inst-debug") << "Isolate for mixed Int/Real : " << veq_c << " * " << pv << " " << atom.getKind() << " " << val << std::endl; Trace("cbqi-inst-debug") << " real part : " << realPart << std::endl; diff --git a/src/theory/quantifiers/ceg_instantiator.h b/src/theory/quantifiers/ceg_instantiator.h index 3d7bbcb55..3d7bbcb55 100644..100755 --- a/src/theory/quantifiers/ceg_instantiator.h +++ b/src/theory/quantifiers/ceg_instantiator.h diff --git a/src/theory/quantifiers/conjecture_generator.cpp b/src/theory/quantifiers/conjecture_generator.cpp index 2cc49ef5a..f4eb67d74 100644..100755 --- a/src/theory/quantifiers/conjecture_generator.cpp +++ b/src/theory/quantifiers/conjecture_generator.cpp @@ -285,7 +285,7 @@ Node ConjectureGenerator::getFreeVar( TypeNode tn, unsigned i ) { } bool ConjectureGenerator::isHandledTerm( TNode n ){ - return !n.getAttribute(NoMatchAttribute()) && inst::Trigger::isAtomicTrigger( n ) && ( n.getKind()!=APPLY_UF || n.getOperator().getKind()!=SKOLEM ); + return d_quantEngine->getTermDatabase()->isTermActive( n ) && inst::Trigger::isAtomicTrigger( n ) && ( n.getKind()!=APPLY_UF || n.getOperator().getKind()!=SKOLEM ); } Node ConjectureGenerator::getGroundEqc( TNode r ) { @@ -425,7 +425,7 @@ void ConjectureGenerator::check( Theory::Effort e, unsigned quant_e ) { eq::EqClassIterator eqc_i = eq::EqClassIterator( r, ee ); while( !eqc_i.isFinished() ){ TNode n = (*eqc_i); - if( getTermDatabase()->hasTermCurrent( n ) && !n.getAttribute(NoMatchAttribute()) && ( n.getKind()!=EQUAL || isFalse ) ){ + if( getTermDatabase()->hasTermCurrent( n ) && getTermDatabase()->isTermActive( n ) && ( n.getKind()!=EQUAL || isFalse ) ){ if( firstTime ){ Trace("sg-gen-eqc") << "e" << d_em[r] << " : { " << std::endl; firstTime = false; @@ -1572,7 +1572,6 @@ bool TermGenerator::getNextMatch( TermGenEnv * s, TNode eqc, std::map< TypeNode, if( d_match_status_child_num==0 ){ //initial binding TNode f = s->getTgFunc( d_typ, d_status_num ); - //std::map< TNode, TermArgTrie >::iterator it = s->getTermDatabase()->d_func_map_eqc_trie[f].d_data.find( eqc ); Assert( !eqc.isNull() ); TermArgTrie * tat = s->getTermDatabase()->getTermArgTrie( eqc, f ); if( tat ){ @@ -1726,9 +1725,9 @@ void TermGenEnv::collectSignatureInformation() { d_func_kind.clear(); d_func_args.clear(); TypeNode tnull; - for( std::map< Node, TermArgTrie >::iterator it = getTermDatabase()->d_func_map_trie.begin(); it != getTermDatabase()->d_func_map_trie.end(); ++it ){ - if( getTermDatabase()->getNumGroundTerms( it->first )>0 ){ - Node nn = getTermDatabase()->getGroundTerm( it->first, 0 ); + for( std::map< Node, std::vector< Node > >::iterator it = getTermDatabase()->d_op_map.begin(); it != getTermDatabase()->d_op_map.end(); ++it ){ + if( !it->second.empty() ){ + Node nn = it->second[0]; Trace("sg-rel-sig-debug") << "Check in signature : " << nn << std::endl; if( d_cg->isHandledTerm( nn ) && nn.getKind()!=APPLY_SELECTOR_TOTAL && !nn.getType().isBoolean() ){ bool do_enum = true; @@ -1750,7 +1749,7 @@ void TermGenEnv::collectSignatureInformation() { d_typ_tg_funcs[nn.getType()].push_back( it->first ); d_tg_func_param[it->first] = ( nn.getMetaKind() == kind::metakind::PARAMETERIZED ); Trace("sg-rel-sig") << "Will enumerate function applications of : " << it->first << ", #args = " << d_func_args[it->first].size() << ", kind = " << nn.getKind() << std::endl; - getTermDatabase()->computeUfEqcTerms( it->first ); + //getTermDatabase()->computeUfEqcTerms( it->first ); } } Trace("sg-rel-sig-debug") << "Done check in signature : " << nn << std::endl; diff --git a/src/theory/quantifiers/conjecture_generator.h b/src/theory/quantifiers/conjecture_generator.h index c89d0f2ee..c89d0f2ee 100644..100755 --- a/src/theory/quantifiers/conjecture_generator.h +++ b/src/theory/quantifiers/conjecture_generator.h diff --git a/src/theory/quantifiers/equality_infer.cpp b/src/theory/quantifiers/equality_infer.cpp index c3064116f..5190025ee 100644..100755 --- a/src/theory/quantifiers/equality_infer.cpp +++ b/src/theory/quantifiers/equality_infer.cpp @@ -53,10 +53,10 @@ void EqualityInference::addToExplanation( std::vector< Node >& exp, Node e ) { } void EqualityInference::addToExplanationEqc( std::vector< Node >& exp, Node eqc ) { - NodeListMap::iterator re_i = d_rep_exp.find( eqc ); + NodeIntMap::iterator re_i = d_rep_exp.find( eqc ); if( re_i!=d_rep_exp.end() ){ - for( unsigned i=0; i<(*re_i).second->size(); i++ ){ - addToExplanation( exp, (*(*re_i).second)[i] ); + for( int i=0; i<(*re_i).second; i++ ){ + addToExplanation( exp, d_rep_exp_data[eqc][i] ); } } //for( unsigned i=0; i<d_eqci[n]->d_rep_exp.size(); i++ ){ @@ -65,16 +65,19 @@ void EqualityInference::addToExplanationEqc( std::vector< Node >& exp, Node eqc } void EqualityInference::addToExplanationEqc( Node eqc, std::vector< Node >& exp_to_add ) { - NodeListMap::iterator re_i = d_rep_exp.find( eqc ); - NodeList* re; + NodeIntMap::iterator re_i = d_rep_exp.find( eqc ); + int n_re = 0; if( re_i != d_rep_exp.end() ){ - re = (*re_i).second; - }else{ - re = new(d_c->getCMM()) NodeList( true, d_c, false, context::ContextMemoryAllocator<TNode>(d_c->getCMM()) ); - d_rep_exp.insertDataFromContextMemory( eqc, re ); + n_re = (*re_i).second; } + d_rep_exp[eqc] = n_re + exp_to_add.size(); for( unsigned i=0; i<exp_to_add.size(); i++ ){ - re->push_back( exp_to_add[i] ); + if( n_re<(int)d_rep_exp_data[eqc].size() ){ + d_rep_exp_data[eqc][n_re] = exp_to_add[i]; + }else{ + d_rep_exp_data[eqc].push_back( exp_to_add[i] ); + } + n_re++; } //for( unsigned i=0; i<exp_to_add.size(); i++ ){ // eqci->d_rep_exp.push_back( exp_to_add[i] ); @@ -204,16 +207,18 @@ void EqualityInference::eqNotifyNewClass(TNode t) { void EqualityInference::addToUseList( Node used, Node eqc ) { #if 1 - NodeListMap::iterator ul_i = d_uselist.find( used ); - NodeList* ul; + NodeIntMap::iterator ul_i = d_uselist.find( used ); + int n_ul = 0; if( ul_i != d_uselist.end() ){ - ul = (*ul_i).second; - }else{ - ul = new(d_c->getCMM()) NodeList( true, d_c, false, context::ContextMemoryAllocator<TNode>(d_c->getCMM()) ); - d_uselist.insertDataFromContextMemory( used, ul ); + n_ul = (*ul_i).second; } + d_uselist[ used ] = n_ul + 1; Trace("eq-infer-debug") << " add to use list : " << used << " -> " << eqc << std::endl; - (*ul).push_back( eqc ); + if( n_ul<(int)d_uselist_data[used].size() ){ + d_uselist_data[used][n_ul] = eqc; + }else{ + d_uselist_data[used].push_back( eqc ); + } #else std::map< Node, EqcInfo * >::iterator itu = d_eqci.find( used ); EqcInfo * eqci_used; @@ -356,12 +361,12 @@ void EqualityInference::eqNotifyMerge(TNode t1, TNode t2) { //go through all equivalence classes that may refer to v_solve std::map< Node, bool > processed; processed[v_solve] = true; - NodeListMap::iterator ul_i = d_uselist.find( v_solve ); + NodeIntMap::iterator ul_i = d_uselist.find( v_solve ); if( ul_i != d_uselist.end() ){ - NodeList* ul = (*ul_i).second; - Trace("eq-infer-debug") << " use list size = " << ul->size() << std::endl; - for( unsigned j=0; j<ul->size(); j++ ){ - Node r = (*ul)[j]; + int n_ul = (*ul_i).second; + Trace("eq-infer-debug") << " use list size = " << n_ul << std::endl; + for( int j=0; j<n_ul; j++ ){ + Node r = d_uselist_data[v_solve][j]; //Trace("eq-infer-debug") << " use list size = " << eqci_solved->d_uselist.size() << std::endl; //for( unsigned j=0; j<eqci_solved->d_uselist.size(); j++ ){ // Node r = eqci_solved->d_uselist[j]; diff --git a/src/theory/quantifiers/equality_infer.h b/src/theory/quantifiers/equality_infer.h index 93c7bd080..80d6ef98b 100644..100755 --- a/src/theory/quantifiers/equality_infer.h +++ b/src/theory/quantifiers/equality_infer.h @@ -39,7 +39,7 @@ class EqualityInference typedef context::CDHashMap< Node, Node, NodeHashFunction > NodeMap; typedef context::CDHashMap< Node, bool, NodeHashFunction > BoolMap; typedef context::CDChunkList<Node> NodeList; - typedef context::CDHashMap< Node, NodeList *, NodeHashFunction > NodeListMap; + typedef context::CDHashMap< Node, int, NodeHashFunction > NodeIntMap; private: context::Context * d_c; Node d_one; @@ -67,11 +67,13 @@ private: BoolMap d_elim_vars; std::map< Node, EqcInfo * > d_eqci; NodeMap d_rep_to_eqc; - NodeListMap d_rep_exp; + NodeIntMap d_rep_exp; + std::map< Node, std::vector< Node > > d_rep_exp_data; /** set eqc rep */ void setEqcRep( Node t, Node r, std::vector< Node >& exp_to_add, EqcInfo * eqci ); /** use list */ - NodeListMap d_uselist; + NodeIntMap d_uselist; + std::map< Node, std::vector< Node > > d_uselist_data; void addToUseList( Node used, Node eqc ); /** pending merges */ NodeList d_pending_merges; diff --git a/src/theory/quantifiers/first_order_model.cpp b/src/theory/quantifiers/first_order_model.cpp index a833f48d2..670f0eff3 100644..100755 --- a/src/theory/quantifiers/first_order_model.cpp +++ b/src/theory/quantifiers/first_order_model.cpp @@ -406,8 +406,8 @@ Node FirstOrderModelIG::evaluateTerm( Node n, int& depIndex, RepSetIterator* ri //check the type of n if( n.getKind()==INST_CONSTANT ){ int v = n.getAttribute(InstVarNumAttribute()); - depIndex = ri->d_var_order[ v ]; - val = ri->getTerm( v ); + depIndex = ri->getIndexOrder( v ); + val = ri->getCurrentTerm( v ); }else if( n.getKind()==ITE ){ int depIndex1, depIndex2; int eval = evaluate( n[0], depIndex1, ri ); diff --git a/src/theory/quantifiers/first_order_model.h b/src/theory/quantifiers/first_order_model.h index cbe83cfa5..cbe83cfa5 100644..100755 --- a/src/theory/quantifiers/first_order_model.h +++ b/src/theory/quantifiers/first_order_model.h diff --git a/src/theory/quantifiers/full_model_check.cpp b/src/theory/quantifiers/full_model_check.cpp index 33c853328..a0665cb7f 100644..100755 --- a/src/theory/quantifiers/full_model_check.cpp +++ b/src/theory/quantifiers/full_model_check.cpp @@ -405,7 +405,7 @@ void FullModelChecker::processBuildModel(TheoryModel* m, bool fullModel){ bool needsDefault = true; for( size_t i=0; i<fm->d_uf_terms[op].size(); i++ ){ Node n = fm->d_uf_terms[op][i]; - if( !n.getAttribute(NoMatchAttribute()) ){ + if( d_qe->getTermDatabase()->isTermActive( n ) ){ add_conds.push_back( n ); add_values.push_back( n ); Node r = fm->getUsedRepresentative(n); @@ -764,7 +764,7 @@ bool FullModelChecker::exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, No Trace("fmc-exh-debug") << "Set element domains..." << std::endl; //set the domains based on the entry for (unsigned i=0; i<c.getNumChildren(); i++) { - if (riter.d_enum_type[i]==RepSetIterator::ENUM_DOMAIN_ELEMENTS) { + if( riter.d_enum_type[i]==RepSetIterator::ENUM_DOMAIN_ELEMENTS || riter.d_enum_type[i]==RepSetIterator::ENUM_SET_MEMBERS ){ TypeNode tn = c[i].getType(); if( d_rep_ids.find(tn)!=d_rep_ids.end() ){ if( fm->isInterval(c[i]) || fm->isStar(c[i]) ){ @@ -773,6 +773,7 @@ bool FullModelChecker::exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, No if (d_rep_ids[tn].find(c[i])!=d_rep_ids[tn].end()) { riter.d_domain[i].clear(); riter.d_domain[i].push_back(d_rep_ids[tn][c[i]]); + riter.d_enum_type[i] = RepSetIterator::ENUM_DOMAIN_ELEMENTS; }else{ Trace("fmc-exh") << "---- Does not have rep : " << c[i] << " for type " << tn << std::endl; return false; @@ -792,7 +793,7 @@ bool FullModelChecker::exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, No std::vector< Node > ev_inst; std::vector< Node > inst; for( int i=0; i<riter.getNumTerms(); i++ ){ - Node rr = riter.getTerm( i ); + Node rr = riter.getCurrentTerm( i ); Node r = rr; //if( r.getType().isSort() ){ r = fm->getUsedRepresentative( r ); @@ -826,18 +827,18 @@ bool FullModelChecker::exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, No int index = riter.increment(); Trace("fmc-exh-debug") << "Incremented index " << index << std::endl; if( !riter.isFinished() ){ - if (index>=0 && riter.d_index[index]>0 && addedLemmas>0 && riter.d_enum_type[index]==RepSetIterator::ENUM_RANGE) { + if (index>=0 && riter.d_index[index]>0 && addedLemmas>0 && riter.d_enum_type[index]==RepSetIterator::ENUM_INT_RANGE) { Trace("fmc-exh-debug") << "Since this is a range enumeration, skip to the next..." << std::endl; riter.increment2( index-1 ); } } } d_addedLemmas += addedLemmas; - Trace("fmc-exh") << "----Finished Exhaustive instantiate, lemmas = " << addedLemmas << ", incomplete=" << riter.d_incomplete << std::endl; - return addedLemmas>0 || !riter.d_incomplete; + Trace("fmc-exh") << "----Finished Exhaustive instantiate, lemmas = " << addedLemmas << ", incomplete=" << riter.isIncomplete() << std::endl; + return addedLemmas>0 || !riter.isIncomplete(); }else{ Trace("fmc-exh") << "----Finished Exhaustive instantiate, failed." << std::endl; - return false; + return !riter.isIncomplete(); } } diff --git a/src/theory/quantifiers/full_model_check.h b/src/theory/quantifiers/full_model_check.h index 411b7a5eb..411b7a5eb 100644..100755 --- a/src/theory/quantifiers/full_model_check.h +++ b/src/theory/quantifiers/full_model_check.h diff --git a/src/theory/quantifiers/fun_def_engine.cpp b/src/theory/quantifiers/fun_def_engine.cpp index cf1d14663..cf1d14663 100644..100755 --- a/src/theory/quantifiers/fun_def_engine.cpp +++ b/src/theory/quantifiers/fun_def_engine.cpp diff --git a/src/theory/quantifiers/fun_def_engine.h b/src/theory/quantifiers/fun_def_engine.h index 3b95281c0..3b95281c0 100644..100755 --- a/src/theory/quantifiers/fun_def_engine.h +++ b/src/theory/quantifiers/fun_def_engine.h diff --git a/src/theory/quantifiers/fun_def_process.cpp b/src/theory/quantifiers/fun_def_process.cpp index 9109aab8a..9109aab8a 100644..100755 --- a/src/theory/quantifiers/fun_def_process.cpp +++ b/src/theory/quantifiers/fun_def_process.cpp diff --git a/src/theory/quantifiers/fun_def_process.h b/src/theory/quantifiers/fun_def_process.h index 1f6ee6562..1f6ee6562 100644..100755 --- a/src/theory/quantifiers/fun_def_process.h +++ b/src/theory/quantifiers/fun_def_process.h diff --git a/src/theory/quantifiers/inst_match.cpp b/src/theory/quantifiers/inst_match.cpp index 8818175db..8818175db 100644..100755 --- a/src/theory/quantifiers/inst_match.cpp +++ b/src/theory/quantifiers/inst_match.cpp diff --git a/src/theory/quantifiers/inst_match.h b/src/theory/quantifiers/inst_match.h index ad287c1a3..ad287c1a3 100644..100755 --- a/src/theory/quantifiers/inst_match.h +++ b/src/theory/quantifiers/inst_match.h diff --git a/src/theory/quantifiers/inst_match_generator.cpp b/src/theory/quantifiers/inst_match_generator.cpp index bf05de3bb..2d3bf76f6 100644..100755 --- a/src/theory/quantifiers/inst_match_generator.cpp +++ b/src/theory/quantifiers/inst_match_generator.cpp @@ -32,6 +32,7 @@ namespace theory { namespace inst { InstMatchGenerator::InstMatchGenerator( Node pat ){ + d_cg = NULL; d_needsReset = true; d_active_add = false; Assert( quantifiers::TermDb::hasInstConstAttr(pat) ); @@ -43,12 +44,20 @@ InstMatchGenerator::InstMatchGenerator( Node pat ){ } InstMatchGenerator::InstMatchGenerator() { + d_cg = NULL; d_needsReset = true; d_active_add = false; d_next = NULL; d_matchPolicy = MATCH_GEN_DEFAULT; } +InstMatchGenerator::~InstMatchGenerator() throw() { + for( unsigned i=0; i<d_children.size(); i++ ){ + delete d_children[i]; + } + delete d_cg; +} + void InstMatchGenerator::setActiveAdd(bool val){ d_active_add = val; if( d_next!=NULL ){ @@ -150,7 +159,7 @@ void InstMatchGenerator::initialize( Node q, QuantifiersEngine* qe, std::vector< d_cg = new inst::CandidateGeneratorQELitDeq( qe, d_match_pattern ); } }else{ - d_cg = new CandidateGeneratorQueue; + d_cg = new CandidateGeneratorQueue( qe ); Trace("inst-match-gen-warn") << "(?) Unknown matching pattern is " << d_match_pattern << std::endl; d_matchPolicy = MATCH_GEN_INTERNAL_ERROR; } @@ -249,7 +258,7 @@ bool InstMatchGenerator::getMatch( Node f, Node t, InstMatch& m, QuantifiersEngi Trace("matching-debug2") << "Reset children..." << std::endl; //now, fit children into match //we will be requesting candidates for matching terms for each child - for( int i=0; i<(int)d_children.size(); i++ ){ + for( unsigned i=0; i<d_children.size(); i++ ){ d_children[i]->reset( t[ d_children_index[i] ], qe ); } Trace("matching-debug2") << "Continue next " << d_next << std::endl; @@ -484,7 +493,7 @@ d_f( q ){ } Debug("smart-multi-trigger") << std::endl; } - for( int i=0; i<(int)pats.size(); i++ ){ + for( unsigned i=0; i<pats.size(); i++ ){ Node n = pats[i]; //make the match generator d_children.push_back( InstMatchGenerator::mkInstMatchGenerator(q, n, qe ) ); @@ -492,7 +501,7 @@ d_f( q ){ std::vector< int > unique_vars; std::map< int, bool > shared_vars; int numSharedVars = 0; - for( int j=0; j<(int)d_var_contains[n].size(); j++ ){ + for( unsigned j=0; j<d_var_contains[n].size(); j++ ){ if( d_var_to_node[ d_var_contains[n][j] ].size()==1 ){ Debug("smart-multi-trigger") << "Var " << d_var_contains[n][j] << " is unique to " << pats[i] << std::endl; unique_vars.push_back( d_var_contains[n][j] ); @@ -503,7 +512,7 @@ d_f( q ){ } //we use the latest shared variables, then unique variables std::vector< int > vars; - int index = i==0 ? (int)(pats.size()-1) : (i-1); + unsigned index = i==0 ? pats.size()-1 : (i-1); while( numSharedVars>0 && index!=i ){ for( std::map< int, bool >::iterator it = shared_vars.begin(); it != shared_vars.end(); ++it ){ if( it->second ){ @@ -519,16 +528,24 @@ d_f( q ){ } vars.insert( vars.end(), unique_vars.begin(), unique_vars.end() ); Debug("smart-multi-trigger") << " Index[" << i << "]: "; - for( int i=0; i<(int)vars.size(); i++ ){ - Debug("smart-multi-trigger") << vars[i] << " "; + for( unsigned j=0; j<vars.size(); j++ ){ + Debug("smart-multi-trigger") << vars[j] << " "; } Debug("smart-multi-trigger") << std::endl; //make ordered inst match trie - InstMatchTrie::ImtIndexOrder* imtio = new InstMatchTrie::ImtIndexOrder; - imtio->d_order.insert( imtio->d_order.begin(), vars.begin(), vars.end() ); - d_children_trie.push_back( InstMatchTrieOrdered( imtio ) ); + d_imtio[i] = new InstMatchTrie::ImtIndexOrder; + d_imtio[i]->d_order.insert( d_imtio[i]->d_order.begin(), vars.begin(), vars.end() ); + d_children_trie.push_back( InstMatchTrieOrdered( d_imtio[i] ) ); } +} +InstMatchGeneratorMulti::~InstMatchGeneratorMulti() throw() { + for( unsigned i=0; i<d_children.size(); i++ ){ + delete d_children[i]; + } + for( std::map< unsigned, InstMatchTrie::ImtIndexOrder* >::iterator it = d_imtio.begin(); it != d_imtio.end(); ++it ){ + delete it->second; + } } /** reset instantiation round (call this whenever equivalence classes have changed) */ @@ -697,7 +714,7 @@ int InstMatchGeneratorMulti::addTerm( Node q, Node t, QuantifiersEngine* qe ){ return addedLemmas; } -InstMatchGeneratorSimple::InstMatchGeneratorSimple( Node q, Node pat ) : d_f( q ), d_match_pattern( pat ) { +InstMatchGeneratorSimple::InstMatchGeneratorSimple( Node q, Node pat, QuantifiersEngine* qe ) : d_f( q ), d_match_pattern( pat ) { if( d_match_pattern.getKind()==NOT ){ d_match_pattern = d_match_pattern[0]; d_pol = false; @@ -720,10 +737,11 @@ InstMatchGeneratorSimple::InstMatchGeneratorSimple( Node q, Node pat ) : d_f( q } d_match_pattern_arg_types.push_back( d_match_pattern[i].getType() ); } + d_op = qe->getTermDatabase()->getMatchOperator( d_match_pattern ); } void InstMatchGeneratorSimple::resetInstantiationRound( QuantifiersEngine* qe ) { - d_op = qe->getTermDatabase()->getMatchOperator( d_match_pattern ); + } int InstMatchGeneratorSimple::addInstantiations( Node q, InstMatch& baseMatch, QuantifiersEngine* qe ){ @@ -751,6 +769,7 @@ int InstMatchGeneratorSimple::addInstantiations( Node q, InstMatch& baseMatch, Q tat = NULL; } } + Debug("simple-trigger-debug") << "Adding instantiations based on " << tat << " from " << d_op << " " << d_eqc << std::endl; if( tat ){ InstMatch m( q ); m.add( baseMatch ); diff --git a/src/theory/quantifiers/inst_match_generator.h b/src/theory/quantifiers/inst_match_generator.h index a1d907001..096774c51 100644..100755 --- a/src/theory/quantifiers/inst_match_generator.h +++ b/src/theory/quantifiers/inst_match_generator.h @@ -91,7 +91,7 @@ public: InstMatchGenerator( Node pat ); InstMatchGenerator(); /** destructor */ - ~InstMatchGenerator() throw() {} + virtual ~InstMatchGenerator() throw(); /** The pattern we are producing matches for. If null, this is a multi trigger that is merging matches from d_children. */ @@ -125,7 +125,7 @@ public: class VarMatchGeneratorBooleanTerm : public InstMatchGenerator { public: VarMatchGeneratorBooleanTerm( Node var, Node comp ); - ~VarMatchGeneratorBooleanTerm() throw() {} + virtual ~VarMatchGeneratorBooleanTerm() throw() {} Node d_comp; bool d_rm_prev; /** reset instantiation round (call this at beginning of instantiation round) */ @@ -142,7 +142,7 @@ public: class VarMatchGeneratorTermSubs : public InstMatchGenerator { public: VarMatchGeneratorTermSubs( Node var, Node subs ); - ~VarMatchGeneratorTermSubs() throw() {} + virtual ~VarMatchGeneratorTermSubs() throw() {} TNode d_var; TypeNode d_var_type; Node d_subs; @@ -183,6 +183,8 @@ private: int d_matchPolicy; /** children generators */ std::vector< InstMatchGenerator* > d_children; + /** order */ + std::map< unsigned, InstMatchTrie::ImtIndexOrder* > d_imtio; /** inst match tries for each child */ std::vector< InstMatchTrieOrdered > d_children_trie; /** calculate matches */ @@ -191,7 +193,7 @@ public: /** constructors */ InstMatchGeneratorMulti( Node q, std::vector< Node >& pats, QuantifiersEngine* qe ); /** destructor */ - ~InstMatchGeneratorMulti() throw() {} + virtual ~InstMatchGeneratorMulti() throw(); /** reset instantiation round (call this whenever equivalence classes have changed) */ void resetInstantiationRound( QuantifiersEngine* qe ); /** reset, eqc is the equivalence class to search in (any if eqc=null) */ @@ -224,7 +226,7 @@ private: void addInstantiations( InstMatch& m, QuantifiersEngine* qe, int& addedLemmas, int argIndex, quantifiers::TermArgTrie* tat ); public: /** constructors */ - InstMatchGeneratorSimple( Node q, Node pat ); + InstMatchGeneratorSimple( Node q, Node pat, QuantifiersEngine* qe ); /** destructor */ ~InstMatchGeneratorSimple() throw() {} /** reset instantiation round (call this whenever equivalence classes have changed) */ diff --git a/src/theory/quantifiers/inst_propagator.cpp b/src/theory/quantifiers/inst_propagator.cpp index d4be58636..41c9c40c8 100644..100755 --- a/src/theory/quantifiers/inst_propagator.cpp +++ b/src/theory/quantifiers/inst_propagator.cpp @@ -34,6 +34,7 @@ bool EqualityQueryInstProp::reset( Theory::Effort e ) { d_uf.clear(); d_uf_exp.clear(); d_diseq_list.clear(); + d_uf_func_map_trie.clear(); return true; } @@ -103,8 +104,7 @@ TNode EqualityQueryInstProp::getCongruentTerm( Node f, std::vector< TNode >& arg if( !t.isNull() ){ return t; }else{ - //TODO? - return TNode::null(); + return d_uf_func_map_trie[f].existsTerm( args ); } } @@ -118,6 +118,7 @@ Node EqualityQueryInstProp::getRepresentativeExp( Node a, std::vector< Node >& e Node ar = getUfRepresentative( a, exp ); if( !ar.isNull() ){ if( engine_has_a || getEngine()->hasTerm( ar ) ){ + Trace("qip-eq") << "getRepresentativeExp " << a << " returns " << ar << std::endl; Assert( getEngine()->hasTerm( ar ) ); Assert( getEngine()->getRepresentative( ar )==ar ); return ar; @@ -168,6 +169,21 @@ bool EqualityQueryInstProp::areDisequalExp( Node a, Node b, std::vector< Node >& } } +TNode EqualityQueryInstProp::getCongruentTermExp( Node f, std::vector< TNode >& args, std::vector< Node >& exp ) { + TNode t = d_qe->getTermDatabase()->getCongruentTerm( f, args ); + if( !t.isNull() ){ + return t; + }else{ + TNode tt = d_uf_func_map_trie[f].existsTerm( args ); + if( !tt.isNull() ){ + //TODO? + return tt; + }else{ + return tt; + } + } +} + Node EqualityQueryInstProp::getUfRepresentative( Node a, std::vector< Node >& exp ) { Assert( exp.empty() ); std::map< Node, Node >::iterator it = d_uf.find( a ); @@ -252,7 +268,7 @@ int EqualityQueryInstProp::setEqual( Node& a, Node& b, bool pol, std::vector< No } } } - + if( swap ){ //swap Node temp_r = ar; @@ -262,7 +278,7 @@ int EqualityQueryInstProp::setEqual( Node& a, Node& b, bool pol, std::vector< No Assert( !getEngine()->hasTerm( ar ) || getEngine()->hasTerm( br ) ); Assert( ar!=br ); - + std::vector< Node > exp_d; if( areDisequalExp( ar, br, exp_d ) ){ if( pol ){ @@ -279,18 +295,20 @@ int EqualityQueryInstProp::setEqual( Node& a, Node& b, bool pol, std::vector< No Assert( d_uf_exp[ar].empty() ); Assert( d_uf_exp[br].empty() ); + //registerUfTerm( ar ); d_uf[ar] = br; merge_exp( d_uf_exp[ar], exp_a ); merge_exp( d_uf_exp[ar], exp_b ); merge_exp( d_uf_exp[ar], reason ); + //registerUfTerm( br ); d_uf[br] = br; d_uf_exp[br].clear(); Trace("qip-eq") << "EqualityQueryInstProp::setEqual : merge " << ar << " -> " << br << ", exp size = " << d_uf_exp[ar].size() << ", status = " << status << std::endl; a = ar; b = br; - + //carry disequality list std::map< Node, std::map< Node, std::vector< Node > > >::iterator itd = d_diseq_list.find( ar ); if( itd!=d_diseq_list.end() ){ @@ -302,13 +320,13 @@ int EqualityQueryInstProp::setEqual( Node& a, Node& b, bool pol, std::vector< No } } } - + return status; }else{ Trace("qip-eq") << "EqualityQueryInstProp::setEqual : disequal " << ar << " <> " << br << std::endl; Assert( d_diseq_list[ar].find( br )==d_diseq_list[ar].end() ); Assert( d_diseq_list[br].find( ar )==d_diseq_list[br].end() ); - + merge_exp( d_diseq_list[ar][br], reason ); merge_exp( d_diseq_list[br][ar], reason ); return STATUS_NONE; @@ -316,187 +334,234 @@ int EqualityQueryInstProp::setEqual( Node& a, Node& b, bool pol, std::vector< No } } -void EqualityQueryInstProp::addArgument( std::vector< Node >& args, std::vector< Node >& props, Node n, bool is_prop, bool pol ) { - if( is_prop ){ - if( isLiteral( n ) ){ - props.push_back( pol ? n : n.negate() ); - return; +void EqualityQueryInstProp::registerUfTerm( TNode n ) { + if( d_uf.find( n )==d_uf.end() ){ + if( !getEngine()->hasTerm( n ) ){ + TNode f = d_qe->getTermDatabase()->getMatchOperator( n ); + if( !f.isNull() ){ + std::vector< TNode > args; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + if( !getEngine()->hasTerm( n[i] ) ){ + return; + }else{ + args.push_back( n[i] ); + } + } + d_uf_func_map_trie[f].addTerm( n, args ); + } } } +} + +//void EqualityQueryInstProp::addArgument( std::vector< Node >& args, std::vector< Node >& props, Node n, bool is_prop, bool pol ) { +void EqualityQueryInstProp::addArgument( Node n, std::vector< Node >& args, std::vector< Node >& watch, bool is_watch ) { + if( is_watch ){ + watch.push_back( n ); + } args.push_back( n ); } -bool EqualityQueryInstProp::isLiteral( Node n ) { - Kind ak = n.getKind()==NOT ? n[0].getKind() : n.getKind(); - Assert( ak!=NOT ); - return ak!=AND && ak!=OR && ak!=IFF && ak!=ITE; +bool EqualityQueryInstProp::isPropagateLiteral( Node n ) { + if( n==d_true || n==d_false ){ + return false; + }else{ + Kind ak = n.getKind()==NOT ? n[0].getKind() : n.getKind(); + Assert( ak!=NOT ); + return ak!=AND && ak!=OR && ak!=IFF && ak!=ITE; + } +} + +void EqualityQueryInstProp::setWatchList( Node n, std::vector< Node >& watch, std::map< Node, std::vector< Node > >& watch_list_out ) { + if( watch.empty() ){ + watch.push_back( n ); + } + for( unsigned j=0; j<watch.size(); j++ ){ + Trace("qip-eval") << "Watch : " << n << " -> " << watch[j] << std::endl; + watch_list_out[n].push_back( watch[j] ); + } +} + +void EqualityQueryInstProp::collectWatchList( Node n, std::map< Node, std::vector< Node > >& watch_list_out, std::vector< Node >& watch_list ) { + std::map< Node, std::vector< Node > >::iterator it = watch_list_out.find( n ); + if( it!=watch_list_out.end() && std::find( watch_list.begin(), watch_list.end(), n )==watch_list.end() ){ + watch_list.push_back( n ); + for( unsigned j=0; j<it->second.size(); j++ ){ + collectWatchList( it->second[j], watch_list_out, watch_list ); + } + } } -//this is identical to TermDb::evaluateTerm2, but tracks more information -Node EqualityQueryInstProp::evaluateTermExp( Node n, std::vector< Node >& exp, std::map< Node, Node >& visited, bool hasPol, bool pol, - std::map< Node, bool >& watch_list_out, std::vector< Node >& props ) { - std::map< Node, Node >::iterator itv = visited.find( n ); - if( itv != visited.end() ){ +//this is similar to TermDb::evaluateTerm2, but tracks more information +Node EqualityQueryInstProp::evaluateTermExp( Node n, std::vector< Node >& exp, std::map< int, std::map< Node, Node > >& visited, + bool hasPol, bool pol, std::map< Node, std::vector< Node > >& watch_list_out, std::vector< Node >& props ) { + int polIndex = hasPol ? ( pol ? 1 : -1 ) : 0; + std::map< Node, Node >::iterator itv = visited[polIndex].find( n ); + if( itv!=visited[polIndex].end() ){ return itv->second; }else{ - visited[n] = n; - Trace("qip-eval") << "evaluate term : " << n << std::endl; - std::vector< Node > exp_n; - Node ret = getRepresentativeExp( n, exp_n ); - if( ret.isNull() ){ - //term is not known to be equal to a representative in equality engine, evaluate it - Kind k = n.getKind(); - if( k==FORALL ){ - ret = Node::null(); - }else{ - std::map< Node, bool > watch_list_out_curr; - TNode f = d_qe->getTermDatabase()->getMatchOperator( n ); - std::vector< Node > args; - bool ret_set = false; - bool childChanged = false; - int abort_i = -1; - //get the child entailed polarity - Assert( n.getKind()!=IMPLIES ); - bool newHasPol, newPol; - QuantPhaseReq::getEntailPolarity( n, 0, hasPol, pol, newHasPol, newPol ); - //for each child - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - Node c = evaluateTermExp( n[i], exp, visited, newHasPol, newPol, watch_list_out_curr, props ); - if( c.isNull() ){ - ret = Node::null(); - ret_set = true; - break; - }else if( c==d_true || c==d_false ){ - //short-circuiting - if( k==kind::AND || k==kind::OR ){ - if( (k==kind::AND)==(c==d_false) ){ - ret = c; - ret_set = true; - break; - }else{ - //redundant - c = Node::null(); - childChanged = true; - } - }else if( k==kind::ITE && i==0 ){ - Assert( watch_list_out_curr.empty() ); - ret = evaluateTermExp( n[ c==d_true ? 1 : 2], exp, visited, hasPol, pol, watch_list_out_curr, props ); - ret_set = true; - break; - }else if( k==kind::NOT ){ - ret = c==d_true ? d_false : d_true; + visited[polIndex][n] = n; + Node ret; + //check if it should be propagated in this context + if( hasPol && isPropagateLiteral( n ) ){ + Assert( n.getType().isBoolean() ); + //must be Boolean + ret = evaluateTermExp( n, exp, visited, false, pol, watch_list_out, props ); + if( isPropagateLiteral( ret ) ){ + Trace("qip-eval") << "-----> propagate : " << ret << std::endl; + props.push_back( pol ? ret : ret.negate() ); + ret = pol ? d_true : d_false; + } + }else{ + Trace("qip-eval") << "evaluate term : " << n << " [" << polIndex << "]" << std::endl; + std::vector< Node > exp_n; + ret = getRepresentativeExp( n, exp_n ); + if( ret.isNull() ){ + //term is not known to be equal to a representative in equality engine, evaluate it + Kind k = n.getKind(); + if( k!=FORALL ){ + TNode f = d_qe->getTermDatabase()->getMatchOperator( n ); + std::vector< Node > args; + bool ret_set = false; + bool childChanged = false; + int abort_i = -1; + //get the child entailed polarity + Assert( n.getKind()!=IMPLIES ); + bool newHasPol, newPol; + QuantPhaseReq::getEntailPolarity( n, 0, hasPol, pol, newHasPol, newPol ); + std::vector< Node > watch; + //for each child + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + Node c = evaluateTermExp( n[i], exp, visited, newHasPol, newPol, watch_list_out, props ); + if( c.isNull() ){ + ret = Node::null(); ret_set = true; break; - } - } - if( !c.isNull() ){ - childChanged = childChanged || n[i]!=c; - if( !f.isNull() && !watch_list_out_curr.empty() ){ - // we are done if this is an UF application and an argument is unevaluated - args.push_back( c ); - abort_i = i; - break; - }else if( ( k==kind::AND || k==kind::OR ) ){ - if( c.getKind()==k ){ - //flatten - for( unsigned j=0; j<c.getNumChildren(); j++ ){ - addArgument( args, props, c[j], newHasPol, newPol ); + }else if( c==d_true || c==d_false ){ + //short-circuiting + if( k==kind::AND || k==kind::OR ){ + if( (k==kind::AND)==(c==d_false) ){ + ret = c; + ret_set = true; + break; + }else{ + //redundant + c = Node::null(); + childChanged = true; } - }else{ - addArgument( args, props, c, newHasPol, newPol ); + }else if( k==kind::ITE && i==0 ){ + ret = evaluateTermExp( n[ c==d_true ? 1 : 2], exp, visited, hasPol, pol, watch_list_out, props ); + ret_set = true; + break; + }else if( k==kind::NOT ){ + ret = c==d_true ? d_false : d_true; + ret_set = true; + break; } - //if we are in a branching position - if( hasPol && !newHasPol && args.size()>=2 ){ - //we are done if at least two args are unevaluated + } + if( !c.isNull() ){ + childChanged = childChanged || n[i]!=c; + bool is_watch = watch_list_out.find( c )!=watch_list_out.end(); + if( !f.isNull() && is_watch ){ + // we are done if this is an UF application and an argument is unevaluated + addArgument( c, args, watch, is_watch ); abort_i = i; break; + }else if( k==kind::AND || k==kind::OR || k==kind::ITE || k==IFF ){ + Trace("qip-eval-debug") << "Adding argument " << c << " to " << k << ", isProp = " << newHasPol << std::endl; + if( ( k==kind::AND || k==kind::OR ) && c.getKind()==k ){ + //flatten + for( unsigned j=0; j<c.getNumChildren(); j++ ){ + addArgument( c[j], args, watch, is_watch ); + } + }else{ + addArgument( c, args, watch, is_watch ); + } + Trace("qip-eval-debug") << "props/args = " << props.size() << "/" << args.size() << std::endl; + //if we are in a branching position + if( hasPol && !newHasPol && args.size()>=2 ){ + //we are done if at least two args are unevaluated + abort_i = i; + break; + } + }else{ + addArgument( c, args, watch, is_watch ); } - }else if( k==kind::ITE ){ - //we are done if we are ITE and condition is unevaluated - Assert( i==0 ); - args.push_back( c ); - abort_i = i; - break; - }else{ - args.push_back( c ); } } - } - //add remaining children if we aborted - if( abort_i!=-1 ){ - for( int i=(abort_i+1); i<(int)n.getNumChildren(); i++ ){ - args.push_back( n[i] ); - } - } - //copy over the watch list - for( std::map< Node, bool >::iterator itc = watch_list_out_curr.begin(); itc != watch_list_out_curr.end(); ++itc ){ - watch_list_out[itc->first] = itc->second; - } - - //if we have not short-circuited evaluation - if( !ret_set ){ - //if it is an indexed term, return the congruent term - if( !f.isNull() && watch_list_out.empty() ){ - std::vector< TNode > t_args; - for( unsigned i=0; i<args.size(); i++ ) { - t_args.push_back( args[i] ); - } - Assert( args.size()==n.getNumChildren() ); - //args contains terms known by the equality engine - TNode nn = getCongruentTerm( f, t_args ); - Trace("qip-eval") << " got congruent term " << nn << " from DB for " << n << std::endl; - if( !nn.isNull() ){ - //successfully constructed representative in EE - Assert( exp_n.empty() ); - ret = getRepresentativeExp( nn, exp_n ); - Trace("qip-eval") << "return rep, exp size = " << exp_n.size() << std::endl; - merge_exp( exp, exp_n ); - ret_set = true; - Assert( !ret.isNull() ); + //add remaining children if we aborted + if( abort_i!=-1 ){ + Trace("qip-eval-debug") << "..." << n << " aborted at " << abort_i << std::endl; + for( int i=(abort_i+1); i<(int)n.getNumChildren(); i++ ){ + args.push_back( n[i] ); } } + //if we have not short-circuited evaluation if( !ret_set ){ - if( childChanged ){ - Trace("qip-eval") << "return rewrite" << std::endl; - if( ( k==kind::AND || k==kind::OR ) ){ - if( args.empty() ){ - ret = k==kind::AND ? d_true : d_false; - ret_set = true; - }else if( args.size()==1 ){ - ret = args[0]; - ret_set = true; - } + //if it is an indexed term, return the congruent term + if( !f.isNull() && watch.empty() ){ + std::vector< TNode > t_args; + for( unsigned i=0; i<args.size(); i++ ) { + Trace("qip-eval") << "arg " << i << " : " << args[i] << std::endl; + t_args.push_back( args[i] ); + } + Assert( args.size()==n.getNumChildren() ); + //args contains terms known by the equality engine + TNode nn = getCongruentTerm( f, t_args ); + Trace("qip-eval") << " got congruent term " << nn << " for " << n << std::endl; + if( !nn.isNull() ){ + //successfully constructed representative in EE + Assert( exp_n.empty() ); + ret = getRepresentativeExp( nn, exp_n ); + Trace("qip-eval") << "return rep, exp size = " << exp_n.size() << std::endl; + merge_exp( exp, exp_n ); + ret_set = true; + Assert( !ret.isNull() ); + Assert( ret!=n ); + // we have that n == ret, check if the union find should be updated TODO? }else{ - Assert( args.size()==n.getNumChildren() ); + watch.push_back( ret ); } - if( !ret_set ){ - if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ - args.insert( args.begin(), n.getOperator() ); + } + if( !ret_set ){ + if( childChanged || args.size()!=n.getNumChildren() ){ + Trace("qip-eval") << "return rewrite" << std::endl; + if( k==kind::AND || k==kind::OR ){ + if( args.empty() ){ + ret = k==kind::AND ? d_true : d_false; + ret_set = true; + }else if( args.size()==1 ){ + //need to re-evaluate (may be new propagations) + ret = evaluateTermExp( args[0], exp, visited, hasPol, pol, watch_list_out, props ); + ret_set = true; + } + }else{ + Assert( args.size()==n.getNumChildren() ); } - ret = NodeManager::currentNM()->mkNode( k, args ); - ret = Rewriter::rewrite( ret ); - //re-evaluate - Node ret_eval = getRepresentativeExp( ret, exp_n ); - if( !ret_eval.isNull() ){ - ret = ret_eval; - watch_list_out.clear(); - }else{ - watch_list_out[ret] = true; + if( !ret_set ){ + if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ + args.insert( args.begin(), n.getOperator() ); + } + ret = NodeManager::currentNM()->mkNode( k, args ); + setWatchList( ret, watch, watch_list_out ); + ret = Rewriter::rewrite( ret ); + //need to re-evaluate + ret = evaluateTermExp( ret, exp, visited, hasPol, pol, watch_list_out, props ); } + }else{ + ret = n; + setWatchList( ret, watch, watch_list_out ); } - }else{ - ret = n; - watch_list_out[ret] = true; } } } + }else{ + Trace("qip-eval") << "...exists in ee, return rep, exp size = " << exp_n.size() << std::endl; + merge_exp( exp, exp_n ); } - }else{ - Trace("qip-eval") << "...exists in ee, return rep, exp size = " << exp_n.size() << std::endl; - merge_exp( exp, exp_n ); } - Trace("qip-eval") << "evaluated term : " << n << ", got : " << ret << ", exp size = " << exp.size() << std::endl; - visited[n] = ret; + + Trace("qip-eval") << "evaluated term : " << n << " [" << polIndex << "], got : " << ret << ", exp size = " << exp.size() << ", watch list size = " << watch_list_out.size() << std::endl; + visited[polIndex][n] = ret; return ret; } } @@ -545,6 +610,7 @@ bool InstPropagator::reset( Theory::Effort e ) { d_watch_list.clear(); d_update_list.clear(); d_relevant_inst.clear(); + d_has_relevant_inst = false; return d_qy.reset( e ); } @@ -556,10 +622,7 @@ bool InstPropagator::notifyInstantiation( unsigned quant_e, Node q, Node lem, st Trace("qip-prop") << " " << terms[i] << std::endl; } } - unsigned id = d_icount; - d_icount++; - Trace("qip-prop") << "...assign id=" << id << std::endl; - d_ii[id].init( q, lem, terms, body ); + unsigned id = allocateInstantiation( q, lem, terms, body ); //initialize the information if( cacheConclusion( id, body ) ){ Assert( d_update_list.empty() ); @@ -582,35 +645,67 @@ bool InstPropagator::notifyInstantiation( unsigned quant_e, Node q, Node lem, st return !d_conflict; }else{ Assert( false ); - return true; + return false; + } +} + +void InstPropagator::filterInstantiations() { + if( d_has_relevant_inst ){ + //now, inform quantifiers engine which instances should be retracted + Trace("qip-prop-debug") << "...remove instantiation ids : "; + for( std::map< unsigned, InstInfo >::iterator it = d_ii.begin(); it != d_ii.end(); ++it ){ + if( !it->second.d_q.isNull() ){ + if( d_relevant_inst.find( it->first )==d_relevant_inst.end() ){ + if( !d_qe->removeInstantiation( it->second.d_q, it->second.d_lem, it->second.d_terms ) ){ + Trace("qip-warn") << "WARNING : did not remove instantiation id " << it->first << std::endl; + Assert( false ); + }else{ + Trace("qip-prop-debug") << it->first << " "; + } + }else{ + //mark the quantified formula as relevant + d_qe->markRelevant( it->second.d_q ); + } + } + } + Trace("qip-prop-debug") << std::endl; + Trace("quant-engine-conflict") << "-----> InstPropagator::" << ( d_conflict ? "conflict" : "propagate" ) << " with " << d_relevant_inst.size() << " instances." << std::endl; } } +unsigned InstPropagator::allocateInstantiation( Node q, Node lem, std::vector< Node >& terms, Node body ) { + unsigned id = d_icount; + d_icount++; + Trace("qip-prop") << "...assign id=" << id << std::endl; + d_ii[id].init( q, lem, terms, body ); + return id; +} + bool InstPropagator::update( unsigned id, InstInfo& ii, bool firstTime ) { Assert( !d_conflict ); Assert( ii.d_active ); Trace("qip-prop-debug") << "Update info [" << id << "]..." << std::endl; //update the evaluation of the current lemma - std::map< Node, Node > visited; - std::map< Node, bool > watch_list; + std::map< Node, std::vector< Node > > watch_list_out; + std::map< int, std::map< Node, Node > > visited; + std::vector< Node > exp; std::vector< Node > props; - Node eval = d_qy.evaluateTermExp( ii.d_curr, ii.d_curr_exp, visited, true, true, watch_list, props ); + Node eval = d_qy.evaluateTermExp( ii.d_curr, exp, visited, true, true, watch_list_out, props ); + EqualityQueryInstProp::merge_exp( ii.d_curr_exp, exp ); if( eval.isNull() ){ ii.d_active = false; }else if( firstTime || eval!=ii.d_curr ){ - if( EqualityQueryInstProp::isLiteral( eval ) ){ - props.push_back( eval ); - eval = d_qy.d_true; - watch_list.clear(); - } + std::vector< Node > watch_list; + d_qy.collectWatchList( eval, watch_list_out, watch_list ); if( Trace.isOn("qip-prop") ){ Trace("qip-prop") << "Update info [" << id << "]..." << std::endl; - Trace("qip-prop") << "...updated lemma " << ii.d_curr << " -> " << eval << ", exp = "; + Trace("qip-prop") << "...updated lemma " << ii.d_curr << " -> " << eval << std::endl; + Trace("qip-prop") << "...explanation = "; debugPrintExplanation( ii.d_curr_exp, "qip-prop" ); Trace("qip-prop") << std::endl; Trace("qip-prop") << "...watch list: " << std::endl; - for( std::map< Node, bool >::iterator itw = watch_list.begin(); itw!=watch_list.end(); ++itw ){ - Trace("qip-prop") << " " << itw->first << std::endl; + for( unsigned i=0; i<watch_list.size(); i++ ){ + Trace("qip-prop") << " " << watch_list[i] << std::endl; } Trace("qip-prop") << "...new propagations: " << std::endl; for( unsigned i=0; i<props.size(); i++ ){ @@ -627,8 +722,13 @@ bool InstPropagator::update( unsigned id, InstInfo& ii, bool firstTime ) { }else{ for( unsigned i=0; i<props.size(); i++ ){ Trace("qip-prop-debug2") << "Process propagation " << props[i] << std::endl; + Assert( d_qy.isPropagateLiteral( props[i] ) ); //if we haven't propagated this literal yet if( cacheConclusion( id, props[i], 1 ) ){ + //watch list for propagated literal: may not yet be purely EE representatives + std::vector< Node > prop_watch_list; + d_qy.collectWatchList( props[i], watch_list_out, prop_watch_list ); + Node lit = props[i].getKind()==NOT ? props[i][0] : props[i]; bool pol = props[i].getKind()!=NOT; if( lit.getKind()==EQUAL ){ @@ -647,10 +747,10 @@ bool InstPropagator::update( unsigned id, InstInfo& ii, bool firstTime ) { ii.d_curr = eval; //update the watch list Trace("qip-prop-debug") << "...updating watch list for [" << id << "], curr is " << ii.d_curr << std::endl; - //Here, we need to be notified of enough terms such that if we are not notified, then update( ii ) will return no propagations. - // Similar to two-watched literals, but since we are in UF, we need to watch all terms on a complete path of two terms. - for( std::map< Node, bool >::iterator itw = watch_list.begin(); itw != watch_list.end(); ++itw ){ - d_watch_list[ itw->first ][ id ] = true; + //Here, we need to be notified of enough terms such that if we are not notified, then update( id, ii ) will return no propagations. + // Similar to two-watched literals, but since we are taking into account UF, we need to watch all terms on a complete path of two terms. + for( unsigned i=0; i<watch_list.size(); i++ ){ + d_watch_list[ watch_list[i] ][ id ] = true; } }else{ Trace("qip-prop-debug") << "...conclusion " << eval << " is duplicate." << std::endl; @@ -682,10 +782,12 @@ void InstPropagator::propagate( Node a, Node b, bool pol, std::vector< Node >& e } if( pol ){ if( status==EqualityQueryInstProp::STATUS_MERGED_KNOWN ){ + Trace("qip-rlv-propagate") << "Relevant propagation : " << a << ( pol ? " == " : " != " ) << b << std::endl; Assert( d_qy.getEngine()->hasTerm( a ) ); Assert( d_qy.getEngine()->hasTerm( b ) ); Trace("qip-prop-debug") << "...equality between known terms." << std::endl; addRelevantInstances( exp, "qip-propagate" ); + //d_has_relevant_inst = true; } Trace("qip-prop-debug") << "...merged representatives " << a << " and " << b << std::endl; for( unsigned i=0; i<2; i++ ){ @@ -712,25 +814,7 @@ void InstPropagator::conflict( std::vector< Node >& exp ) { d_conflict = true; d_relevant_inst.clear(); addRelevantInstances( exp, "qip-propagate" ); - - //now, inform quantifiers engine which instances should be retracted - Trace("qip-prop-debug") << "...remove instantiation ids : "; - for( std::map< unsigned, InstInfo >::iterator it = d_ii.begin(); it != d_ii.end(); ++it ){ - if( d_relevant_inst.find( it->first )==d_relevant_inst.end() ){ - if( !d_qe->removeInstantiation( it->second.d_q, it->second.d_lem, it->second.d_terms ) ){ - Trace("qip-warn") << "WARNING : did not remove instantiation id " << it->first << std::endl; - Assert( false ); - }else{ - Trace("qip-prop-debug") << it->first << " "; - } - }else{ - //mark the quantified formula as relevant - d_qe->markRelevant( it->second.d_q ); - } - } - Trace("qip-prop-debug") << std::endl; - //will interupt the quantifiers engine - Trace("quant-engine-conflict") << "-----> InstPropagator::conflict with " << exp.size() << " instances." << std::endl; + d_has_relevant_inst = true; } bool InstPropagator::cacheConclusion( unsigned id, Node body, int prop_index ) { diff --git a/src/theory/quantifiers/inst_propagator.h b/src/theory/quantifiers/inst_propagator.h index 0c02c7f95..6201cf152 100644..100755 --- a/src/theory/quantifiers/inst_propagator.h +++ b/src/theory/quantifiers/inst_propagator.h @@ -64,9 +64,11 @@ public: bool areEqualExp( Node a, Node b, std::vector< Node >& exp ); /** returns true is a and b are disequal in the current context */ bool areDisequalExp( Node a, Node b, std::vector< Node >& exp ); + /** get congruent term */ + TNode getCongruentTermExp( Node f, std::vector< TNode >& args, std::vector< Node >& exp ); private: /** term index */ - std::map< Node, TermArgTrie > d_func_map_trie; + std::map< Node, TermArgTrie > d_uf_func_map_trie; /** union find for terms beyond what is stored in equality engine */ std::map< Node, Node > d_uf; std::map< Node, std::vector< Node > > d_uf_exp; @@ -74,7 +76,9 @@ private: /** disequality list, stores explanations */ std::map< Node, std::map< Node, std::vector< Node > > > d_diseq_list; /** add arg */ - void addArgument( std::vector< Node >& args, std::vector< Node >& props, Node n, bool is_prop, bool pol ); + void addArgument( Node n, std::vector< Node >& args, std::vector< Node >& watch, bool is_watch ); + /** register term */ + void registerUfTerm( TNode n ); public: enum { STATUS_CONFLICT, @@ -89,10 +93,13 @@ public: public: //for explanations static void merge_exp( std::vector< Node >& v, std::vector< Node >& v_to_merge, int up_to_size = -1 ); + //for watch list + static void setWatchList( Node n, std::vector< Node >& watch, std::map< Node, std::vector< Node > >& watch_list_out ); + static void collectWatchList( Node n, std::map< Node, std::vector< Node > >& watch_list_out, std::vector< Node >& watch_list ); - Node evaluateTermExp( Node n, std::vector< Node >& exp, std::map< Node, Node >& visited, bool hasPol, bool pol, - std::map< Node, bool >& watch_list_out, std::vector< Node >& props ); - static bool isLiteral( Node n ); + Node evaluateTermExp( Node n, std::vector< Node >& exp, std::map< int, std::map< Node, Node > >& visited, + bool hasPol, bool pol, std::map< Node, std::vector< Node > >& watch_list_out, std::vector< Node >& props ); + bool isPropagateLiteral( Node n ); }; class InstPropagator : public QuantifiersUtil { @@ -104,13 +111,18 @@ private: InstPropagator& d_ip; public: InstantiationNotifyInstPropagator(InstPropagator& ip): d_ip(ip) {} - virtual bool notifyInstantiation( unsigned quant_e, Node q, Node lem, std::vector< Node >& terms, Node body ) { - return d_ip.notifyInstantiation( quant_e, q, lem, terms, body ); + virtual bool notifyInstantiation( unsigned quant_e, Node q, Node lem, std::vector< Node >& terms, Node body ) { + return d_ip.notifyInstantiation( quant_e, q, lem, terms, body ); } + virtual void filterInstantiations() { d_ip.filterInstantiations(); } }; InstantiationNotifyInstPropagator d_notify; /** notify instantiation method */ bool notifyInstantiation( unsigned quant_e, Node q, Node lem, std::vector< Node >& terms, Node body ); + /** remove instance ids */ + void filterInstantiations(); + /** allocate instantiation */ + unsigned allocateInstantiation( Node q, Node lem, std::vector< Node >& terms, Node body ); /** equality query */ EqualityQueryInstProp d_qy; class InstInfo { @@ -137,13 +149,13 @@ private: std::vector< unsigned > d_update_list; /** relevant instances */ std::map< unsigned, bool > d_relevant_inst; + bool d_has_relevant_inst; private: bool update( unsigned id, InstInfo& i, bool firstTime = false ); void propagate( Node a, Node b, bool pol, std::vector< Node >& exp ); void conflict( std::vector< Node >& exp ); bool cacheConclusion( unsigned id, Node body, int prop_index = 0 ); void addRelevantInstances( std::vector< Node >& exp, const char * c ); - void debugPrintExplanation( std::vector< Node >& exp, const char * c ); public: InstPropagator( QuantifiersEngine* qe ); diff --git a/src/theory/quantifiers/inst_strategy_cbqi.cpp b/src/theory/quantifiers/inst_strategy_cbqi.cpp index 149330c61..523d868b5 100644..100755 --- a/src/theory/quantifiers/inst_strategy_cbqi.cpp +++ b/src/theory/quantifiers/inst_strategy_cbqi.cpp @@ -190,7 +190,7 @@ bool InstStrategyCbqi::hasNonCbqiOperator( Node n, std::map< Node, bool >& visit bool InstStrategyCbqi::hasNonCbqiVariable( Node q ){ for( unsigned i=0; i<q[0].getNumChildren(); i++ ){ TypeNode tn = q[0][i].getType(); - if( !tn.isInteger() && !tn.isReal() && !tn.isBoolean() ){ + if( !tn.isInteger() && !tn.isReal() && !tn.isBoolean() && !tn.isBitVector() ){ if( options::cbqiSplx() ){ return true; }else{ @@ -242,7 +242,7 @@ Node InstStrategyCbqi::getNextDecisionRequest(){ Node cel = d_quantEngine->getTermDatabase()->getCounterexampleLiteral( q ); bool value; if( !d_quantEngine->getValuation().hasSatValue( cel, value ) ){ - Trace("cbqi-debug2") << "CBQI: get next decision " << cel << std::endl; + Trace("cbqi-dec") << "CBQI: get next decision " << cel << std::endl; return cel; } } @@ -692,8 +692,10 @@ CegInstantiator * InstStrategyCegqi::getInstantiator( Node q ) { void InstStrategyCegqi::registerQuantifier( Node q ) { if( options::cbqiPreRegInst() ){ - //just get the instantiator - getInstantiator( q ); + if( doCbqi( q ) ){ + //just get the instantiator + getInstantiator( q ); + } } } diff --git a/src/theory/quantifiers/inst_strategy_cbqi.h b/src/theory/quantifiers/inst_strategy_cbqi.h index 8ed59778b..8ed59778b 100644..100755 --- a/src/theory/quantifiers/inst_strategy_cbqi.h +++ b/src/theory/quantifiers/inst_strategy_cbqi.h diff --git a/src/theory/quantifiers/inst_strategy_e_matching.cpp b/src/theory/quantifiers/inst_strategy_e_matching.cpp index 630880690..efd765c86 100644..100755 --- a/src/theory/quantifiers/inst_strategy_e_matching.cpp +++ b/src/theory/quantifiers/inst_strategy_e_matching.cpp @@ -34,9 +34,10 @@ using namespace CVC4::theory::quantifiers; struct sortQuantifiersForSymbol { QuantifiersEngine* d_qe; + std::map< Node, Node > d_op_map; bool operator() (Node i, Node j) { - int nqfsi = d_qe->getQuantifierRelevance()->getNumQuantifiersForSymbol( i.getOperator() ); - int nqfsj = d_qe->getQuantifierRelevance()->getNumQuantifiersForSymbol( j.getOperator() ); + int nqfsi = d_qe->getQuantifierRelevance()->getNumQuantifiersForSymbol( d_op_map[i] ); + int nqfsj = d_qe->getQuantifierRelevance()->getNumQuantifiersForSymbol( d_op_map[j] ); if( nqfsi<nqfsj ){ return true; }else if( nqfsi>nqfsj ){ @@ -83,9 +84,8 @@ int InstStrategyUserPatterns::process( Node f, Theory::Effort effort, int e ){ Trace("inst-alg") << "-> User-provided instantiate " << f << "..." << std::endl; if( d_quantEngine->getInstUserPatMode()==USER_PAT_MODE_RESORT ){ - int matchOption = 0; for( unsigned i=0; i<d_user_gen_wait[f].size(); i++ ){ - Trigger * t = Trigger::mkTrigger( d_quantEngine, f, d_user_gen_wait[f][i], matchOption, true, Trigger::TR_RETURN_NULL ); + Trigger * t = Trigger::mkTrigger( d_quantEngine, f, d_user_gen_wait[f][i], true, Trigger::TR_RETURN_NULL ); if( t ){ d_user_gen[f].push_back( t ); } @@ -134,11 +134,10 @@ void InstStrategyUserPatterns::addUserPattern( Node q, Node pat ){ if( usable ){ Trace("user-pat") << "Add user pattern: " << pat << " for " << q << std::endl; //check match option - int matchOption = 0; if( d_quantEngine->getInstUserPatMode()==USER_PAT_MODE_RESORT ){ d_user_gen_wait[q].push_back( nodes ); }else{ - Trigger * t = Trigger::mkTrigger( d_quantEngine, q, nodes, matchOption, true, Trigger::TR_MAKE_NEW ); + Trigger * t = Trigger::mkTrigger( d_quantEngine, q, nodes, true, Trigger::TR_MAKE_NEW ); if( t ){ d_user_gen[q].push_back( t ); }else{ @@ -279,8 +278,8 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ Trace("auto-gen-trigger-debug") << "Collected pat terms for " << bd << ", no-patterns : " << d_user_no_gen[f].size() << std::endl; for( unsigned i=0; i<patTermsF.size(); i++ ){ Assert( tinfo.find( patTermsF[i] )!=tinfo.end() ); - Trace("auto-gen-trigger-debug") << " " << patTermsF[i]; - Trace("auto-gen-trigger-debug") << " info[" << tinfo[patTermsF[i]].d_reqPol << ", " << tinfo[patTermsF[i]].d_reqPolEq << ", " << tinfo[patTermsF[i]].d_fv.size() << "]" << std::endl; + Trace("auto-gen-trigger-debug") << " " << patTermsF[i] << std::endl; + Trace("auto-gen-trigger-debug2") << " info = [" << tinfo[patTermsF[i]].d_reqPol << ", " << tinfo[patTermsF[i]].d_reqPolEq << ", " << tinfo[patTermsF[i]].d_fv.size() << "]" << std::endl; } Trace("auto-gen-trigger-debug") << std::endl; } @@ -306,10 +305,28 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ last_weight = curr_w; } } + d_num_trigger_vars[f] = vcMap.size(); + if( d_num_trigger_vars[f]>0 && d_num_trigger_vars[f]<f[0].getNumChildren() ){ + Trace("auto-gen-trigger-partial") << "Quantified formula : " << f << std::endl; + Trace("auto-gen-trigger-partial") << "...does not contain all variables in triggers!!!" << std::endl; + if( options::partialTriggers() ){ + std::vector< Node > vcs[2]; + for( unsigned i=0; i<f[0].getNumChildren(); i++ ){ + Node ic = d_quantEngine->getTermDatabase()->getInstantiationConstant( f, i ); + vcs[ vcMap.find( ic )==vcMap.end() ? 0 : 1 ].push_back( f[0][i] ); + } + for( unsigned i=0; i<2; i++ ){ + d_vc_partition[i][f] = NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, vcs[i] ); + } + }else{ + return; + } + } for( unsigned i=0; i<patTermsF.size(); i++ ){ Node pat = patTermsF[i]; if( rmPatTermsF.find( pat )==rmPatTermsF.end() ){ Trace("auto-gen-trigger-debug") << "...processing pattern " << pat << std::endl; + Node mpat = pat; //process the pattern: if it has a required polarity, consider it Assert( tinfo.find( pat )!=tinfo.end() ); int rpol = tinfo[pat].d_reqPol; @@ -317,19 +334,30 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ unsigned num_fv = tinfo[pat].d_fv.size(); Trace("auto-gen-trigger-debug") << "...required polarity for " << pat << " is " << rpol << ", eq=" << rpoleq << std::endl; if( rpol!=0 ){ + Assert( rpol==1 || rpol==-1 ); if( Trigger::isRelationalTrigger( pat ) ){ pat = rpol==-1 ? pat.negate() : pat; }else{ Assert( Trigger::isAtomicTrigger( pat ) ); if( pat.getType().isBoolean() && rpoleq.isNull() ){ - pat = NodeManager::currentNM()->mkNode( IFF, pat, NodeManager::currentNM()->mkConst( rpol==-1 ) ).negate(); + if( options::literalMatchMode()==LITERAL_MATCH_USE ){ + pat = NodeManager::currentNM()->mkNode( IFF, pat, NodeManager::currentNM()->mkConst( rpol==-1 ) ).negate(); + }else if( options::literalMatchMode()!=LITERAL_MATCH_NONE ){ + pat = NodeManager::currentNM()->mkNode( IFF, pat, NodeManager::currentNM()->mkConst( rpol==1 ) ); + } }else{ Assert( !rpoleq.isNull() ); if( rpol==-1 ){ - //all equivalence classes except rpoleq - pat = NodeManager::currentNM()->mkNode( EQUAL, pat, rpoleq ).negate(); + if( options::literalMatchMode()!=LITERAL_MATCH_NONE ){ + //all equivalence classes except rpoleq + pat = NodeManager::currentNM()->mkNode( EQUAL, pat, rpoleq ).negate(); + } }else if( rpol==1 ){ - //all equivalence classes that are not disequal to rpoleq TODO + if( options::literalMatchMode()==LITERAL_MATCH_AGG ){ + //only equivalence class rpoleq + pat = NodeManager::currentNM()->mkNode( EQUAL, pat, rpoleq ); + } + //all equivalence classes that are not disequal to rpoleq TODO? } } } @@ -337,10 +365,10 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ }else{ if( Trigger::isRelationalTrigger( pat ) ){ //consider both polarities - addPatternToPool( f, pat.negate(), num_fv ); + addPatternToPool( f, pat.negate(), num_fv, mpat ); } } - addPatternToPool( f, pat, num_fv ); + addPatternToPool( f, pat, num_fv, mpat ); } } //tinfo not used below this point @@ -372,19 +400,23 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ if( options::relevantTriggers() ){ sortQuantifiersForSymbol sqfs; sqfs.d_qe = d_quantEngine; + for( unsigned i=0; i<patTerms.size(); i++ ){ + Assert( d_pat_to_mpat.find( patTerms[i] )!=d_pat_to_mpat.end() ); + Assert( d_pat_to_mpat[patTerms[i]].hasOperator() ); + sqfs.d_op_map[ patTerms[i] ] = d_pat_to_mpat[patTerms[i]].getOperator(); + } //sort based on # occurrences (this will cause Trigger to select rarer symbols) std::sort( patTerms.begin(), patTerms.end(), sqfs ); Debug("relevant-trigger") << "Terms based on relevance: " << std::endl; for( unsigned i=0; i<patTerms.size(); i++ ){ - Debug("relevant-trigger") << " " << patTerms[i] << " ("; - Debug("relevant-trigger") << d_quantEngine->getQuantifierRelevance()->getNumQuantifiersForSymbol( patTerms[i].getOperator() ) << ")" << std::endl; + Debug("relevant-trigger") << " " << patTerms[i] << " from " << d_pat_to_mpat[patTerms[i]] << " ("; + Debug("relevant-trigger") << d_quantEngine->getQuantifierRelevance()->getNumQuantifiersForSymbol( d_pat_to_mpat[patTerms[i]].getOperator() ) << ")" << std::endl; } } //now, generate the trigger... - int matchOption = 0; Trigger* tr = NULL; if( d_is_single_trigger[ patTerms[0] ] ){ - tr = Trigger::mkTrigger( d_quantEngine, f, patTerms[0], matchOption, false, Trigger::TR_RETURN_NULL ); + tr = Trigger::mkTrigger( d_quantEngine, f, patTerms[0], false, Trigger::TR_RETURN_NULL, d_num_trigger_vars[f] ); d_single_trigger_gen[ patTerms[0] ] = true; }else{ //only generate multi trigger if option set, or if no single triggers exist @@ -402,29 +434,14 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ d_made_multi_trigger[f] = true; } //will possibly want to get an old trigger - tr = Trigger::mkTrigger( d_quantEngine, f, patTerms, matchOption, false, Trigger::TR_GET_OLD ); + tr = Trigger::mkTrigger( d_quantEngine, f, patTerms, false, Trigger::TR_GET_OLD, d_num_trigger_vars[f] ); } if( tr ){ - unsigned tindex; - if( tr->isMultiTrigger() ){ - //disable all other multi triggers - for( std::map< Trigger*, bool >::iterator it = d_auto_gen_trigger[1][f].begin(); it != d_auto_gen_trigger[1][f].end(); ++it ){ - d_auto_gen_trigger[1][f][ it->first ] = false; - } - tindex = 1; - }else{ - tindex = 0; - } - //making it during an instantiation round, so must reset - if( d_auto_gen_trigger[tindex][f].find( tr )==d_auto_gen_trigger[tindex][f].end() ){ - tr->resetInstantiationRound(); - tr->reset( Node::null() ); - } - d_auto_gen_trigger[tindex][f][tr] = true; + addTrigger( tr, f ); //if we are generating additional triggers... - if( tindex==0 ){ - int index = 0; - if( index<(int)patTerms.size() ){ + if( !tr->isMultiTrigger() ){ + unsigned index = 0; + if( index<patTerms.size() ){ //Notice() << "check add additional" << std::endl; //check if similar patterns exist, and if so, add them additionally int nqfs_curr = 0; @@ -433,18 +450,13 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ } index++; bool success = true; - while( success && index<(int)patTerms.size() && d_is_single_trigger[ patTerms[index] ] ){ + while( success && index<patTerms.size() && d_is_single_trigger[ patTerms[index] ] ){ success = false; if( !options::relevantTriggers() || d_quantEngine->getQuantifierRelevance()->getNumQuantifiersForSymbol( patTerms[index].getOperator() )<=nqfs_curr ){ d_single_trigger_gen[ patTerms[index] ] = true; - Trigger* tr2 = Trigger::mkTrigger( d_quantEngine, f, patTerms[index], matchOption, false, Trigger::TR_RETURN_NULL ); - if( tr2 ){ - //Notice() << "Add additional trigger " << patTerms[index] << std::endl; - tr2->resetInstantiationRound(); - tr2->reset( Node::null() ); - d_auto_gen_trigger[0][f][tr2] = true; - } + Trigger* tr2 = Trigger::mkTrigger( d_quantEngine, f, patTerms[index], false, Trigger::TR_RETURN_NULL, d_num_trigger_vars[f] ); + addTrigger( tr2, f ); success = true; } index++; @@ -457,8 +469,10 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ } } -void InstStrategyAutoGenTriggers::addPatternToPool( Node q, Node pat, unsigned num_fv ) { - if( num_fv==q[0].getNumChildren() && ( options::pureThTriggers() || !Trigger::isPureTheoryTrigger( pat ) ) ){ +void InstStrategyAutoGenTriggers::addPatternToPool( Node q, Node pat, unsigned num_fv, Node mpat ) { + d_pat_to_mpat[pat] = mpat; + unsigned num_vars = options::partialTriggers() ? d_num_trigger_vars[q] : q[0].getNumChildren(); + if( num_fv==num_vars && ( options::pureThTriggers() || !Trigger::isPureTheoryTrigger( pat ) ) ){ d_patTerms[0][q].push_back( pat ); d_is_single_trigger[ pat ] = true; }else{ @@ -467,6 +481,38 @@ void InstStrategyAutoGenTriggers::addPatternToPool( Node q, Node pat, unsigned n } } + +void InstStrategyAutoGenTriggers::addTrigger( inst::Trigger * tr, Node q ) { + if( tr ){ + if( d_num_trigger_vars[q]<q[0].getNumChildren() ){ + //partial trigger : generate implication to mark user pattern + Node ipl = NodeManager::currentNM()->mkNode( INST_PATTERN_LIST, d_quantEngine->getTermDatabase()->getVariableNode( tr->getInstPattern(), q ) ); + Node qq = NodeManager::currentNM()->mkNode( FORALL, d_vc_partition[1][q], NodeManager::currentNM()->mkNode( FORALL, d_vc_partition[0][q], q[1] ), ipl ); + Trace("auto-gen-trigger-partial") << "Make partially specified user pattern: " << std::endl; + Trace("auto-gen-trigger-partial") << " " << qq << std::endl; + Node lem = NodeManager::currentNM()->mkNode( OR, q.negate(), qq ); + d_quantEngine->addLemma( lem ); + }else{ + unsigned tindex; + if( tr->isMultiTrigger() ){ + //disable all other multi triggers + for( std::map< Trigger*, bool >::iterator it = d_auto_gen_trigger[1][q].begin(); it != d_auto_gen_trigger[1][q].end(); ++it ){ + d_auto_gen_trigger[1][q][ it->first ] = false; + } + tindex = 1; + }else{ + tindex = 0; + } + //making it during an instantiation round, so must reset + if( d_auto_gen_trigger[tindex][q].find( tr )==d_auto_gen_trigger[tindex][q].end() ){ + tr->resetInstantiationRound(); + tr->reset( Node::null() ); + } + d_auto_gen_trigger[tindex][q][tr] = true; + } + } +} + bool InstStrategyAutoGenTriggers::hasUserPatterns( Node q ) { if( q.getNumChildren()==3 ){ std::map< Node, bool >::iterator it = d_hasUserPatterns.find( q ); @@ -519,8 +565,7 @@ bool InstStrategyLocalTheoryExt::isLocalTheoryExt( Node f ) { Trace("local-t-ext") << " " << patTerms[i] << std::endl; } Trace("local-t-ext") << std::endl; - int matchOption = 0; - Trigger * tr = Trigger::mkTrigger( d_quantEngine, f, patTerms, matchOption, true, Trigger::TR_GET_OLD ); + Trigger * tr = Trigger::mkTrigger( d_quantEngine, f, patTerms, true, Trigger::TR_GET_OLD ); d_lte_trigger[f] = tr; }else{ Trace("local-t-ext") << "No local theory extensions trigger for " << f << "." << std::endl; diff --git a/src/theory/quantifiers/inst_strategy_e_matching.h b/src/theory/quantifiers/inst_strategy_e_matching.h index 028f24b27..e6d993294 100644..100755 --- a/src/theory/quantifiers/inst_strategy_e_matching.h +++ b/src/theory/quantifiers/inst_strategy_e_matching.h @@ -83,14 +83,18 @@ private: std::map< Node, std::map< inst::Trigger*, bool > > d_processed_trigger; //instantiation no patterns std::map< Node, std::vector< Node > > d_user_no_gen; + // number of trigger variables per quantifier + std::map< Node, unsigned > d_num_trigger_vars; + std::map< Node, Node > d_vc_partition[2]; + std::map< Node, Node > d_pat_to_mpat; private: /** process functions */ void processResetInstantiationRound( Theory::Effort effort ); int process( Node q, Theory::Effort effort, int e ); /** generate triggers */ void generateTriggers( Node q ); - void addPatternToPool( Node q, Node pat, unsigned num_fv ); - //bool addTrigger( inst::Trigger * tr, Node f, unsigned r ); + void addPatternToPool( Node q, Node pat, unsigned num_fv, Node mpat ); + void addTrigger( inst::Trigger * tr, Node f ); /** has user patterns */ bool hasUserPatterns( Node q ); /** has user patterns */ diff --git a/src/theory/quantifiers/instantiation_engine.cpp b/src/theory/quantifiers/instantiation_engine.cpp index 955dc5d86..db597d031 100644..100755 --- a/src/theory/quantifiers/instantiation_engine.cpp +++ b/src/theory/quantifiers/instantiation_engine.cpp @@ -137,11 +137,7 @@ void InstantiationEngine::check( Theory::Effort e, unsigned quant_e ){ doInstantiationRound( e ); if( d_quantEngine->inConflict() ){ Assert( d_quantEngine->getNumLemmasWaiting()>lastWaiting ); - Trace("inst-engine") << "Conflict = " << d_quantEngine->getNumLemmasWaiting() << " / " << d_quantEngine->getNumLemmasAddedThisRound(); - if( lastWaiting>0 ){ - Trace("inst-engine") << " (prev " << lastWaiting << ")"; - } - Trace("inst-engine") << std::endl; + Trace("inst-engine") << "Conflict, added lemmas = " << (d_quantEngine->getNumLemmasWaiting()-lastWaiting) << std::endl; }else if( d_quantEngine->hasAddedLemma() ){ Trace("inst-engine") << "Added lemmas = " << (d_quantEngine->getNumLemmasWaiting()-lastWaiting) << std::endl; } diff --git a/src/theory/quantifiers/instantiation_engine.h b/src/theory/quantifiers/instantiation_engine.h index d2b3740a1..d2b3740a1 100644..100755 --- a/src/theory/quantifiers/instantiation_engine.h +++ b/src/theory/quantifiers/instantiation_engine.h diff --git a/src/theory/quantifiers/kinds b/src/theory/quantifiers/kinds index b03c4ad3b..b03c4ad3b 100644..100755 --- a/src/theory/quantifiers/kinds +++ b/src/theory/quantifiers/kinds diff --git a/src/theory/quantifiers/local_theory_ext.cpp b/src/theory/quantifiers/local_theory_ext.cpp index ada28c084..ada28c084 100644..100755 --- a/src/theory/quantifiers/local_theory_ext.cpp +++ b/src/theory/quantifiers/local_theory_ext.cpp diff --git a/src/theory/quantifiers/local_theory_ext.h b/src/theory/quantifiers/local_theory_ext.h index 94abf3c90..94abf3c90 100644..100755 --- a/src/theory/quantifiers/local_theory_ext.h +++ b/src/theory/quantifiers/local_theory_ext.h diff --git a/src/theory/quantifiers/macros.cpp b/src/theory/quantifiers/macros.cpp index 582599680..582599680 100644..100755 --- a/src/theory/quantifiers/macros.cpp +++ b/src/theory/quantifiers/macros.cpp diff --git a/src/theory/quantifiers/macros.h b/src/theory/quantifiers/macros.h index 39ec2f0a1..39ec2f0a1 100644..100755 --- a/src/theory/quantifiers/macros.h +++ b/src/theory/quantifiers/macros.h diff --git a/src/theory/quantifiers/model_builder.cpp b/src/theory/quantifiers/model_builder.cpp index 3ae36b1d4..10a5ae41b 100644..100755 --- a/src/theory/quantifiers/model_builder.cpp +++ b/src/theory/quantifiers/model_builder.cpp @@ -66,7 +66,7 @@ void QModelBuilder::debugModel( FirstOrderModel* fm ){ tests++; std::vector< Node > terms; for( int k=0; k<riter.getNumTerms(); k++ ){ - terms.push_back( riter.getTerm( k ) ); + terms.push_back( riter.getCurrentTerm( k ) ); } Node n = d_qe->getInstantiation( f, vars, terms ); Node val = fm->getValue( n ); @@ -84,7 +84,9 @@ void QModelBuilder::debugModel( FirstOrderModel* fm ){ } Trace("quant-check-model") << "." << std::endl; }else{ - Trace("quant-check-model") << "Warning: Could not test quantifier " << f << std::endl; + if( riter.isIncomplete() ){ + Trace("quant-check-model") << "Warning: Could not test quantifier " << f << std::endl; + } } } } @@ -114,7 +116,7 @@ bool TermArgBasisTrie::addTerm2( FirstOrderModel* fm, Node n, int argIndex ){ QModelBuilderIG::QModelBuilderIG( context::Context* c, QuantifiersEngine* qe ) : -QModelBuilder( c, qe ) { +QModelBuilder( c, qe ), d_basisNoMatch( c ) { } @@ -302,7 +304,7 @@ void QModelBuilderIG::analyzeModel( FirstOrderModel* fm ){ for( size_t i=0; i<fmig->d_uf_terms[op].size(); i++ ){ Node n = fmig->d_uf_terms[op][i]; //for calculating if op is constant - if( !n.getAttribute(NoMatchAttribute()) ){ + if( d_qe->getTermDatabase()->isTermActive( n ) ){ Node v = fmig->getRepresentative( n ); if( i==0 ){ d_uf_prefs[op].d_const_val = v; @@ -312,12 +314,11 @@ void QModelBuilderIG::analyzeModel( FirstOrderModel* fm ){ } } //for calculating terms that we don't need to consider - if( !n.getAttribute(NoMatchAttribute()) || n.getAttribute(ModelBasisArgAttribute())!=0 ){ - if( !n.getAttribute(BasisNoMatchAttribute()) ){ + if( d_qe->getTermDatabase()->isTermActive( n ) || n.getAttribute(ModelBasisArgAttribute())!=0 ){ + if( d_basisNoMatch.find( n )==d_basisNoMatch.end() ){ //need to consider if it is not congruent modulo model basis if( !tabt.addTerm( fmig, n ) ){ - BasisNoMatchAttribute bnma; - n.setAttribute(bnma,true); + d_basisNoMatch[n] = true; } } } @@ -382,8 +383,8 @@ bool QModelBuilderIG::isQuantifierActive( Node f ){ } bool QModelBuilderIG::isTermActive( Node n ){ - return !n.getAttribute(NoMatchAttribute()) || //it is not congruent to another active term - ( n.getAttribute(ModelBasisArgAttribute())!=0 && !n.getAttribute(BasisNoMatchAttribute()) ); //or it has model basis arguments + return d_qe->getTermDatabase()->isTermActive( n ) || //it is not congruent to another active term + ( n.getAttribute(ModelBasisArgAttribute())!=0 && d_basisNoMatch.find( n )==d_basisNoMatch.end() ); //or it has model basis arguments //and is not congruent modulo model basis //to another active term } @@ -400,15 +401,19 @@ bool QModelBuilderIG::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, i Debug("inst-fmf-ei") << "Begin instantiation..." << std::endl; while( !riter.isFinished() && ( d_addedLemmas==0 || !options::fmfOneInstPerRound() ) ){ d_triedLemmas++; - for( int i=0; i<(int)riter.d_index.size(); i++ ){ - Trace("try") << i << " : " << riter.d_index[i] << " : " << riter.getTerm( i ) << std::endl; + 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 - Debug("fmf-model-eval") << "Evaluating "; - riter.debugPrintSmall("fmf-model-eval"); - Debug("fmf-model-eval") << "Done calculating terms." << std::endl; + 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; @@ -426,7 +431,7 @@ bool QModelBuilderIG::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, i //instantiation was not shown to be true, construct the match InstMatch m( f ); for( int i=0; i<riter.getNumTerms(); i++ ){ - m.set( d_qe, riter.d_index_order[i], riter.getTerm( i ) ); + m.set( d_qe, i, riter.getCurrentTerm( i ) ); } Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl; //add as instantiation @@ -464,8 +469,8 @@ bool QModelBuilderIG::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, i 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 - d_incomplete_check = riter.d_incomplete; + //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round + d_incomplete_check = riter.isIncomplete(); return true; }else{ return false; @@ -667,7 +672,7 @@ int QModelBuilderDefault::doInstGen( FirstOrderModel* fm, Node f ){ //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, 0, true, inst::Trigger::TR_MAKE_NEW ); + 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() ); diff --git a/src/theory/quantifiers/model_builder.h b/src/theory/quantifiers/model_builder.h index 906673903..e4f9529a8 100644..100755 --- a/src/theory/quantifiers/model_builder.h +++ b/src/theory/quantifiers/model_builder.h @@ -57,15 +57,6 @@ public: -/** Attribute true for nodes that should not be used when considered for inst-gen basis */ -struct BasisNoMatchAttributeId {}; -/** use the special for boolean flag */ -typedef expr::Attribute< BasisNoMatchAttributeId, - bool, - expr::attr::NullCleanupStrategy, - true // context dependent - > BasisNoMatchAttribute; - class TermArgBasisTrie { private: bool addTerm2( FirstOrderModel* fm, Node n, int argIndex ); @@ -85,7 +76,9 @@ public: */ class QModelBuilderIG : public QModelBuilder { + typedef context::CDHashMap<Node, bool, NodeHashFunction> BoolMap; protected: + BoolMap d_basisNoMatch; //map from operators to model preference data std::map< Node, uf::UfModelPreferenceData > d_uf_prefs; //built model uf diff --git a/src/theory/quantifiers/model_engine.cpp b/src/theory/quantifiers/model_engine.cpp index 0bbca88eb..5d575969f 100644..100755 --- a/src/theory/quantifiers/model_engine.cpp +++ b/src/theory/quantifiers/model_engine.cpp @@ -153,27 +153,23 @@ int ModelEngine::checkModel(){ //d_quantEngine->getEqualityQuery()->flattenRepresentatives( fm->d_rep_set.d_type_reps ); //for debugging - if( Trace.isOn("model-engine") || Trace.isOn("model-engine-debug") ){ - for( std::map< TypeNode, std::vector< Node > >::iterator it = fm->d_rep_set.d_type_reps.begin(); - it != fm->d_rep_set.d_type_reps.end(); ++it ){ - if( it->first.isSort() ){ - Trace("model-engine") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl; - if( Trace.isOn("model-engine-debug") ){ - Trace("model-engine-debug") << " Reps : "; - for( size_t i=0; i<it->second.size(); i++ ){ - Trace("model-engine-debug") << it->second[i] << " "; - } - Trace("model-engine-debug") << std::endl; - Trace("model-engine-debug") << " Term reps : "; - for( size_t i=0; i<it->second.size(); i++ ){ - Node r = d_quantEngine->getEqualityQuery()->getInternalRepresentative( it->second[i], Node::null(), 0 ); - Trace("model-engine-debug") << r << " "; - } - Trace("model-engine-debug") << std::endl; - Node mbt = d_quantEngine->getTermDatabase()->getModelBasisTerm(it->first); - Trace("model-engine-debug") << " Basis term : " << mbt << std::endl; - } + for( std::map< TypeNode, std::vector< Node > >::iterator it = fm->d_rep_set.d_type_reps.begin(); + it != fm->d_rep_set.d_type_reps.end(); ++it ){ + if( it->first.isSort() ){ + Trace("model-engine") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl; + Trace("model-engine-debug") << " Reps : "; + for( size_t i=0; i<it->second.size(); i++ ){ + Trace("model-engine-debug") << it->second[i] << " "; } + Trace("model-engine-debug") << std::endl; + Trace("model-engine-debug") << " Term reps : "; + for( size_t i=0; i<it->second.size(); i++ ){ + Node r = d_quantEngine->getEqualityQuery()->getInternalRepresentative( it->second[i], Node::null(), 0 ); + Trace("model-engine-debug") << r << " "; + } + Trace("model-engine-debug") << std::endl; + Node mbt = d_quantEngine->getTermDatabase()->getModelBasisTerm(it->first); + Trace("model-engine-debug") << " Basis term : " << mbt << std::endl; } } @@ -221,11 +217,12 @@ int ModelEngine::checkModel(){ //print debug information if( d_quantEngine->inConflict() ){ - Trace("model-engine") << "Conflict = " << d_quantEngine->getNumLemmasWaiting() << " / " << d_quantEngine->getNumLemmasAddedThisRound() << std::endl; + Trace("model-engine") << "Conflict, added lemmas = "; }else{ - Trace("model-engine") << "Added Lemmas = " << d_addedLemmas << " / " << d_triedLemmas << " / "; - Trace("model-engine") << d_totalLemmas << std::endl; - } + Trace("model-engine") << "Added Lemmas = "; + } + Trace("model-engine") << d_addedLemmas << " / " << d_triedLemmas << " / "; + Trace("model-engine") << d_totalLemmas << std::endl; return d_addedLemmas; } @@ -281,15 +278,15 @@ void ModelEngine::exhaustiveInstantiate( Node f, int effort ){ //create a rep set iterator and iterate over the (relevant) domain of the quantifier RepSetIterator riter( d_quantEngine, &(d_quantEngine->getModel()->d_rep_set) ); if( riter.setQuantifier( f ) ){ - Trace("fmf-exh-inst") << "...exhaustive instantiation set, incomplete=" << riter.d_incomplete << "..." << std::endl; - if( !riter.d_incomplete ){ + Trace("fmf-exh-inst") << "...exhaustive instantiation set, incomplete=" << riter.isIncomplete() << "..." << std::endl; + if( !riter.isIncomplete() ){ int triedLemmas = 0; int addedLemmas = 0; while( !riter.isFinished() && ( addedLemmas==0 || !options::fmfOneInstPerRound() ) ){ //instantiation was not shown to be true, construct the match InstMatch m( f ); for( int i=0; i<riter.getNumTerms(); i++ ){ - m.set( d_quantEngine, riter.d_index_order[i], riter.getTerm( i ) ); + m.set( d_quantEngine, i, riter.getCurrentTerm( i ) ); } Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl; triedLemmas++; @@ -309,11 +306,10 @@ void ModelEngine::exhaustiveInstantiate( Node f, int effort ){ d_statistics.d_exh_inst_lemmas += addedLemmas; } }else{ - Trace("fmf-exh-inst") << "...exhaustive instantiation failed to set, incomplete=" << riter.d_incomplete << "..." << std::endl; - Assert( riter.d_incomplete ); + Trace("fmf-exh-inst") << "...exhaustive instantiation did set, incomplete=" << riter.isIncomplete() << "..." << std::endl; } //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round - d_incomplete_check = d_incomplete_check || riter.d_incomplete; + d_incomplete_check = d_incomplete_check || riter.isIncomplete(); } } diff --git a/src/theory/quantifiers/model_engine.h b/src/theory/quantifiers/model_engine.h index 12f18aa08..12f18aa08 100644..100755 --- a/src/theory/quantifiers/model_engine.h +++ b/src/theory/quantifiers/model_engine.h diff --git a/src/theory/quantifiers/quant_conflict_find.cpp b/src/theory/quantifiers/quant_conflict_find.cpp index ca87a607d..bac2aa35c 100644..100755 --- a/src/theory/quantifiers/quant_conflict_find.cpp +++ b/src/theory/quantifiers/quant_conflict_find.cpp @@ -49,6 +49,7 @@ QuantInfo::~QuantInfo() { void QuantInfo::initialize( QuantConflictFind * p, Node q, Node qn ) { d_q = q; + d_extra_var.clear(); for( unsigned i=0; i<q[0].getNumChildren(); i++ ){ d_match.push_back( TNode::null() ); d_match_term.push_back( TNode::null() ); @@ -77,33 +78,31 @@ void QuantInfo::initialize( QuantConflictFind * p, Node q, Node qn ) { } } */ - if( d_mg->isValid() ){ - for( unsigned j=q[0].getNumChildren(); j<d_vars.size(); j++ ){ - if( d_vars[j].getKind()!=BOUND_VARIABLE ){ - d_var_mg[j] = NULL; - bool is_tsym = false; - if( !MatchGen::isHandledUfTerm( d_vars[j] ) && d_vars[j].getKind()!=ITE ){ - is_tsym = true; - d_tsym_vars.push_back( j ); - } - if( !is_tsym || options::qcfTConstraint() ){ - d_var_mg[j] = new MatchGen( this, d_vars[j], true ); - } - if( !d_var_mg[j] || !d_var_mg[j]->isValid() ){ - Trace("qcf-invalid") << "QCF invalid : cannot match for " << d_vars[j] << std::endl; - d_mg->setInvalid(); - break; - }else{ - std::vector< int > bvars; - d_var_mg[j]->determineVariableOrder( this, bvars ); - } + for( unsigned j=q[0].getNumChildren(); j<d_vars.size(); j++ ){ + if( d_vars[j].getKind()!=BOUND_VARIABLE ){ + d_var_mg[j] = NULL; + bool is_tsym = false; + if( !MatchGen::isHandledUfTerm( d_vars[j] ) && d_vars[j].getKind()!=ITE ){ + is_tsym = true; + d_tsym_vars.push_back( j ); + } + if( !is_tsym || options::qcfTConstraint() ){ + d_var_mg[j] = new MatchGen( this, d_vars[j], true ); + } + if( !d_var_mg[j] || !d_var_mg[j]->isValid() ){ + Trace("qcf-invalid") << "QCF invalid : cannot match for " << d_vars[j] << std::endl; + d_mg->setInvalid(); + break; + }else{ + std::vector< int > bvars; + d_var_mg[j]->determineVariableOrder( this, bvars ); } - } - if( d_mg->isValid() ){ - std::vector< int > bvars; - d_mg->determineVariableOrder( this, bvars ); } } + if( d_mg->isValid() ){ + std::vector< int > bvars; + d_mg->determineVariableOrder( this, bvars ); + } }else{ Trace("qcf-invalid") << "QCF invalid : body of formula cannot be processed." << std::endl; } @@ -113,14 +112,15 @@ void QuantInfo::initialize( QuantConflictFind * p, Node q, Node qn ) { //optimization : record variable argument positions for terms that must be matched std::vector< TNode > vars; //TODO: revisit this, makes QCF faster, but misses conflicts due to caring about paths that may not be relevant (starExec jobs 14136/14137) - //if( options::qcfSkipRd() ){ - // for( unsigned j=q[0].getNumChildren(); j<d_vars.size(); j++ ){ - // vars.push_back( d_vars[j] ); - // } - //} - //get all variables that are always relevant - std::map< TNode, bool > visited; - getPropagateVars( vars, q[1], false, visited ); + if( options::qcfSkipRd() ){ + for( unsigned j=q[0].getNumChildren(); j<d_vars.size(); j++ ){ + vars.push_back( d_vars[j] ); + } + }else{ + //get all variables that are always relevant + std::map< TNode, bool > visited; + getPropagateVars( p, vars, q[1], false, visited ); + } for( unsigned j=0; j<vars.size(); j++ ){ Node v = vars[j]; TNode f = p->getTermDatabase()->getMatchOperator( v ); @@ -141,7 +141,7 @@ void QuantInfo::initialize( QuantConflictFind * p, Node q, Node qn ) { } } -void QuantInfo::getPropagateVars( std::vector< TNode >& vars, TNode n, bool pol, std::map< TNode, bool >& visited ){ +void QuantInfo::getPropagateVars( QuantConflictFind * p, std::vector< TNode >& vars, TNode n, bool pol, std::map< TNode, bool >& visited ){ std::map< TNode, bool >::iterator itv = visited.find( n ); if( itv==visited.end() ){ visited[n] = true; @@ -150,6 +150,12 @@ void QuantInfo::getPropagateVars( std::vector< TNode >& vars, TNode n, bool pol, if( d_var_num.find( n )!=d_var_num.end() ){ Assert( std::find( vars.begin(), vars.end(), n )==vars.end() ); vars.push_back( n ); + TNode f = p->getTermDatabase()->getMatchOperator( n ); + if( !f.isNull() ){ + if( std::find( p->d_func_rel_dom[f].begin(), p->d_func_rel_dom[f].end(), d_q )==p->d_func_rel_dom[f].end() ){ + p->d_func_rel_dom[f].push_back( d_q ); + } + } }else if( MatchGen::isHandledBoolConnective( n ) ){ Assert( n.getKind()!=IMPLIES ); QuantPhaseReq::getEntailPolarity( n, 0, true, pol, rec, newPol ); @@ -157,12 +163,16 @@ void QuantInfo::getPropagateVars( std::vector< TNode >& vars, TNode n, bool pol, Trace("qcf-opt-debug") << "getPropagateVars " << n << ", pol = " << pol << ", rec = " << rec << std::endl; if( rec ){ for( unsigned i=0; i<n.getNumChildren(); i++ ){ - getPropagateVars( vars, n[i], pol, visited ); + getPropagateVars( p, vars, n[i], pol, visited ); } } } } +bool QuantInfo::isBaseMatchComplete() { + return d_vars_set.size()==(d_q[0].getNumChildren()+d_extra_var.size()); +} + void QuantInfo::registerNode( Node n, bool hasPol, bool pol, bool beneathQuant ) { Trace("qcf-qregister-debug2") << "Register : " << n << std::endl; if( n.getKind()==FORALL ){ @@ -209,7 +219,6 @@ void QuantInfo::flatten( Node n, bool beneathQuant ) { if( n.getKind()==BOUND_VARIABLE ){ d_inMatchConstraint[n] = true; } - //if( MatchGen::isHandledUfTerm( n ) || n.getKind()==ITE ){ if( d_var_num.find( n )==d_var_num.end() ){ Trace("qcf-qregister-debug2") << "Add FLATTEN VAR : " << n << std::endl; d_var_num[n] = d_vars.size(); @@ -219,6 +228,8 @@ void QuantInfo::flatten( Node n, bool beneathQuant ) { d_match_term.push_back( TNode::null() ); if( n.getKind()==ITE ){ registerNode( n, false, false ); + }else if( n.getKind()==BOUND_VARIABLE ){ + d_extra_var.push_back( n ); }else{ for( unsigned i=0; i<n.getNumChildren(); i++ ){ flatten( n[i], beneathQuant ); @@ -233,13 +244,15 @@ void QuantInfo::flatten( Node n, bool beneathQuant ) { } -void QuantInfo::reset_round( QuantConflictFind * p ) { +bool QuantInfo::reset_round( QuantConflictFind * p ) { for( unsigned i=0; i<d_match.size(); i++ ){ d_match[i] = TNode::null(); d_match_term[i] = TNode::null(); } + d_vars_set.clear(); d_curr_var_deq.clear(); d_tconstraints.clear(); + //add built-in variable constraints for( unsigned r=0; r<2; r++ ){ for( std::map< int, std::vector< Node > >::iterator it = d_var_constraint[r].begin(); @@ -257,7 +270,7 @@ void QuantInfo::reset_round( QuantConflictFind * p ) { d_mg->d_children.clear(); d_mg->d_n = NodeManager::currentNM()->mkConst( true ); d_mg->d_type = MatchGen::typ_ground; - return; + return false; } } } @@ -268,6 +281,7 @@ void QuantInfo::reset_round( QuantConflictFind * p ) { } //now, reset for matching d_mg->reset( p, false, this ); + return true; } int QuantInfo::getCurrentRepVar( int v ) { @@ -377,11 +391,12 @@ int QuantInfo::addConstraint( QuantConflictFind * p, int v, TNode n, int vn, boo } } } - d_match[v] = TNode::null(); + unsetMatch( p, v ); return 1; }else{ //std::map< int, TNode >::iterator itm = d_match.find( v ); bool isGroundRep = false; + bool isGround = false; if( vn!=-1 ){ Debug("qcf-match-debug") << " ...Variable bound to variable" << std::endl; //std::map< int, TNode >::iterator itmn = d_match.find( vn ); @@ -428,13 +443,14 @@ int QuantInfo::addConstraint( QuantConflictFind * p, int v, TNode n, int vn, boo Debug("qcf-match-debug") << " ...Variable bound to ground" << std::endl; if( d_match[v].isNull() ){ //isGroundRep = true; ?? + isGround = true; }else{ //compare ground values Debug("qcf-match-debug") << " -> Ground value, compare " << d_match[v] << " "<< n << std::endl; return p->areMatchEqual( d_match[v], n ) ? 0 : -1; } } - if( setMatch( p, v, n, isGroundRep ) ){ + if( setMatch( p, v, n, isGroundRep, isGround ) ){ Debug("qcf-match-debug") << " -> success" << std::endl; return 1; }else{ @@ -500,7 +516,7 @@ bool QuantInfo::isConstrainedVar( int v ) { } } -bool QuantInfo::setMatch( QuantConflictFind * p, int v, TNode n, bool isGroundRep ) { +bool QuantInfo::setMatch( QuantConflictFind * p, int v, TNode n, bool isGroundRep, bool isGround ) { if( getCurrentCanBeEqual( p, v, n ) ){ if( isGroundRep ){ //fail if n does not exist in the relevant domain of each of the argument positions @@ -518,6 +534,12 @@ bool QuantInfo::setMatch( QuantConflictFind * p, int v, TNode n, bool isGroundRe } } Debug("qcf-match-debug") << "-- bind : " << v << " -> " << n << ", checked " << d_curr_var_deq[v].size() << " disequalities" << std::endl; + if( isGround ){ + if( d_vars[v].getKind()==BOUND_VARIABLE ){ + d_vars_set[v] = true; + Debug("qcf-match-debug") << "---- now bound " << d_vars_set.size() << " / " << d_q[0].getNumChildren() << " base variables." << std::endl; + } + } d_match[v] = n; return true; }else{ @@ -525,6 +547,14 @@ bool QuantInfo::setMatch( QuantConflictFind * p, int v, TNode n, bool isGroundRe } } +void QuantInfo::unsetMatch( QuantConflictFind * p, int v ) { + Debug("qcf-match-debug") << "-- unbind : " << v << std::endl; + if( d_vars[v].getKind()==BOUND_VARIABLE && d_vars_set.find( v )!=d_vars_set.end() ){ + d_vars_set.erase( v ); + } + d_match[ v ] = TNode::null(); +} + bool QuantInfo::isMatchSpurious( QuantConflictFind * p ) { for( int i=0; i<getNumVars(); i++ ){ //std::map< int, TNode >::iterator it = d_match.find( i ); @@ -538,6 +568,42 @@ bool QuantInfo::isMatchSpurious( QuantConflictFind * p ) { } bool QuantInfo::isTConstraintSpurious( QuantConflictFind * p, std::vector< Node >& terms ) { + if( options::qcfEagerTest() ){ + //check whether the instantiation evaluates as expected + if( p->d_effort==QuantConflictFind::effort_conflict ){ + Trace("qcf-instance-check") << "Possible conflict instance for " << d_q << " : " << std::endl; + std::map< TNode, TNode > subs; + for( unsigned i=0; i<terms.size(); i++ ){ + Trace("qcf-instance-check") << " " << terms[i] << std::endl; + subs[d_q[0][i]] = terms[i]; + } + for( unsigned i=0; i<d_extra_var.size(); i++ ){ + Node n = getCurrentExpValue( d_extra_var[i] ); + Trace("qcf-instance-check") << " " << d_extra_var[i] << " -> " << n << std::endl; + subs[d_extra_var[i]] = n; + } + if( !p->getTermDatabase()->isEntailed( d_q[1], subs, false, false ) ){ + Trace("qcf-instance-check") << "...not entailed to be false." << std::endl; + return true; + } + }else{ + Node inst = p->d_quantEngine->getInstantiation( d_q, terms ); + Node inst_eval = p->getTermDatabase()->evaluateTerm( inst, NULL, options::qcfTConstraint() ); + if( Trace.isOn("qcf-instance-check") ){ + Trace("qcf-instance-check") << "Possible propagating instance for " << d_q << " : " << std::endl; + for( unsigned i=0; i<terms.size(); i++ ){ + Trace("qcf-instance-check") << " " << terms[i] << std::endl; + } + Trace("qcf-instance-check") << "...evaluates to " << inst_eval << std::endl; + } + if( inst_eval.isNull() || inst_eval==p->getTermDatabase()->d_true || !isPropagatingInstance( p, inst_eval ) ){ + Trace("qcf-instance-check") << "...spurious." << std::endl; + return true; + }else{ + Trace("qcf-instance-check") << "...not spurious." << std::endl; + } + } + } if( !d_tconstraints.empty() ){ //check constraints for( std::map< Node, bool >::iterator it = d_tconstraints.begin(); it != d_tconstraints.end(); ++it ){ @@ -552,6 +618,26 @@ bool QuantInfo::isTConstraintSpurious( QuantConflictFind * p, std::vector< Node return false; } +bool QuantInfo::isPropagatingInstance( QuantConflictFind * p, Node n ) { + if( n.getKind()==FORALL ){ + //TODO? + return true; + }else if( n.getKind()==NOT || n.getKind()==AND || n.getKind()==OR || n.getKind()==EQUAL || n.getKind()==ITE || n.getKind()==IFF ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + if( !isPropagatingInstance( p, n[i] ) ){ + return false; + } + } + return true; + }else{ + if( p->getEqualityEngine()->hasTerm( n ) || isGroundSubterm( n ) ){ + return true; + } + } + Trace("qcf-instance-check-debug") << "...not propagating instance because of " << n << std::endl; + return false; +} + bool QuantInfo::entailmentTest( QuantConflictFind * p, Node lit, bool chEnt ) { Trace("qcf-tconstraint-debug") << "Check : " << lit << std::endl; Node rew = Rewriter::rewrite( lit ); @@ -606,6 +692,9 @@ bool QuantInfo::completeMatch( QuantConflictFind * p, std::vector< int >& assign doFail = true; success = false; }else{ + if( isBaseMatchComplete() && options::qcfEagerTest() ){ + return true; + } //solve for interpreted symbol matches // this breaks the invariant that all introduced constraints are over existing terms for( int i=(int)(d_tsym_vars.size()-1); i>=0; i-- ){ @@ -636,7 +725,7 @@ bool QuantInfo::completeMatch( QuantConflictFind * p, std::vector< int >& assign if( !z.isNull() ){ Trace("qcf-tconstraint-debug") << "...set " << d_vars[vn] << " = " << z << std::endl; assigned.push_back( vn ); - if( !setMatch( p, vn, z, false ) ){ + if( !setMatch( p, vn, z, false, true ) ){ success = false; break; } @@ -678,7 +767,7 @@ bool QuantInfo::completeMatch( QuantConflictFind * p, std::vector< int >& assign if( !sum.isNull() ){ assigned.push_back( slv_v ); Trace("qcf-tconstraint-debug") << "...set " << d_vars[slv_v] << " = " << sum << std::endl; - if( !setMatch( p, slv_v, sum, false ) ){ + if( !setMatch( p, slv_v, sum, false, true ) ){ success = false; } p->d_tempCache.push_back( sum ); @@ -764,7 +853,7 @@ bool QuantInfo::completeMatch( QuantConflictFind * p, std::vector< int >& assign int currIndex = d_una_eqc_count[d_una_index]; d_una_eqc_count[d_una_index]++; Trace("qcf-check-unassign") << d_unassigned[d_una_index] << "->" << p->d_eqcs[d_unassigned_tn[d_una_index]][currIndex] << std::endl; - if( setMatch( p, d_unassigned[d_una_index], p->d_eqcs[d_unassigned_tn[d_una_index]][currIndex], true ) ){ + if( setMatch( p, d_unassigned[d_una_index], p->d_eqcs[d_unassigned_tn[d_una_index]][currIndex], true, true ) ){ d_match_term[d_unassigned[d_una_index]] = TNode::null(); Trace("qcf-check-unassign") << "Succeeded match " << d_una_index << std::endl; d_una_index++; @@ -813,9 +902,7 @@ bool QuantInfo::completeMatch( QuantConflictFind * p, std::vector< int >& assign } return true; }else{ - for( unsigned i=0; i<assigned.size(); i++ ){ - d_match[ assigned[i] ] = TNode::null(); - } + revertMatch( p, assigned ); assigned.clear(); return false; } @@ -837,9 +924,9 @@ void QuantInfo::getMatch( std::vector< Node >& terms ){ } } -void QuantInfo::revertMatch( std::vector< int >& assigned ) { +void QuantInfo::revertMatch( QuantConflictFind * p, std::vector< int >& assigned ) { for( unsigned i=0; i<assigned.size(); i++ ){ - d_match[ assigned[i] ] = TNode::null(); + unsetMatch( p, assigned[i] ); } } @@ -899,26 +986,7 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar ) if( isVar ){ Assert( qi->d_var_num.find( n )!=qi->d_var_num.end() ); if( n.getKind()==ITE ){ - /* - d_type = typ_ite_var; - d_type_not = false; - d_n = n; - d_children.push_back( MatchGen( qi, d_n[0] ) ); - if( d_children[0].isValid() ){ - d_type = typ_ite_var; - for( unsigned i=1; i<=2; i++ ){ - Node nn = n.eqNode( n[i] ); - d_children.push_back( MatchGen( qi, nn ) ); - d_children[d_children.size()-1].d_qni_bound_except.push_back( 0 ); - if( !d_children[d_children.size()-1].isValid() ){ - setInvalid(); - break; - } - } - }else{ -*/ - d_type = typ_invalid; - //} + d_type = typ_invalid; }else{ d_type = isHandledUfTerm( n ) ? typ_var : typ_tsym; d_qni_var_num[0] = qi->getVarNum( n ); @@ -961,26 +1029,6 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar ) break; } } - /* - else if( isTop && n.getKind()==OR && d_children[d_children.size()-1].d_type==typ_var_eq ){ - Trace("qcf-qregister-debug") << "Remove child, make built-in constraint" << std::endl; - //if variable equality/disequality at top level, remove immediately - bool cIsNot = d_children[d_children.size()-1].d_type_not; - Node cn = d_children[d_children.size()-1].d_n; - Assert( cn.getKind()==EQUAL ); - Assert( p->d_qinfo[q].isVar( cn[0] ) || p->d_qinfo[q].isVar( cn[1] ) ); - //make it a built-in constraint instead - for( unsigned i=0; i<2; i++ ){ - if( p->d_qinfo[q].isVar( cn[i] ) ){ - int v = p->d_qinfo[q].getVarNum( cn[i] ); - Node cno = cn[i==0 ? 1 : 0]; - p->d_qinfo[q].d_var_constraint[ cIsNot ? 0 : 1 ][v].push_back( cno ); - break; - } - } - d_children.pop_back(); - } - */ } }else{ d_type = typ_invalid; @@ -1003,6 +1051,7 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar ) } }else{ d_qni_gterm[i] = d_n[i]; + qi->setGroundSubterm( d_n[i] ); } } d_type = d_n.getKind()==EQUAL ? typ_eq : typ_tconstraint; @@ -1013,21 +1062,8 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar ) //we will just evaluate d_n = n; d_type = typ_ground; + qi->setGroundSubterm( d_n ); } - //if( d_type!=typ_invalid ){ - //determine an efficient children ordering - //if( !d_children.empty() ){ - //for( unsigned i=0; i<d_children.size(); i++ ){ - // d_children_order.push_back( i ); - //} - //if( !d_n.isNull() && ( d_n.getKind()==OR || d_n.getKind()==AND || d_n.getKind()==IFF ) ){ - //sort based on the type of the constraint : ground comes first, then literals, then others - //MatchGenSort mgs; - //mgs.d_mg = this; - //std::sort( d_children_order.begin(), d_children_order.end(), mgs ); - //} - //} - //} } Trace("qcf-qregister-debug") << "Done make match gen " << n << ", type = "; debugPrintType( "qcf-qregister-debug", d_type, true ); @@ -1036,78 +1072,96 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar ) } -void MatchGen::collectBoundVar( QuantInfo * qi, Node n, std::vector< int >& cbvars ) { - int v = qi->getVarNum( n ); - if( v!=-1 && std::find( cbvars.begin(), cbvars.end(), v )==cbvars.end() ){ - cbvars.push_back( v ); - } - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - collectBoundVar( qi, n[i], cbvars ); +void MatchGen::collectBoundVar( QuantInfo * qi, Node n, std::vector< int >& cbvars, std::map< Node, bool >& visited, bool& hasNested ) { + if( visited.find( n )==visited.end() ){ + visited[n] = true; + if( n.getKind()==FORALL ){ + hasNested = true; + } + int v = qi->getVarNum( n ); + if( v!=-1 && std::find( cbvars.begin(), cbvars.end(), v )==cbvars.end() ){ + cbvars.push_back( v ); + } + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + collectBoundVar( qi, n[i], cbvars, visited, hasNested ); + } } } void MatchGen::determineVariableOrder( QuantInfo * qi, std::vector< int >& bvars ) { - Trace("qcf-qregister-debug") << "Determine variable order " << d_n << std::endl; - bool isCom = d_type==typ_formula && ( d_n.getKind()==OR || d_n.getKind()==AND || d_n.getKind()==IFF ); - std::map< int, std::vector< int > > c_to_vars; - std::map< int, std::vector< int > > vars_to_c; - std::map< int, int > vb_count; - std::map< int, int > vu_count; - std::vector< bool > assigned; - Trace("qcf-qregister-debug") << "Calculate bound variables..." << std::endl; - for( unsigned i=0; i<d_children.size(); i++ ){ - collectBoundVar( qi, d_children[i].d_n, c_to_vars[i] ); - assigned.push_back( false ); - vb_count[i] = 0; - vu_count[i] = 0; - for( unsigned j=0; j<c_to_vars[i].size(); j++ ){ - int v = c_to_vars[i][j]; - vars_to_c[v].push_back( i ); - if( std::find( bvars.begin(), bvars.end(), v )==bvars.end() ){ - vu_count[i]++; - if( !isCom ){ - bvars.push_back( v ); + Trace("qcf-qregister-debug") << "Determine variable order " << d_n << ", #bvars = " << bvars.size() << std::endl; + bool isComm = d_type==typ_formula && ( d_n.getKind()==OR || d_n.getKind()==AND || d_n.getKind()==IFF ); + if( isComm ){ + std::map< int, std::vector< int > > c_to_vars; + std::map< int, std::vector< int > > vars_to_c; + std::map< int, int > vb_count; + std::map< int, int > vu_count; + std::map< int, bool > has_nested; + std::vector< bool > assigned; + Trace("qcf-qregister-debug") << "Calculate bound variables..." << std::endl; + for( unsigned i=0; i<d_children.size(); i++ ){ + std::map< Node, bool > visited; + has_nested[i] = false; + collectBoundVar( qi, d_children[i].d_n, c_to_vars[i], visited, has_nested[i] ); + assigned.push_back( false ); + vb_count[i] = 0; + vu_count[i] = 0; + for( unsigned j=0; j<c_to_vars[i].size(); j++ ){ + int v = c_to_vars[i][j]; + vars_to_c[v].push_back( i ); + if( std::find( bvars.begin(), bvars.end(), v )==bvars.end() ){ + vu_count[i]++; + }else{ + vb_count[i]++; } - }else{ - vb_count[i]++; } } - } - if( isCom ){ - //children that bind the least number of unbound variables go first + //children that bind no unbound variable, then the most number of bound, unbound variables go first + Trace("qcf-qregister-vo") << "Variable order for " << d_n << " : " << std::endl; do { + int min_score0 = -1; int min_score = -1; int min_score_index = -1; for( unsigned i=0; i<d_children.size(); i++ ){ if( !assigned[i] ){ - int score = vu_count[i]; - if( min_score==-1 || score<min_score ){ + Trace("qcf-qregister-debug2") << "Child " << i << " has b/ub : " << vb_count[i] << "/" << vu_count[i] << std::endl; + int score0 = 0;//has_nested[i] ? 0 : 1; + int score; + if( !options::qcfVoExp() ){ + score = vu_count[i]; + }else{ + score = vu_count[i]==0 ? 0 : ( 1 + qi->d_vars.size()*( qi->d_vars.size() - vb_count[i] ) + ( qi->d_vars.size() - vu_count[i] ) ); + } + if( min_score==-1 || score0<min_score0 || ( score0==min_score0 && score<min_score ) ){ + min_score0 = score0; min_score = score; min_score_index = i; } } } - Trace("qcf-qregister-debug") << "...assign child " << min_score_index << "/" << d_children.size() << std::endl; + Trace("qcf-qregister-vo") << " " << d_children_order.size()+1 << ": " << d_children[min_score_index].d_n << " : "; + Trace("qcf-qregister-vo") << vu_count[min_score_index] << " " << vb_count[min_score_index] << " " << has_nested[min_score_index] << std::endl; + Trace("qcf-qregister-debug") << "...assign child " << min_score_index << std::endl; + Trace("qcf-qregister-debug") << "...score : " << min_score << std::endl; Assert( min_score_index!=-1 ); //add to children order d_children_order.push_back( min_score_index ); assigned[min_score_index] = true; - //if( vb_count[min_score_index]==0 ){ - // d_independent.push_back( min_score_index ); - //} //determine order internal to children d_children[min_score_index].determineVariableOrder( qi, bvars ); Trace("qcf-qregister-debug") << "...bind variables" << std::endl; //now, make it a bound variable - for( unsigned i=0; i<c_to_vars[min_score_index].size(); i++ ){ - int v = c_to_vars[min_score_index][i]; - if( std::find( bvars.begin(), bvars.end(), v )==bvars.end() ){ - for( unsigned j=0; j<vars_to_c[v].size(); j++ ){ - int vc = vars_to_c[v][j]; - vu_count[vc]--; - vb_count[vc]++; + if( vu_count[min_score_index]>0 ){ + for( unsigned i=0; i<c_to_vars[min_score_index].size(); i++ ){ + int v = c_to_vars[min_score_index][i]; + if( std::find( bvars.begin(), bvars.end(), v )==bvars.end() ){ + for( unsigned j=0; j<vars_to_c[v].size(); j++ ){ + int vc = vars_to_c[v][j]; + vu_count[vc]--; + vb_count[vc]++; + } + bvars.push_back( v ); } - bvars.push_back( v ); } } Trace("qcf-qregister-debug") << "...done assign child " << min_score_index << std::endl; @@ -1117,6 +1171,16 @@ void MatchGen::determineVariableOrder( QuantInfo * qi, std::vector< int >& bvars for( unsigned i=0; i<d_children.size(); i++ ){ d_children_order.push_back( i ); d_children[i].determineVariableOrder( qi, bvars ); + //now add to bvars + std::map< Node, bool > visited; + std::vector< int > cvars; + bool has_nested = false; + collectBoundVar( qi, d_children[i].d_n, cvars, visited, has_nested ); + for( unsigned j=0; j<cvars.size(); j++ ){ + if( std::find( bvars.begin(), bvars.end(), cvars[j] )==bvars.end() ){ + bvars.push_back( cvars[j] ); + } + } } } } @@ -1169,15 +1233,20 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { d_qni.clear(); d_qni_bound.clear(); d_child_counter = -1; + d_use_children = true; d_tgt_orig = d_tgt; //set up processing matches if( d_type==typ_invalid ){ - //do nothing + d_use_children = false; }else if( d_type==typ_ground ){ + d_use_children = false; if( d_ground_eval[0]==( d_tgt ? p->d_true : p->d_false ) ){ d_child_counter = 0; } + }else if( qi->isBaseMatchComplete() && options::qcfEagerTest() ){ + d_use_children = false; + d_child_counter = 0; }else if( d_type==typ_bool_var ){ //get current value of the variable TNode n = qi->getCurrentValue( d_n ); @@ -1195,7 +1264,7 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { }else{ //unassigned, set match to true/false d_qni_bound[0] = vn; - qi->setMatch( p, vn, d_tgt ? p->d_true : p->d_false, false ); + qi->setMatch( p, vn, d_tgt ? p->d_true : p->d_false, false, true ); d_child_counter = 0; } if( d_child_counter==0 ){ @@ -1203,11 +1272,14 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { } }else if( d_type==typ_var ){ Assert( isHandledUfTerm( d_n ) ); - Node f = getMatchOperator( p, d_n ); + TNode f = getMatchOperator( p, d_n ); Debug("qcf-match-debug") << " reset: Var will match operators of " << f << std::endl; TermArgTrie * qni = p->getTermDatabase()->getTermArgTrie( Node::null(), f ); if( qni!=NULL ){ d_qn.push_back( qni ); + }else{ + //inform irrelevant quantifiers + p->setIrrelevantFunction( f ); } d_matched_basis = false; }else if( d_type==typ_tsym || d_type==typ_tconstraint ){ @@ -1257,7 +1329,7 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { } } }else{ - //otherwise, add a constraint to a variable + //otherwise, add a constraint to a variable TODO: this may be over-eager at effort > conflict, since equality may be a propagation if( vn[1]!=-1 && vn[0]==-1 ){ //swap Node t = nn[1]; @@ -1292,7 +1364,9 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { d_qn.push_back( NULL ); }else{ if( d_tgt && d_n.getKind()==FORALL ){ - //do nothing + //fail + }else if( d_n.getKind()==FORALL && p->d_effort==QuantConflictFind::effort_conflict && !options::qcfNestedConflict() ){ + //fail }else{ //reset the first child to d_tgt d_child_counter = 0; @@ -1309,7 +1383,7 @@ bool MatchGen::getNextMatch( QuantConflictFind * p, QuantInfo * qi ) { Debug("qcf-match") << " Get next match for : " << d_n << ", type = "; debugPrintType( "qcf-match", d_type ); Debug("qcf-match") << ", children = " << d_children.size() << ", binding = " << d_binding << std::endl; - if( d_type==typ_invalid || d_type==typ_ground ){ + if( !d_use_children ){ if( d_child_counter==0 ){ d_child_counter = -1; return true; @@ -1423,7 +1497,7 @@ bool MatchGen::getNextMatch( QuantConflictFind * p, QuantInfo * qi ) { for( std::map< int, int >::iterator it = d_qni_bound.begin(); it != d_qni_bound.end(); ++it ){ Debug("qcf-match") << " Clean up bound var " << it->second << std::endl; Assert( it->second<qi->getNumVars() ); - qi->d_match[ it->second ] = TNode::null(); + qi->unsetMatch( p, it->second ); qi->d_match_term[ it->second ] = TNode::null(); } d_qni_bound.clear(); @@ -1654,7 +1728,7 @@ bool MatchGen::doMatching( QuantConflictFind * p, QuantInfo * qi ) { if( it != d_qn[index]->d_data.end() ) { d_qni.push_back( it ); //set the match - if( it->first.getType().isComparableTo( qi->d_var_types[repVar] ) && qi->setMatch( p, d_qni_bound[index], it->first, true ) ){ + if( it->first.getType().isComparableTo( qi->d_var_types[repVar] ) && qi->setMatch( p, d_qni_bound[index], it->first, true, true ) ){ Debug("qcf-match-debug") << " Binding variable" << std::endl; if( d_qn.size()<d_qni_size ){ d_qn.push_back( &it->second ); @@ -1699,7 +1773,7 @@ bool MatchGen::doMatching( QuantConflictFind * p, QuantInfo * qi ) { d_qni[index]++; if( d_qni[index]!=d_qn[index]->d_data.end() ){ success = true; - if( qi->setMatch( p, itb->second, d_qni[index]->first, true ) ){ + if( qi->setMatch( p, itb->second, d_qni[index]->first, true, true ) ){ Debug("qcf-match-debug") << " Bind next variable" << std::endl; if( d_qn.size()<d_qni_size ){ d_qn.push_back( &d_qni[index]->second ); @@ -1709,7 +1783,7 @@ bool MatchGen::doMatching( QuantConflictFind * p, QuantInfo * qi ) { invalidMatch = true; } }else{ - qi->d_match[ itb->second ] = TNode::null(); + qi->unsetMatch( p, itb->second ); qi->d_match_term[ itb->second ] = TNode::null(); Debug("qcf-match-debug") << " Bind next variable, no more variables to bind" << std::endl; } @@ -1779,7 +1853,7 @@ void MatchGen::setInvalid() { } bool MatchGen::isHandledBoolConnective( TNode n ) { - return n.getType().isBoolean() && TermDb::isBoolConnective( n.getKind() ); + return TermDb::isBoolConnective( n.getKind() ) && ( n.getKind()!=ITE || n.getType().isBoolean() ); } bool MatchGen::isHandledUfTerm( TNode n ) { @@ -1835,28 +1909,31 @@ void QuantConflictFind::registerQuantifier( Node q ) { if( d_quantEngine->hasOwnership( q, this ) ){ d_quants.push_back( q ); d_quant_id[q] = d_quants.size(); - Trace("qcf-qregister") << "Register "; - debugPrintQuant( "qcf-qregister", q ); - Trace("qcf-qregister") << " : " << q << std::endl; + if( Trace.isOn("qcf-qregister") ){ + Trace("qcf-qregister") << "Register "; + debugPrintQuant( "qcf-qregister", q ); + Trace("qcf-qregister") << " : " << q << std::endl; + } //make QcfNode structure Trace("qcf-qregister") << "- Get relevant equality/disequality pairs, calculate flattening..." << std::endl; d_qinfo[q].initialize( this, q, q[1] ); //debug print - Trace("qcf-qregister") << "- Flattened structure is :" << std::endl; - Trace("qcf-qregister") << " "; - debugPrintQuantBody( "qcf-qregister", q, q[1] ); - Trace("qcf-qregister") << std::endl; - if( d_qinfo[q].d_vars.size()>q[0].getNumChildren() ){ - Trace("qcf-qregister") << " with additional constraints : " << std::endl; - for( unsigned j=q[0].getNumChildren(); j<d_qinfo[q].d_vars.size(); j++ ){ - Trace("qcf-qregister") << " ?x" << j << " = "; - debugPrintQuantBody( "qcf-qregister", q, d_qinfo[q].d_vars[j], false ); - Trace("qcf-qregister") << std::endl; - } - } - - Trace("qcf-qregister") << "Done registering quantifier." << std::endl; + if( Trace.isOn("qcf-qregister") ){ + Trace("qcf-qregister") << "- Flattened structure is :" << std::endl; + Trace("qcf-qregister") << " "; + debugPrintQuantBody( "qcf-qregister", q, q[1] ); + Trace("qcf-qregister") << std::endl; + if( d_qinfo[q].d_vars.size()>q[0].getNumChildren() ){ + Trace("qcf-qregister") << " with additional constraints : " << std::endl; + for( unsigned j=q[0].getNumChildren(); j<d_qinfo[q].d_vars.size(); j++ ){ + Trace("qcf-qregister") << " ?x" << j << " = "; + debugPrintQuantBody( "qcf-qregister", q, d_qinfo[q].d_vars[j], false ); + Trace("qcf-qregister") << std::endl; + } + } + Trace("qcf-qregister") << "Done registering quantifier." << std::endl; + } } } @@ -1933,6 +2010,18 @@ void QuantConflictFind::reset_round( Theory::Effort level ) { d_needs_computeRelEqr = true; } +void QuantConflictFind::setIrrelevantFunction( TNode f ) { + if( d_irr_func.find( f )==d_irr_func.end() ){ + d_irr_func[f] = true; + std::map< TNode, std::vector< Node > >::iterator it = d_func_rel_dom.find( f ); + if( it != d_func_rel_dom.end()){ + for( unsigned j=0; j<it->second.size(); j++ ){ + d_irr_quant[it->second[j]] = true; + } + } + } +} + /** check */ void QuantConflictFind::check( Theory::Effort level, unsigned quant_e ) { if( quant_e==QuantifiersEngine::QEFFORT_CONFLICT ){ @@ -1955,14 +2044,9 @@ void QuantConflictFind::check( Theory::Effort level, unsigned quant_e ) { } computeRelevantEqr(); - //determine order for quantified formulas - std::vector< Node > qorder; - for( unsigned i=0; i<d_quantEngine->getModel()->getNumAssertedQuantifiers(); i++ ){ - Node q = d_quantEngine->getModel()->getAssertedQuantifier( i, true ); - if( d_quantEngine->hasOwnership( q, this ) ){ - qorder.push_back( q ); - } - } + d_irr_func.clear(); + d_irr_quant.clear(); + if( Trace.isOn("qcf-debug") ){ Trace("qcf-debug") << std::endl; debugPrint("qcf-debug"); @@ -1973,77 +2057,83 @@ void QuantConflictFind::check( Theory::Effort level, unsigned quant_e ) { for( short e = effort_conflict; e<=end_e; e++ ){ d_effort = e; Trace("qcf-check") << "Checking quantified formulas at effort " << e << "..." << std::endl; - for( unsigned j=0; j<qorder.size(); j++ ){ - Node q = qorder[j]; - QuantInfo * qi = &d_qinfo[q]; - - Assert( d_qinfo.find( q )!=d_qinfo.end() ); - if( qi->matchGeneratorIsValid() ){ - Trace("qcf-check") << "Check quantified formula "; - debugPrintQuant("qcf-check", q); - Trace("qcf-check") << " : " << q << "..." << std::endl; - - Trace("qcf-check-debug") << "Reset round..." << std::endl; - qi->reset_round( this ); - //try to make a matches making the body false - Trace("qcf-check-debug") << "Get next match..." << std::endl; - while( qi->getNextMatch( this ) ){ - Trace("qcf-inst") << "*** Produced match at effort " << e << " : " << std::endl; - qi->debugPrintMatch("qcf-inst"); - Trace("qcf-inst") << std::endl; - std::vector< int > assigned; - if( !qi->isMatchSpurious( this ) ){ - if( qi->completeMatch( this, assigned ) ){ - std::vector< Node > terms; - qi->getMatch( terms ); - if( !qi->isTConstraintSpurious( this, terms ) ){ - //for debugging - if( Debug.isOn("qcf-check-inst") ){ - Node inst = d_quantEngine->getInstantiation( q, terms ); - Debug("qcf-check-inst") << "Check instantiation " << inst << "..." << std::endl; - Assert( !getTermDatabase()->isEntailed( inst, true ) ); - Assert( getTermDatabase()->isEntailed( inst, false ) || e>effort_conflict ); - } - if( d_quantEngine->addInstantiation( q, terms ) ){ - Trace("qcf-check") << " ... Added instantiation" << std::endl; - Trace("qcf-inst") << "*** Was from effort " << e << " : " << std::endl; - qi->debugPrintMatch("qcf-inst"); - Trace("qcf-inst") << std::endl; - ++addedLemmas; - if( e==effort_conflict ){ - d_quantEngine->markRelevant( q ); - ++(d_statistics.d_conflict_inst); - if( options::qcfAllConflict() ){ - isConflict = true; + for( unsigned i=0; i<d_quantEngine->getModel()->getNumAssertedQuantifiers(); i++ ){ + Node q = d_quantEngine->getModel()->getAssertedQuantifier( i, true ); + if( d_quantEngine->hasOwnership( q, this ) && d_irr_quant.find( q )==d_irr_quant.end() ){ + QuantInfo * qi = &d_qinfo[q]; + + Assert( d_qinfo.find( q )!=d_qinfo.end() ); + if( qi->matchGeneratorIsValid() ){ + Trace("qcf-check") << "Check quantified formula "; + debugPrintQuant("qcf-check", q); + Trace("qcf-check") << " : " << q << "..." << std::endl; + + Trace("qcf-check-debug") << "Reset round..." << std::endl; + if( qi->reset_round( this ) ){ + //try to make a matches making the body false + Trace("qcf-check-debug") << "Get next match..." << std::endl; + while( qi->getNextMatch( this ) ){ + Trace("qcf-inst") << "*** Produced match at effort " << e << " : " << std::endl; + qi->debugPrintMatch("qcf-inst"); + Trace("qcf-inst") << std::endl; + if( !qi->isMatchSpurious( this ) ){ + std::vector< int > assigned; + if( qi->completeMatch( this, assigned ) ){ + std::vector< Node > terms; + qi->getMatch( terms ); + bool tcs = qi->isTConstraintSpurious( this, terms ); + if( !tcs ){ + //for debugging + if( Debug.isOn("qcf-check-inst") ){ + Node inst = d_quantEngine->getInstantiation( q, terms ); + Debug("qcf-check-inst") << "Check instantiation " << inst << "..." << std::endl; + Assert( !getTermDatabase()->isEntailed( inst, true ) ); + Assert( getTermDatabase()->isEntailed( inst, false ) || e>effort_conflict ); + } + if( d_quantEngine->addInstantiation( q, terms ) ){ + Trace("qcf-check") << " ... Added instantiation" << std::endl; + Trace("qcf-inst") << "*** Was from effort " << e << " : " << std::endl; + qi->debugPrintMatch("qcf-inst"); + Trace("qcf-inst") << std::endl; + ++addedLemmas; + if( e==effort_conflict ){ + d_quantEngine->markRelevant( q ); + ++(d_statistics.d_conflict_inst); + if( options::qcfAllConflict() ){ + isConflict = true; + }else{ + d_conflict.set( true ); + } + break; + }else if( e==effort_prop_eq ){ + d_quantEngine->markRelevant( q ); + ++(d_statistics.d_prop_inst); + } }else{ - d_conflict.set( true ); + Trace("qcf-inst") << " ... Failed to add instantiation" << std::endl; + //this should only happen if the algorithm generates the same propagating instance twice this round + //in this case, break to avoid exponential behavior + break; } - break; - }else if( e==effort_prop_eq ){ - d_quantEngine->markRelevant( q ); - ++(d_statistics.d_prop_inst); + }else{ + Trace("qcf-inst") << " ... Spurious instantiation (match is T-inconsistent)" << std::endl; } + //clean up assigned + qi->revertMatch( this, assigned ); + d_tempCache.clear(); }else{ - Trace("qcf-inst") << " ... Failed to add instantiation" << std::endl; - //this should only happen if the algorithm generates the same propagating instance twice this round - //in this case, break to avoid exponential behavior - break; + Trace("qcf-inst") << " ... Spurious instantiation (cannot assign unassigned variables)" << std::endl; } + }else{ + Trace("qcf-inst") << " ... Spurious instantiation (match is inconsistent)" << std::endl; } - //clean up assigned - qi->revertMatch( assigned ); - d_tempCache.clear(); - }else{ - Trace("qcf-inst") << " ... Spurious instantiation (cannot assign unassigned variables)" << std::endl; } - }else{ - Trace("qcf-inst") << " ... Spurious instantiation (match is inconsistent)" << std::endl; + Trace("qcf-check") << "Done, conflict = " << d_conflict << std::endl; + if( d_conflict ){ + break; + } } } - Trace("qcf-check") << "Done, conflict = " << d_conflict << std::endl; - if( d_conflict ){ - break; - } } } if( addedLemmas>0 ){ @@ -2074,17 +2164,9 @@ void QuantConflictFind::check( Theory::Effort level, unsigned quant_e ) { void QuantConflictFind::computeRelevantEqr() { if( d_needs_computeRelEqr ){ d_needs_computeRelEqr = false; - Trace("qcf-check") << "Compute relevant equalities..." << std::endl; - //d_uf_terms.clear(); - //d_eqc_uf_terms.clear(); + Trace("qcf-check") << "Compute relevant equivalence classes..." << std::endl; d_eqcs.clear(); - //d_arg_reps.clear(); - //double clSet = 0; - //if( Trace.isOn("qcf-opt") ){ - // clSet = double(clock())/double(CLOCKS_PER_SEC); - //} - //now, store matches eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( getEqualityEngine() ); while( !eqcs_i.isFinished() ){ Node r = (*eqcs_i); diff --git a/src/theory/quantifiers/quant_conflict_find.h b/src/theory/quantifiers/quant_conflict_find.h index 8b42b0916..47a66b1b1 100644..100755 --- a/src/theory/quantifiers/quant_conflict_find.h +++ b/src/theory/quantifiers/quant_conflict_find.h @@ -35,6 +35,7 @@ class MatchGen { private: //current children information int d_child_counter; + bool d_use_children; //children of this object std::vector< int > d_children_order; unsigned getNumChildren() { return d_children.size(); } @@ -61,7 +62,7 @@ private: std::map< int, Node > d_ground_eval; //determine variable order void determineVariableOrder( QuantInfo * qi, std::vector< int >& bvars ); - void collectBoundVar( QuantInfo * qi, Node n, std::vector< int >& cbvars ); + void collectBoundVar( QuantInfo * qi, Node n, std::vector< int >& cbvars, std::map< Node, bool >& visited, bool& hasNested ); public: //type of the match generator enum { @@ -116,7 +117,16 @@ private: //for completing match std::vector< int > d_una_eqc_count; //optimization: track which arguments variables appear under UF terms in std::map< int, std::map< TNode, std::vector< unsigned > > > d_var_rel_dom; - void getPropagateVars( std::vector< TNode >& vars, TNode n, bool pol, std::map< TNode, bool >& visited ); + void getPropagateVars( QuantConflictFind * p, std::vector< TNode >& vars, TNode n, bool pol, std::map< TNode, bool >& visited ); + //optimization: number of variables set, to track when we can stop + std::map< int, bool > d_vars_set; + std::map< Node, bool > d_ground_terms; + std::vector< Node > d_extra_var; +public: + void setGroundSubterm( Node t ) { d_ground_terms[t] = true; } + bool isGroundSubterm( Node t ) { return d_ground_terms.find( t )!=d_ground_terms.end(); } + bool isBaseMatchComplete(); + bool isPropagatingInstance( QuantConflictFind * p, Node n ); public: QuantInfo(); ~QuantInfo(); @@ -146,7 +156,7 @@ public: } Node d_q; - void reset_round( QuantConflictFind * p ); + bool reset_round( QuantConflictFind * p ); public: //initialize void initialize( QuantConflictFind * p, Node q, Node qn ); @@ -161,12 +171,13 @@ public: bool getCurrentCanBeEqual( QuantConflictFind * p, int v, TNode n, bool chDiseq = false ); int addConstraint( QuantConflictFind * p, int v, TNode n, bool polarity ); int addConstraint( QuantConflictFind * p, int v, TNode n, int vn, bool polarity, bool doRemove ); - bool setMatch( QuantConflictFind * p, int v, TNode n, bool isGroundRep ); + bool setMatch( QuantConflictFind * p, int v, TNode n, bool isGroundRep, bool isGround ); + void unsetMatch( QuantConflictFind * p, int v ); bool isMatchSpurious( QuantConflictFind * p ); bool isTConstraintSpurious( QuantConflictFind * p, std::vector< Node >& terms ); bool entailmentTest( QuantConflictFind * p, Node lit, bool chEnt = true ); bool completeMatch( QuantConflictFind * p, std::vector< int >& assigned, bool doContinue = false ); - void revertMatch( std::vector< int >& assigned ); + void revertMatch( QuantConflictFind * p, std::vector< int >& assigned ); void debugPrintMatch( const char * c ); bool isConstrainedVar( int v ); public: @@ -184,6 +195,11 @@ private: std::map< Kind, Node > d_zero; //for storing nodes created during t-constraint solving (prevents memory leaks) std::vector< Node > d_tempCache; + //optimization: list of quantifiers that depend on ground function applications + std::map< TNode, std::vector< Node > > d_func_rel_dom; + std::map< TNode, bool > d_irr_func; + std::map< Node, bool > d_irr_quant; + void setIrrelevantFunction( TNode f ); private: std::map< Node, Node > d_op_node; int d_fid_count; diff --git a/src/theory/quantifiers/quant_equality_engine.cpp b/src/theory/quantifiers/quant_equality_engine.cpp index 3f89a799c..3f89a799c 100644..100755 --- a/src/theory/quantifiers/quant_equality_engine.cpp +++ b/src/theory/quantifiers/quant_equality_engine.cpp diff --git a/src/theory/quantifiers/quant_equality_engine.h b/src/theory/quantifiers/quant_equality_engine.h index 26654de4d..26654de4d 100644..100755 --- a/src/theory/quantifiers/quant_equality_engine.h +++ b/src/theory/quantifiers/quant_equality_engine.h diff --git a/src/theory/quantifiers/quant_split.cpp b/src/theory/quantifiers/quant_split.cpp index 9fb943e5e..5aff1a848 100644..100755 --- a/src/theory/quantifiers/quant_split.cpp +++ b/src/theory/quantifiers/quant_split.cpp @@ -48,11 +48,11 @@ void QuantDSplit::preRegisterQuantifier( Node q ) { }else{ int score = -1; if( options::quantDynamicSplit()==quantifiers::QUANT_DSPLIT_MODE_AGG ){ - score = dt.isUFinite() ? 1 : -1; + score = dt.isInterpretedFinite() ? 1 : -1; }else if( options::quantDynamicSplit()==quantifiers::QUANT_DSPLIT_MODE_DEFAULT ){ - score = dt.isUFinite() ? 1 : -1; + score = dt.isInterpretedFinite() ? 1 : -1; } - Trace("quant-dsplit-debug") << "Datatype " << dt.getName() << " is score " << score << " (" << dt.isUFinite() << " " << dt.isFinite() << ")" << std::endl; + Trace("quant-dsplit-debug") << "Datatype " << dt.getName() << " is score " << score << " (" << dt.isInterpretedFinite() << " " << dt.isFinite() << ")" << std::endl; if( score>max_score ){ max_index = i; max_score = score; diff --git a/src/theory/quantifiers/quant_split.h b/src/theory/quantifiers/quant_split.h index d36824998..d36824998 100644..100755 --- a/src/theory/quantifiers/quant_split.h +++ b/src/theory/quantifiers/quant_split.h diff --git a/src/theory/quantifiers/quant_util.cpp b/src/theory/quantifiers/quant_util.cpp index 3b7787a20..b9aab0236 100644..100755 --- a/src/theory/quantifiers/quant_util.cpp +++ b/src/theory/quantifiers/quant_util.cpp @@ -33,22 +33,15 @@ eq::EqualityEngine * QuantifiersModule::getEqualityEngine() { } bool QuantifiersModule::areEqual( TNode n1, TNode n2 ) { - eq::EqualityEngine * ee = getEqualityEngine(); - return n1==n2 || ( ee->hasTerm( n1 ) && ee->hasTerm( n2 ) && ee->areEqual( n1, n2 ) ); + return d_quantEngine->getEqualityQuery()->areEqual( n1, n2 ); } bool QuantifiersModule::areDisequal( TNode n1, TNode n2 ) { - eq::EqualityEngine * ee = getEqualityEngine(); - return n1!=n2 && ee->hasTerm( n1 ) && ee->hasTerm( n2 ) && ee->areDisequal( n1, n2, false ); + return d_quantEngine->getEqualityQuery()->areDisequal( n1, n2 ); } TNode QuantifiersModule::getRepresentative( TNode n ) { - eq::EqualityEngine * ee = getEqualityEngine(); - if( ee->hasTerm( n ) ){ - return ee->getRepresentative( n ); - }else{ - return n; - } + return d_quantEngine->getEqualityQuery()->getRepresentative( n ); } quantifiers::TermDb * QuantifiersModule::getTermDatabase() { @@ -389,7 +382,7 @@ void QuantPhaseReq::computePhaseReqs( Node n, bool polarity, std::map< Node, int } void QuantPhaseReq::getPolarity( Node n, int child, bool hasPol, bool pol, bool& newHasPol, bool& newPol ) { - if( n.getKind()==AND || n.getKind()==OR ){ + if( n.getKind()==AND || n.getKind()==OR || n.getKind()==SEP_STAR ){ newHasPol = hasPol; newPol = pol; }else if( n.getKind()==IMPLIES ){ diff --git a/src/theory/quantifiers/quant_util.h b/src/theory/quantifiers/quant_util.h index 79cdae437..79cdae437 100644..100755 --- a/src/theory/quantifiers/quant_util.h +++ b/src/theory/quantifiers/quant_util.h diff --git a/src/theory/quantifiers/quantifiers_attributes.cpp b/src/theory/quantifiers/quantifiers_attributes.cpp index b797f4ce9..b797f4ce9 100644..100755 --- a/src/theory/quantifiers/quantifiers_attributes.cpp +++ b/src/theory/quantifiers/quantifiers_attributes.cpp diff --git a/src/theory/quantifiers/quantifiers_attributes.h b/src/theory/quantifiers/quantifiers_attributes.h index 53cef796a..53cef796a 100644..100755 --- a/src/theory/quantifiers/quantifiers_attributes.h +++ b/src/theory/quantifiers/quantifiers_attributes.h diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp index 5aae4d640..68f824c57 100644..100755 --- a/src/theory/quantifiers/quantifiers_rewriter.cpp +++ b/src/theory/quantifiers/quantifiers_rewriter.cpp @@ -48,7 +48,7 @@ bool QuantifiersRewriter::isClause( Node n ){ bool QuantifiersRewriter::isLiteral( Node n ){ switch( n.getKind() ){ case NOT: - return isLiteral( n[0] ); + return n[0].getKind()!=NOT && isLiteral( n[0] ); break; case OR: case AND: @@ -59,7 +59,8 @@ bool QuantifiersRewriter::isLiteral( Node n ){ return false; break; case EQUAL: - return n[0].getType()!=NodeManager::currentNM()->booleanType(); + //for boolean terms + return !n[0].getType().isBoolean(); break; default: break; @@ -185,19 +186,10 @@ RewriteResponse QuantifiersRewriter::preRewrite(TNode in) { if( in.getKind()==kind::EXISTS || in.getKind()==kind::FORALL ){ Trace("quantifiers-rewrite-debug") << "pre-rewriting " << in << std::endl; std::vector< Node > args; - for( int i=0; i<(int)in[0].getNumChildren(); i++ ){ - args.push_back( in[0][i] ); - } - Node body = in[1]; + Node body = in; bool doRewrite = false; - std::vector< Node > ipl; - while( body.getNumChildren()>=2 && body.getKind()==in.getKind() ){ - if( body.getNumChildren()==3 ){ - for( unsigned i=0; i<body[2].getNumChildren(); i++ ){ - ipl.push_back( body[2][i] ); - } - } - for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + while( body.getNumChildren()==2 && body.getKind()==body[1].getKind() ){ + for( unsigned i=0; i<body[0].getNumChildren(); i++ ){ args.push_back( body[0][i] ); } body = body[1]; @@ -205,16 +197,11 @@ RewriteResponse QuantifiersRewriter::preRewrite(TNode in) { } if( doRewrite ){ std::vector< Node > children; + for( unsigned i=0; i<body[0].getNumChildren(); i++ ){ + args.push_back( body[0][i] ); + } children.push_back( NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST,args) ); - children.push_back( body ); - if( in.getNumChildren()==3 ){ - for( unsigned i=0; i<in[2].getNumChildren(); i++ ){ - ipl.push_back( in[2][i] ); - } - } - if( !ipl.empty() ){ - children.push_back( NodeManager::currentNM()->mkNode( INST_PATTERN_LIST, ipl ) ); - } + children.push_back( body[1] ); Node n = NodeManager::currentNM()->mkNode( in.getKind(), children ); if( in!=n ){ Trace("quantifiers-pre-rewrite") << "*** pre-rewrite " << in << std::endl; @@ -244,7 +231,7 @@ RewriteResponse QuantifiersRewriter::postRewrite(TNode in) { ret = ret.negate(); status = REWRITE_AGAIN_FULL; }else if( in.getKind()==FORALL ){ - if( in[1].isConst() ){ + if( in[1].isConst() && in.getNumChildren()==2 ){ return RewriteResponse( status, in[1] ); }else{ //compute attributes @@ -273,121 +260,98 @@ RewriteResponse QuantifiersRewriter::postRewrite(TNode in) { return RewriteResponse( status, ret ); } -Node QuantifiersRewriter::computeElimSymbols( Node body ) { - if( isLiteral( body ) ){ - return body; - }else{ - bool childrenChanged = false; - Kind k = body.getKind(); - if( body.getKind()==IMPLIES ){ - k = OR; - childrenChanged = true; - }else if( body.getKind()==XOR ){ - k = IFF; +bool QuantifiersRewriter::addCheckElimChild( std::vector< Node >& children, Node c, Kind k, std::map< Node, bool >& lit_pol, bool& childrenChanged ){ + if( ( k==OR || k==AND ) && options::elimTautQuant() ){ + Node lit = c.getKind()==NOT ? c[0] : c; + bool pol = c.getKind()!=NOT; + std::map< Node, bool >::iterator it = lit_pol.find( lit ); + if( it==lit_pol.end() ){ + lit_pol[lit] = pol; + children.push_back( c ); + }else{ childrenChanged = true; - } - std::vector< Node > children; - std::map< Node, bool > lit_pol; - for( unsigned i=0; i<body.getNumChildren(); i++ ){ - Node c = computeElimSymbols( body[i] ); - if( i==0 && ( body.getKind()==IMPLIES || body.getKind()==XOR ) ){ - c = c.negate(); - } - if( ( k==OR || k==AND ) && options::elimTautQuant() ){ - Node lit = c.getKind()==NOT ? c[0] : c; - bool pol = c.getKind()!=NOT; - std::map< Node, bool >::iterator it = lit_pol.find( lit ); - if( it==lit_pol.end() ){ - lit_pol[lit] = pol; - children.push_back( c ); - }else{ - childrenChanged = true; - if( it->second!=pol ){ - return NodeManager::currentNM()->mkConst( k==OR ); - } - } - }else{ - children.push_back( c ); + if( it->second!=pol ){ + return false; } - childrenChanged = childrenChanged || c!=body[i]; - } - //if( body.getKind()==ITE && isLiteral( body[0] ) ){ - // ret = NodeManager::currentNM()->mkNode( AND, NodeManager::currentNM()->mkNode( OR, body[0].negate(), body[1] ), - // NodeManager::currentNM()->mkNode( OR, body[0], body[2] ) ); - //} - if( childrenChanged ){ - return ( children.size()==1 && k!=NOT ) ? children[0] : NodeManager::currentNM()->mkNode( k, children ); - }else{ - return body; } + }else{ + children.push_back( c ); } + return true; } -Node QuantifiersRewriter::computeNNF( Node body ){ - if( body.getKind()==NOT ){ +// eliminates IMPLIES/XOR, removes duplicates/infers tautologies of AND/OR, and computes NNF +Node QuantifiersRewriter::computeElimSymbols( Node body ) { + Kind ok = body.getKind(); + Kind k = ok; + bool negAllCh = false; + bool negCh1 = false; + if( ok==IMPLIES ){ + k = OR; + negCh1 = true; + }else if( ok==XOR ){ + k = IFF; + negCh1 = true; + }else if( ok==NOT ){ if( body[0].getKind()==NOT ){ - return computeNNF( body[0][0] ); - }else if( isLiteral( body[0] ) ){ - return body; + return computeElimSymbols( body[0][0] ); + }else if( body[0].getKind()==OR || body[0].getKind()==IMPLIES ){ + k = AND; + negAllCh = true; + negCh1 = body[0].getKind()==IMPLIES; + body = body[0]; + }else if( body[0].getKind()==AND ){ + k = OR; + negAllCh = true; + body = body[0]; + }else if( body[0].getKind()==XOR || body[0].getKind()==IFF ){ + k = IFF; + negCh1 = ( body[0].getKind()==IFF ); + body = body[0]; + }else if( body[0].getKind()==ITE ){ + k = body[0].getKind(); + negAllCh = true; + negCh1 = true; + body = body[0]; }else{ - std::vector< Node > children; - Kind k = body[0].getKind(); - - if( body[0].getKind()==OR || body[0].getKind()==AND ){ - k = body[0].getKind()==AND ? OR : AND; - for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ - Node nc = computeNNF( body[0][i].notNode() ); - if( nc.getKind()==k ){ - for( unsigned j=0; j<nc.getNumChildren(); j++ ){ - children.push_back( nc[j] ); - } - }else{ - children.push_back( nc ); - } - } - }else if( body[0].getKind()==IFF ){ - for( int i=0; i<2; i++ ){ - Node nn = i==0 ? body[0][i] : body[0][i].notNode(); - children.push_back( computeNNF( nn ) ); - } - }else if( body[0].getKind()==ITE ){ - for( int i=0; i<3; i++ ){ - Node nn = i==0 ? body[0][i] : body[0][i].notNode(); - children.push_back( computeNNF( nn ) ); - } - }else{ - Notice() << "Unhandled Quantifiers NNF: " << body << std::endl; - return body; - } - return NodeManager::currentNM()->mkNode( k, children ); + return body; } - }else if( isLiteral( body ) ){ + }else if( ok!=IFF && ok!=ITE && ok!=AND && ok!=OR ){ + //a literal return body; - }else{ - std::vector< Node > children; - bool childrenChanged = false; - bool isAssoc = body.getKind()==AND || body.getKind()==OR; - for( int i=0; i<(int)body.getNumChildren(); i++ ){ - Node nc = computeNNF( body[i] ); - if( isAssoc && nc.getKind()==body.getKind() ){ - for( unsigned j=0; j<nc.getNumChildren(); j++ ){ - children.push_back( nc[j] ); + } + bool childrenChanged = false; + std::vector< Node > children; + std::map< Node, bool > lit_pol; + for( unsigned i=0; i<body.getNumChildren(); i++ ){ + Node c = computeElimSymbols( ( i==0 && negCh1 )!=negAllCh ? body[i].negate() : body[i] ); + bool success = true; + if( c.getKind()==k && ( k==OR || k==AND ) ){ + //flatten + childrenChanged = true; + for( unsigned j=0; j<c.getNumChildren(); j++ ){ + if( !addCheckElimChild( children, c[j], k, lit_pol, childrenChanged ) ){ + success = false; + break; } - childrenChanged = true; - }else{ - children.push_back( nc ); - childrenChanged = childrenChanged || nc!=body[i]; } - } - if( childrenChanged ){ - return NodeManager::currentNM()->mkNode( body.getKind(), children ); }else{ - return body; + success = addCheckElimChild( children, c, k, lit_pol, childrenChanged ); } + if( !success ){ + // tautology + Assert( k==OR || k==AND ); + return NodeManager::currentNM()->mkConst( k==OR ); + } + childrenChanged = childrenChanged || c!=body[i]; + } + if( childrenChanged || k!=ok ){ + return ( children.size()==1 && k!=NOT ) ? children[0] : NodeManager::currentNM()->mkNode( k, children ); + }else{ + return body; } } - void QuantifiersRewriter::computeDtTesterIteSplit( Node n, std::map< Node, Node >& pcons, std::map< Node, std::map< int, Node > >& ncons, std::vector< Node >& conj ){ if( n.getKind()==ITE && n[0].getKind()==APPLY_TESTER && n[1].getType().isBoolean() ){ @@ -463,6 +427,8 @@ int getEntailedCond( Node n, std::map< Node, bool >& currCond ){ std::map< Node, bool >::iterator it = currCond.find( n ); if( it!=currCond.end() ){ return it->second ? 1 : -1; + }else if( n.getKind()==NOT ){ + return -getEntailedCond( n[0], currCond ); }else if( n.getKind()==AND || n.getKind()==OR ){ bool hasZero = false; for( unsigned i=0; i<n.getNumChildren(); i++ ){ @@ -483,8 +449,7 @@ int getEntailedCond( Node n, std::map< Node, bool >& currCond ){ }else if( res==-1 ){ return getEntailedCond( n[2], currCond ); } - } - if( n.getKind()==IFF || n.getKind()==ITE ){ + }else if( n.getKind()==IFF || n.getKind()==ITE ){ unsigned start = n.getKind()==IFF ? 0 : 1; int res1 = 0; for( unsigned j=start; j<=(start+1); j++ ){ @@ -1049,144 +1014,6 @@ Node QuantifiersRewriter::computeVarElimination( Node body, std::vector< Node >& return body; } -Node QuantifiersRewriter::computeClause( Node n ){ - Assert( isClause( n ) ); - if( isLiteral( n ) ){ - return n; - }else{ - NodeBuilder<> t(OR); - if( n.getKind()==NOT ){ - if( n[0].getKind()==NOT ){ - return computeClause( n[0][0] ); - }else{ - //De-Morgan's law - Assert( n[0].getKind()==AND ); - for( int i=0; i<(int)n[0].getNumChildren(); i++ ){ - Node nn = computeClause( n[0][i].notNode() ); - addNodeToOrBuilder( nn, t ); - } - } - }else{ - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - Node nn = computeClause( n[i] ); - addNodeToOrBuilder( nn, t ); - } - } - return t.constructNode(); - } -} - -Node QuantifiersRewriter::computeCNF( Node n, std::vector< Node >& args, NodeBuilder<>& defs, bool forcePred ){ - if( isLiteral( n ) ){ - return n; - }else if( !forcePred && isClause( n ) ){ - return computeClause( n ); - }else{ - Kind k = n.getKind(); - NodeBuilder<> t(k); - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - Node nc = n[i]; - Node ncnf = computeCNF( nc, args, defs, k!=OR ); - if( k==OR ){ - addNodeToOrBuilder( ncnf, t ); - }else{ - t << ncnf; - } - } - if( !forcePred && k==OR ){ - return t.constructNode(); - }else{ - //compute the free variables - Node nt = t; - std::vector< Node > activeArgs; - computeArgVec( args, activeArgs, nt ); - std::vector< TypeNode > argTypes; - for( int i=0; i<(int)activeArgs.size(); i++ ){ - argTypes.push_back( activeArgs[i].getType() ); - } - //create the predicate - Assert( argTypes.size()>0 ); - TypeNode typ = NodeManager::currentNM()->mkFunctionType( argTypes, NodeManager::currentNM()->booleanType() ); - std::stringstream ss; - ss << "cnf_" << n.getKind() << "_" << n.getId(); - Node op = NodeManager::currentNM()->mkSkolem( ss.str(), typ, "was created by the quantifiers rewriter" ); - std::vector< Node > predArgs; - predArgs.push_back( op ); - predArgs.insert( predArgs.end(), activeArgs.begin(), activeArgs.end() ); - Node pred = NodeManager::currentNM()->mkNode( APPLY_UF, predArgs ); - Trace("quantifiers-rewrite-cnf-debug") << "Made predicate " << pred << " for " << nt << std::endl; - //create the bound var list - Node bvl = NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, activeArgs ); - //now, look at the structure of nt - if( nt.getKind()==NOT ){ - //case for NOT - for( int i=0; i<2; i++ ){ - NodeBuilder<> tt(OR); - tt << ( i==0 ? nt[0].notNode() : nt[0] ); - tt << ( i==0 ? pred.notNode() : pred ); - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - } - }else if( nt.getKind()==OR ){ - //case for OR - for( int i=0; i<(int)nt.getNumChildren(); i++ ){ - NodeBuilder<> tt(OR); - tt << nt[i].notNode() << pred; - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - } - NodeBuilder<> tt(OR); - for( int i=0; i<(int)nt.getNumChildren(); i++ ){ - tt << nt[i]; - } - tt << pred.notNode(); - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - }else if( nt.getKind()==AND ){ - //case for AND - for( int i=0; i<(int)nt.getNumChildren(); i++ ){ - NodeBuilder<> tt(OR); - tt << nt[i] << pred.notNode(); - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - } - NodeBuilder<> tt(OR); - for( int i=0; i<(int)nt.getNumChildren(); i++ ){ - tt << nt[i].notNode(); - } - tt << pred; - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - }else if( nt.getKind()==IFF ){ - //case for IFF - for( int i=0; i<4; i++ ){ - NodeBuilder<> tt(OR); - tt << ( ( i==0 || i==3 ) ? nt[0].notNode() : nt[0] ); - tt << ( ( i==1 || i==3 ) ? nt[1].notNode() : nt[1] ); - tt << ( ( i==0 || i==1 ) ? pred.notNode() : pred ); - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - } - }else if( nt.getKind()==ITE ){ - //case for ITE - for( int j=1; j<=2; j++ ){ - for( int i=0; i<2; i++ ){ - NodeBuilder<> tt(OR); - tt << ( ( j==1 ) ? nt[0].notNode() : nt[0] ); - tt << ( ( i==1 ) ? nt[j].notNode() : nt[j] ); - tt << ( ( i==0 ) ? pred.notNode() : pred ); - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - } - } - for( int i=0; i<2; i++ ){ - NodeBuilder<> tt(OR); - tt << ( i==0 ? nt[1].notNode() : nt[1] ); - tt << ( i==0 ? nt[2].notNode() : nt[2] ); - tt << ( i==1 ? pred.notNode() : pred ); - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - } - }else{ - Notice() << "Unhandled Quantifiers CNF: " << nt << std::endl; - } - return pred; - } - } -} - Node QuantifiersRewriter::computePrenex( Node body, std::vector< Node >& args, bool pol ){ if( body.getKind()==FORALL ){ if( pol && ( options::prenexQuant()==PRENEX_ALL || body.getNumChildren()==2 ) ){ @@ -1194,15 +1021,13 @@ Node QuantifiersRewriter::computePrenex( Node body, std::vector< Node >& args, b std::vector< Node > subs; //for doing prenexing of same-signed quantifiers //must rename each variable that already exists - for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ - //if( std::find( args.begin(), args.end(), body[0][i] )!=args.end() ){ + for( unsigned i=0; i<body[0].getNumChildren(); i++ ){ terms.push_back( body[0][i] ); subs.push_back( NodeManager::currentNM()->mkBoundVar( body[0][i].getType() ) ); } args.insert( args.end(), subs.begin(), subs.end() ); Node newBody = body[1]; newBody = newBody.substitute( terms.begin(), terms.end(), subs.begin(), subs.end() ); - Debug("quantifiers-substitute-debug") << "Did substitute have an effect" << (body[1] != newBody) << body[1] << " became " << newBody << endl; return newBody; }else{ return body; @@ -1211,7 +1036,7 @@ Node QuantifiersRewriter::computePrenex( Node body, std::vector< Node >& args, b Assert( body.getKind()!=EXISTS ); bool childrenChanged = false; std::vector< Node > newChildren; - for( int i=0; i<(int)body.getNumChildren(); i++ ){ + for( unsigned i=0; i<body.getNumChildren(); i++ ){ bool newHasPol; bool newPol; QuantPhaseReq::getPolarity( body, i, true, pol, newHasPol, newPol ); @@ -1237,7 +1062,7 @@ Node QuantifiersRewriter::computePrenex( Node body, std::vector< Node >& args, b } } -Node QuantifiersRewriter::computeSplit( Node f, std::vector< Node >& args, Node body ) { +Node QuantifiersRewriter::computeSplit( std::vector< Node >& args, Node body, QAttributes& qa ) { Assert( body.getKind()==OR ); size_t var_found_count = 0; size_t eqc_count = 0; @@ -1253,8 +1078,8 @@ Node QuantifiersRewriter::computeSplit( Node f, std::vector< Node >& args, Node Node n = body[i]; std::vector< Node > lit_args; computeArgVec( args, lit_args, n ); - if (lit_args.empty()) { - lits.push_back(n); + if( lit_args.empty() ){ + lits.push_back( n ); }else { //collect the equivalence classes this literal belongs to, and the new variables it contributes std::vector< int > eqcs; @@ -1306,8 +1131,8 @@ Node QuantifiersRewriter::computeSplit( Node f, std::vector< Node >& args, Node eqc_to_lit[eqcz].push_back(n); } } - if ( eqc_active>1 || !lits.empty() ){ - Trace("clause-split-debug") << "Split clause " << f << std::endl; + if ( eqc_active>1 || !lits.empty() || var_to_eqc.size()!=args.size() ){ + Trace("clause-split-debug") << "Split quantified formula with body " << body << std::endl; Trace("clause-split-debug") << " Ground literals: " << std::endl; for( size_t i=0; i<lits.size(); i++) { Trace("clause-split-debug") << " " << lits[i] << std::endl; @@ -1330,27 +1155,21 @@ Node QuantifiersRewriter::computeSplit( Node f, std::vector< Node >& args, Node Node fa = NodeManager::currentNM()->mkNode( FORALL, bvl, body ); lits.push_back(fa); } - Assert( lits.size()>1 ); - Node nf = NodeManager::currentNM()->mkNode(OR,lits); + Assert( !lits.empty() ); + Node nf = lits.size()==1 ? lits[0] : NodeManager::currentNM()->mkNode(OR,lits); Trace("clause-split-debug") << "Made node : " << nf << std::endl; return nf; + }else{ + return mkForAll( args, body, qa ); } - return f; } Node QuantifiersRewriter::mkForAll( std::vector< Node >& args, Node body, QAttributes& qa ){ - std::vector< Node > activeArgs; - //if cegqi is on, may be synthesis conjecture, in which case we want to keep all variables - if( options::ceGuidedInst() && qa.d_sygus ){ - activeArgs.insert( activeArgs.end(), args.begin(), args.end() ); - }else{ - computeArgVec2( args, activeArgs, body, qa.d_ipl ); - } - if( activeArgs.empty() ){ + if( args.empty() ){ return body; }else{ std::vector< Node > children; - children.push_back( NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, activeArgs ) ); + children.push_back( NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, args ) ); children.push_back( body ); if( !qa.d_ipl.isNull() ){ children.push_back( qa.d_ipl ); @@ -1359,82 +1178,59 @@ Node QuantifiersRewriter::mkForAll( std::vector< Node >& args, Node body, QAttri } } -Node QuantifiersRewriter::computeMiniscoping( Node f, std::vector< Node >& args, Node body, QAttributes& qa ){ +//computes miniscoping, also eliminates variables that do not occur free in body +Node QuantifiersRewriter::computeMiniscoping( std::vector< Node >& args, Node body, QAttributes& qa ){ if( body.getKind()==FORALL ){ - //combine arguments + //combine prenex std::vector< Node > newArgs; - for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + newArgs.insert( newArgs.end(), args.begin(), args.end() ); + for( unsigned i=0; i<body[0].getNumChildren(); i++ ){ newArgs.push_back( body[0][i] ); } - newArgs.insert( newArgs.end(), args.begin(), args.end() ); - return mkForAll( newArgs, body[ 1 ], qa ); - }else{ - if( body.getKind()==NOT ){ - //push not downwards - if( body[0].getKind()==NOT ){ - return computeMiniscoping( f, args, body[0][0], qa ); - }else if( body[0].getKind()==AND ){ - if( options::miniscopeQuantFreeVar() ){ - NodeBuilder<> t(kind::OR); - for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ - t << ( body[0][i].getKind()==NOT ? body[0][i][0] : body[0][i].notNode() ); - } - return computeMiniscoping( f, args, t.constructNode(), qa ); - } - }else if( body[0].getKind()==OR ){ - if( options::miniscopeQuant() ){ - NodeBuilder<> t(kind::AND); - for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ - Node trm = body[0][i].negate(); - t << computeMiniscoping( f, args, trm, qa ); - } - return t.constructNode(); + return mkForAll( newArgs, body[1], qa ); + }else if( body.getKind()==AND ){ + if( options::miniscopeQuant() ){ + //break apart + NodeBuilder<> t(kind::AND); + for( unsigned i=0; i<body.getNumChildren(); i++ ){ + t << computeMiniscoping( args, body[i], qa ); + } + Node retVal = t; + return retVal; + } + }else if( body.getKind()==OR ){ + if( options::quantSplit() ){ + //splitting subsumes free variable miniscoping, apply it with higher priority + return computeSplit( args, body, qa ); + }else if( options::miniscopeQuantFreeVar() ){ + Node newBody = body; + NodeBuilder<> body_split(kind::OR); + NodeBuilder<> tb(kind::OR); + for( unsigned i=0; i<body.getNumChildren(); i++ ){ + Node trm = body[i]; + if( TermDb::containsTerms( body[i], args ) ){ + tb << trm; + }else{ + body_split << trm; } } - }else if( body.getKind()==AND ){ - if( options::miniscopeQuant() ){ - //break apart - NodeBuilder<> t(kind::AND); - for( unsigned i=0; i<body.getNumChildren(); i++ ){ - t << computeMiniscoping( f, args, body[i], qa ); - } - Node retVal = t; - return retVal; - } - }else if( body.getKind()==OR ){ - if( options::quantSplit() ){ - //splitting subsumes free variable miniscoping, apply it with higher priority - Node nf = computeSplit( f, args, body ); - if( nf!=f ){ - return nf; - } - }else if( options::miniscopeQuantFreeVar() ){ - Node newBody = body; - NodeBuilder<> body_split(kind::OR); - NodeBuilder<> tb(kind::OR); - for( unsigned i=0; i<body.getNumChildren(); i++ ){ - Node trm = body[i]; - if( TermDb::containsTerms( body[i], args ) ){ - tb << trm; - }else{ - body_split << trm; - } - } - if( tb.getNumChildren()==0 ){ - return body_split; - }else if( body_split.getNumChildren()>0 ){ - newBody = tb.getNumChildren()==1 ? tb.getChild( 0 ) : tb; - body_split << mkForAll( args, newBody, qa ); - return body_split.getNumChildren()==1 ? body_split.getChild( 0 ) : body_split; - } + if( tb.getNumChildren()==0 ){ + return body_split; + }else if( body_split.getNumChildren()>0 ){ + newBody = tb.getNumChildren()==1 ? tb.getChild( 0 ) : tb; + std::vector< Node > activeArgs; + computeArgVec2( args, activeArgs, newBody, qa.d_ipl ); + body_split << mkForAll( activeArgs, newBody, qa ); + return body_split.getNumChildren()==1 ? body_split.getChild( 0 ) : body_split; } } + }else if( body.getKind()==NOT ){ + Assert( isLiteral( body[0] ) ); } - //if( body==f[1] ){ - // return f; - //}else{ - return mkForAll( args, body, qa ); - //} + //remove variables that don't occur + std::vector< Node > activeArgs; + computeArgVec2( args, activeArgs, body, qa.d_ipl ); + return mkForAll( activeArgs, body, qa ); } Node QuantifiersRewriter::computeAggressiveMiniscoping( std::vector< Node >& args, Node body ){ @@ -1552,19 +1348,14 @@ bool QuantifiersRewriter::doOperation( Node q, int computeOption, QAttributes& q return is_std; }else if( computeOption==COMPUTE_AGGRESSIVE_MINISCOPING ){ return options::aggressiveMiniscopeQuant() && is_std; - }else if( computeOption==COMPUTE_NNF ){ - return true; }else if( computeOption==COMPUTE_PROCESS_TERMS ){ - return true; - //return options::iteLiftQuant()!=ITE_LIFT_QUANT_MODE_NONE || options::iteCondVarSplitQuant(); + return options::condRewriteQuant(); }else if( computeOption==COMPUTE_COND_SPLIT ){ return ( options::iteDtTesterSplitQuant() || options::condVarSplitQuant() ) && !is_strict_trigger; }else if( computeOption==COMPUTE_PRENEX ){ return options::prenexQuant()!=PRENEX_NONE && !options::aggressiveMiniscopeQuant() && is_std; }else if( computeOption==COMPUTE_VAR_ELIMINATION ){ return ( options::varElimQuant() || options::dtVarExpandQuant() || options::purifyQuant() ) && is_std; - //}else if( computeOption==COMPUTE_CNF ){ - // return options::cnfQuant(); }else if( computeOption==COMPUTE_PURIFY_EXPAND ){ return options::purifyQuant() && is_std; }else{ @@ -1584,11 +1375,9 @@ Node QuantifiersRewriter::computeOperation( Node f, int computeOption, QAttribut n = computeElimSymbols( n ); }else if( computeOption==COMPUTE_MINISCOPING ){ //return directly - return computeMiniscoping( f, args, n, qa ); + return computeMiniscoping( args, n, qa ); }else if( computeOption==COMPUTE_AGGRESSIVE_MINISCOPING ){ return computeAggressiveMiniscoping( args, n ); - }else if( computeOption==COMPUTE_NNF ){ - n = computeNNF( n ); }else if( computeOption==COMPUTE_PROCESS_TERMS ){ std::vector< Node > new_conds; n = computeProcessTerms( n, args, new_conds, f, qa ); @@ -1602,9 +1391,6 @@ Node QuantifiersRewriter::computeOperation( Node f, int computeOption, QAttribut n = computePrenex( n, args, true ); }else if( computeOption==COMPUTE_VAR_ELIMINATION ){ n = computeVarElimination( n, args, qa ); - //}else if( computeOption==COMPUTE_CNF ){ - //n = computeCNF( n, args, defs, false ); - //ipl = Node::null(); }else if( computeOption==COMPUTE_PURIFY_EXPAND ){ std::vector< Node > conj; computePurifyExpand( n, conj, args, qa ); @@ -1738,15 +1524,15 @@ struct ContainsQuantAttributeId {}; typedef expr::Attribute<ContainsQuantAttributeId, uint64_t> ContainsQuantAttribute; // check if the given node contains a universal quantifier -bool QuantifiersRewriter::containsQuantifiers(Node n) { +bool QuantifiersRewriter::containsQuantifiers( Node n ){ if( n.hasAttribute(ContainsQuantAttribute()) ){ return n.getAttribute(ContainsQuantAttribute())==1; - } else if(n.getKind() == kind::FORALL) { + }else if( n.getKind() == kind::FORALL ){ return true; - } else { + }else{ bool cq = false; for( unsigned i = 0; i < n.getNumChildren(); ++i ){ - if( containsQuantifiers(n[i]) ){ + if( containsQuantifiers( n[i] ) ){ cq = true; break; } diff --git a/src/theory/quantifiers/quantifiers_rewriter.h b/src/theory/quantifiers/quantifiers_rewriter.h index 2071d1793..776517109 100644..100755 --- a/src/theory/quantifiers/quantifiers_rewriter.h +++ b/src/theory/quantifiers/quantifiers_rewriter.h @@ -38,6 +38,7 @@ public: static int getPurifyId( Node n ); static int getPurifyIdLit( Node n ); private: + static bool addCheckElimChild( std::vector< Node >& children, Node c, Kind k, std::map< Node, bool >& lit_pol, bool& childrenChanged ); static void addNodeToOrBuilder( Node n, NodeBuilder<>& t ); static Node mkForAll( std::vector< Node >& args, Node body, QAttributes& qa ); static void computeArgs( std::vector< Node >& args, std::map< Node, bool >& activeMap, Node n, std::map< Node, bool >& visited ); @@ -46,7 +47,6 @@ private: static Node computeProcessTerms2( Node body, bool hasPol, bool pol, std::map< Node, bool >& currCond, int nCurrCond, std::map< Node, Node >& cache, std::map< Node, Node >& icache, std::vector< Node >& new_vars, std::vector< Node >& new_conds ); - static Node computeClause( Node n ); static void computeDtTesterIteSplit( Node n, std::map< Node, Node >& pcons, std::map< Node, std::map< int, Node > >& ncons, std::vector< Node >& conj ); static bool isConditionalVariableElim( Node n, int pol=0 ); static bool isVariableElim( Node v, Node s, std::map< Node, std::vector< int > >& var_parent ); @@ -57,15 +57,13 @@ private: static Node computeVarElimination2( Node body, std::vector< Node >& args, QAttributes& qa, std::map< Node, std::vector< int > >& var_parent ); private: static Node computeElimSymbols( Node body ); - static Node computeMiniscoping( Node f, std::vector< Node >& args, Node body, QAttributes& qa ); + static Node computeMiniscoping( std::vector< Node >& args, Node body, QAttributes& qa ); static Node computeAggressiveMiniscoping( std::vector< Node >& args, Node body ); - static Node computeNNF( Node body ); //cache is dependent upon currCond, icache is not, new_conds are negated conditions static Node computeProcessTerms( Node body, std::vector< Node >& new_vars, std::vector< Node >& new_conds, Node q, QAttributes& qa ); static Node computeCondSplit( Node body, QAttributes& qa ); - static Node computeCNF( Node body, std::vector< Node >& args, NodeBuilder<>& defs, bool forcePred ); static Node computePrenex( Node body, std::vector< Node >& args, bool pol ); - static Node computeSplit( Node f, std::vector< Node >& args, Node body ); + static Node computeSplit( std::vector< Node >& args, Node body, QAttributes& qa ); static Node computeVarElimination( Node body, std::vector< Node >& args, QAttributes& qa ); static Node computePurify( Node body, std::vector< Node >& args, std::map< Node, std::vector< int > >& var_parent ); static void computePurifyExpand( Node body, std::vector< Node >& conj, std::vector< Node >& args, QAttributes& qa ); @@ -74,7 +72,6 @@ private: COMPUTE_ELIM_SYMBOLS = 0, COMPUTE_MINISCOPING, COMPUTE_AGGRESSIVE_MINISCOPING, - COMPUTE_NNF, COMPUTE_PROCESS_TERMS, COMPUTE_PRENEX, COMPUTE_VAR_ELIMINATION, diff --git a/src/theory/quantifiers/relevant_domain.cpp b/src/theory/quantifiers/relevant_domain.cpp index b353fce2f..b4b51fd84 100644..100755 --- a/src/theory/quantifiers/relevant_domain.cpp +++ b/src/theory/quantifiers/relevant_domain.cpp @@ -110,7 +110,7 @@ void RelevantDomain::compute(){ for( unsigned i=0; i<sz; i++ ){ Node n = it->second[i]; //if it is a non-redundant term - if( !n.getAttribute(NoMatchAttribute()) ){ + if( db->isTermActive( n ) ){ for( unsigned j=0; j<n.getNumChildren(); j++ ){ RDomain * rf = getRDomain( op, j ); rf->addTerm( n[j] ); diff --git a/src/theory/quantifiers/relevant_domain.h b/src/theory/quantifiers/relevant_domain.h index 2b90520fd..2b90520fd 100644..100755 --- a/src/theory/quantifiers/relevant_domain.h +++ b/src/theory/quantifiers/relevant_domain.h diff --git a/src/theory/quantifiers/rewrite_engine.cpp b/src/theory/quantifiers/rewrite_engine.cpp index 5365dbcfa..2c58b8f77 100644..100755 --- a/src/theory/quantifiers/rewrite_engine.cpp +++ b/src/theory/quantifiers/rewrite_engine.cpp @@ -175,7 +175,7 @@ int RewriteEngine::checkRewriteRule( Node f, Theory::Effort e ) { for( unsigned j=0; j<to_remove.size(); j++ ){ Node ns = d_quantEngine->getSubstitute( to_remove[j], inst ); Trace("rewrite-engine-inst-debug") << "Will remove : " << ns << std::endl; - ns.setAttribute(NoMatchAttribute(),true); + d_quantEngine->getTermDatabase()->setTermInactive( ns ); } */ }else{ diff --git a/src/theory/quantifiers/rewrite_engine.h b/src/theory/quantifiers/rewrite_engine.h index 424530696..424530696 100644..100755 --- a/src/theory/quantifiers/rewrite_engine.h +++ b/src/theory/quantifiers/rewrite_engine.h diff --git a/src/theory/quantifiers/symmetry_breaking.cpp b/src/theory/quantifiers/symmetry_breaking.cpp index 2a2b13583..2a2b13583 100644..100755 --- a/src/theory/quantifiers/symmetry_breaking.cpp +++ b/src/theory/quantifiers/symmetry_breaking.cpp diff --git a/src/theory/quantifiers/symmetry_breaking.h b/src/theory/quantifiers/symmetry_breaking.h index 38fea4f45..e682955e7 100644..100755 --- a/src/theory/quantifiers/symmetry_breaking.h +++ b/src/theory/quantifiers/symmetry_breaking.h @@ -42,9 +42,7 @@ class SubsortSymmetryBreaker { typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap; typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap; typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; - //typedef context::CDChunkList<int> IntList; typedef context::CDList<Node> NodeList; - typedef context::CDHashMap<Node, NodeList*, NodeHashFunction> NodeListMap; private: /** quantifiers engine */ QuantifiersEngine* d_qe; diff --git a/src/theory/quantifiers/term_database.cpp b/src/theory/quantifiers/term_database.cpp index 8b09d8e5d..d3a5e178f 100644..100755 --- a/src/theory/quantifiers/term_database.cpp +++ b/src/theory/quantifiers/term_database.cpp @@ -84,13 +84,13 @@ void TermArgTrie::debugPrint( const char * c, Node n, unsigned depth ) { } } -TermDb::TermDb( context::Context* c, context::UserContext* u, QuantifiersEngine* qe ) : d_quantEngine( qe ), d_op_id_count( 0 ), d_typ_id_count( 0 ) { +TermDb::TermDb( context::Context* c, context::UserContext* u, QuantifiersEngine* qe ) : d_quantEngine( qe ), d_inactive_map( c ), d_op_id_count( 0 ), d_typ_id_count( 0 ) { d_true = NodeManager::currentNM()->mkConst( true ); d_false = NodeManager::currentNM()->mkConst( false ); d_zero = NodeManager::currentNM()->mkConst( Rational( 0 ) ); d_one = NodeManager::currentNM()->mkConst( Rational( 1 ) ); if( options::ceGuidedInst() ){ - d_sygus_tdb = new TermDbSygus; + d_sygus_tdb = new TermDbSygus( c, qe ); }else{ d_sygus_tdb = NULL; } @@ -163,9 +163,18 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant, bool wi //if this is an atomic trigger, consider adding it if( inst::Trigger::isAtomicTrigger( n ) ){ Trace("term-db") << "register term in db " << n << std::endl; + if( options::finiteModelFind() ){ + computeModelBasisArgAttribute( n ); + } + Node op = getMatchOperator( n ); + Trace("term-db-debug") << " match operator is : " << op << std::endl; d_op_map[op].push_back( n ); added.insert( n ); + + if( d_sygus_tdb ){ + d_sygus_tdb->registerEvalTerm( n ); + } if( options::eagerInstQuant() ){ for( unsigned i=0; i<n.getNumChildren(); i++ ){ @@ -178,6 +187,8 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant, bool wi } } } + }else{ + setTermInactive( n ); } rec = true; } @@ -210,7 +221,7 @@ void TermDb::computeUfEqcTerms( TNode f ) { for( unsigned i=0; i<d_op_map[f].size(); i++ ){ TNode n = d_op_map[f][i]; if( hasTermCurrent( n ) ){ - if( !n.getAttribute(NoMatchAttribute()) ){ + if( isTermActive( n ) ){ computeArgReps( n ); TNode r = ee->hasTerm( n ) ? ee->getRepresentative( n ) : n; d_func_map_eqc_trie[f].d_data[r].addTerm( n, d_arg_reps[n] ); @@ -220,7 +231,84 @@ void TermDb::computeUfEqcTerms( TNode f ) { } } +void TermDb::computeUfTerms( TNode f ) { + if( d_op_nonred_count.find( f )==d_op_nonred_count.end() ){ + d_op_nonred_count[ f ] = 0; + std::map< Node, std::vector< Node > >::iterator it = d_op_map.find( f ); + if( it!=d_op_map.end() ){ + eq::EqualityEngine* ee = d_quantEngine->getMasterEqualityEngine(); + Trace("term-db-debug") << "Adding terms for operator " << f << std::endl; + unsigned congruentCount = 0; + unsigned nonCongruentCount = 0; + unsigned alreadyCongruentCount = 0; + unsigned relevantCount = 0; + for( unsigned i=0; i<it->second.size(); i++ ){ + Node n = it->second[i]; + //to be added to term index, term must be relevant, and exist in EE + if( hasTermCurrent( n ) && ee->hasTerm( n ) ){ + relevantCount++; + if( isTermActive( n ) ){ + computeArgReps( n ); + + Trace("term-db-debug") << "Adding term " << n << " with arg reps : "; + for( unsigned i=0; i<d_arg_reps[n].size(); i++ ){ + Trace("term-db-debug") << d_arg_reps[n][i] << " "; + if( std::find( d_func_map_rel_dom[f][i].begin(), + d_func_map_rel_dom[f][i].end(), d_arg_reps[n][i] ) == d_func_map_rel_dom[f][i].end() ){ + d_func_map_rel_dom[f][i].push_back( d_arg_reps[n][i] ); + } + } + Trace("term-db-debug") << std::endl; + Trace("term-db-debug") << " and value : " << ee->getRepresentative( n ) << std::endl; + Node at = d_func_map_trie[ f ].addOrGetTerm( n, d_arg_reps[n] ); + Trace("term-db-debug2") << "...add term returned " << at << std::endl; + if( at!=n && ee->areEqual( at, n ) ){ + setTermInactive( n ); + Trace("term-db-debug") << n << " is redundant." << std::endl; + congruentCount++; + }else{ + if( at!=n && ee->areDisequal( at, n, false ) ){ + std::vector< Node > lits; + lits.push_back( NodeManager::currentNM()->mkNode( at.getType().isBoolean() ? IFF : EQUAL, at, n ) ); + for( unsigned i=0; i<at.getNumChildren(); i++ ){ + if( at[i]!=n[i] ){ + lits.push_back( NodeManager::currentNM()->mkNode( at[i].getType().isBoolean() ? IFF : EQUAL, at[i], n[i] ).negate() ); + } + } + Node lem = lits.size()==1 ? lits[0] : NodeManager::currentNM()->mkNode( OR, lits ); + if( Trace.isOn("term-db-lemma") ){ + Trace("term-db-lemma") << "Disequal congruent terms : " << at << " " << n << "!!!!" << std::endl; + if( !d_quantEngine->getTheoryEngine()->needCheck() ){ + Trace("term-db-lemma") << " all theories passed with no lemmas." << std::endl; + } + Trace("term-db-lemma") << " add lemma : " << lem << std::endl; + } + d_quantEngine->addLemma( lem ); + d_consistent_ee = false; + return; + } + nonCongruentCount++; + d_op_nonred_count[ f ]++; + } + }else{ + Trace("term-db-debug") << n << " is already redundant." << std::endl; + alreadyCongruentCount++; + } + }else{ + Trace("term-db-debug") << n << " is not relevant." << std::endl; + } + } + if( Trace.isOn("tdb") ){ + Trace("tdb") << "Term db size [" << f << "] : " << nonCongruentCount << " / "; + Trace("tdb") << ( nonCongruentCount + congruentCount ) << " / " << ( nonCongruentCount + congruentCount + alreadyCongruentCount ) << " / "; + Trace("tdb") << relevantCount << " / " << it->second.size() << std::endl; + } + } + } +} + bool TermDb::inRelevantDomain( TNode f, unsigned i, TNode r ) { + computeUfTerms( f ); Assert( d_quantEngine->getTheoryEngine()->getMasterEqualityEngine()->getRepresentative( r )==r ); std::map< Node, std::map< unsigned, std::vector< Node > > >::iterator it = d_func_map_rel_dom.find( f ); if( it != d_func_map_rel_dom.end() ){ @@ -237,25 +325,23 @@ bool TermDb::inRelevantDomain( TNode f, unsigned i, TNode r ) { //return a term n' equivalent to n // maximal subterms of n' are representatives in the equality engine qy -Node TermDb::evaluateTerm2( TNode n, std::map< TNode, Node >& visited, EqualityQuery * qy ) { +Node TermDb::evaluateTerm2( TNode n, std::map< TNode, Node >& visited, EqualityQuery * qy, bool useEntailmentTests ) { std::map< TNode, Node >::iterator itv = visited.find( n ); if( itv != visited.end() ){ return itv->second; } Trace("term-db-eval") << "evaluate term : " << n << std::endl; - Node ret; - if( n.getKind()==BOUND_VARIABLE ){ - return n; + Node ret = n; + if( n.getKind()==FORALL || n.getKind()==BOUND_VARIABLE ){ + //do nothing }else if( !qy->hasTerm( n ) ){ //term is not known to be equal to a representative in equality engine, evaluate it - if( n.getKind()==FORALL ){ - ret = Node::null(); - }else if( n.hasOperator() ){ + if( n.hasOperator() ){ TNode f = getMatchOperator( n ); std::vector< TNode > args; bool ret_set = false; for( unsigned i=0; i<n.getNumChildren(); i++ ){ - TNode c = evaluateTerm2( n[i], visited, qy ); + TNode c = evaluateTerm2( n[i], visited, qy, useEntailmentTests ); if( c.isNull() ){ ret = Node::null(); ret_set = true; @@ -267,7 +353,7 @@ Node TermDb::evaluateTerm2( TNode n, std::map< TNode, Node >& visited, EqualityQ ret_set = true; break; }else if( n.getKind()==kind::ITE && i==0 ){ - ret = evaluateTerm2( n[ c==d_true ? 1 : 2], visited, qy ); + ret = evaluateTerm2( n[ c==d_true ? 1 : 2], visited, qy, useEntailmentTests ); ret_set = true; break; } @@ -295,6 +381,22 @@ Node TermDb::evaluateTerm2( TNode n, std::map< TNode, Node >& visited, EqualityQ } ret = NodeManager::currentNM()->mkNode( n.getKind(), args ); ret = Rewriter::rewrite( ret ); + if( ret.getKind()==kind::EQUAL ){ + if( qy->areDisequal( ret[0], ret[1] ) ){ + ret = d_false; + } + } + if( useEntailmentTests ){ + if( ret.getKind()==kind::EQUAL || ret.getKind()==kind::GEQ ){ + for( unsigned j=0; j<2; j++ ){ + std::pair<bool, Node> et = d_quantEngine->getTheoryEngine()->entailmentCheck(THEORY_OF_TYPE_BASED, j==0 ? ret : ret.negate() ); + if( et.first ){ + ret = j==0 ? d_true : d_false; + break; + } + } + } + } } } } @@ -316,14 +418,16 @@ TNode TermDb::getEntailedTerm2( TNode n, std::map< TNode, TNode >& subs, bool su return n; }else if( n.getKind()==BOUND_VARIABLE ){ if( hasSubs ){ - Assert( subs.find( n )!=subs.end() ); - Trace("term-db-entail") << "...substitution is : " << subs[n] << std::endl; - if( subsRep ){ - Assert( qy->getEngine()->hasTerm( subs[n] ) ); - Assert( qy->getEngine()->getRepresentative( subs[n] )==subs[n] ); - return subs[n]; - }else{ - return getEntailedTerm2( subs[n], subs, subsRep, hasSubs, qy ); + std::map< TNode, TNode >::iterator it = subs.find( n ); + if( it!=subs.end() ){ + Trace("term-db-entail") << "...substitution is : " << it->second << std::endl; + if( subsRep ){ + Assert( qy->getEngine()->hasTerm( it->second ) ); + Assert( qy->getEngine()->getRepresentative( it->second )==it->second ); + return it->second; + }else{ + return getEntailedTerm2( it->second, subs, subsRep, hasSubs, qy ); + } } } }else if( n.getKind()==ITE ){ @@ -355,12 +459,12 @@ TNode TermDb::getEntailedTerm2( TNode n, std::map< TNode, TNode >& subs, bool su return TNode::null(); } -Node TermDb::evaluateTerm( TNode n, EqualityQuery * qy ) { +Node TermDb::evaluateTerm( TNode n, EqualityQuery * qy, bool useEntailmentTests ) { if( qy==NULL ){ qy = d_quantEngine->getEqualityQuery(); } std::map< TNode, Node > visited; - return evaluateTerm2( n, visited, qy ); + return evaluateTerm2( n, visited, qy, useEntailmentTests ); } TNode TermDb::getEntailedTerm( TNode n, std::map< TNode, TNode >& subs, bool subsRep, EqualityQuery * qy ) { @@ -436,6 +540,8 @@ bool TermDb::isEntailed2( TNode n, std::map< TNode, TNode >& subs, bool subsRep, return qy->getEngine()->getRepresentative( n1 ) == ( pol ? d_true : d_false ); } } + }else if( n.getKind()==FORALL && !pol ){ + return isEntailed2( n[1], subs, subsRep, hasSubs, pol, qy ); } return false; } @@ -457,6 +563,18 @@ bool TermDb::isEntailed( TNode n, std::map< TNode, TNode >& subs, bool subsRep, return isEntailed2( n, subs, subsRep, true, pol, qy ); } +bool TermDb::isTermActive( Node n ) { + return d_inactive_map.find( n )==d_inactive_map.end(); + //return !n.getAttribute(NoMatchAttribute()); +} + +void TermDb::setTermInactive( Node n ) { + d_inactive_map[n] = true; + //Trace("term-db-debug2") << "set no match attribute" << std::endl; + //NoMatchAttribute nma; + //n.setAttribute(nma,true); +} + bool TermDb::hasTermCurrent( Node n, bool useMode ) { if( !useMode ){ return d_has_map.find( n )!=d_has_map.end(); @@ -556,10 +674,6 @@ void TermDb::presolve() { } bool TermDb::reset( Theory::Effort effort ){ - int nonCongruentCount = 0; - int congruentCount = 0; - int alreadyCongruentCount = 0; - int nonRelevantCount = 0; d_op_nonred_count.clear(); d_arg_reps.clear(); d_func_map_trie.clear(); @@ -614,89 +728,20 @@ bool TermDb::reset( Theory::Effort effort ){ } } +/* //rebuild d_func/pred_map_trie for each operation, this will calculate all congruent terms for( std::map< Node, std::vector< Node > >::iterator it = d_op_map.begin(); it != d_op_map.end(); ++it ){ - d_op_nonred_count[ it->first ] = 0; - Trace("term-db-debug") << "Adding terms for operator " << it->first << std::endl; - for( unsigned i=0; i<it->second.size(); i++ ){ - Node n = it->second[i]; - //to be added to term index, term must be relevant, and exist in EE - if( hasTermCurrent( n ) && ee->hasTerm( n ) ){ - if( !n.getAttribute(NoMatchAttribute()) ){ - if( options::finiteModelFind() ){ - computeModelBasisArgAttribute( n ); - } - computeArgReps( n ); - - Trace("term-db-debug") << "Adding term " << n << " with arg reps : "; - for( unsigned i=0; i<d_arg_reps[n].size(); i++ ){ - Trace("term-db-debug") << d_arg_reps[n][i] << " "; - if( std::find( d_func_map_rel_dom[it->first][i].begin(), - d_func_map_rel_dom[it->first][i].end(), d_arg_reps[n][i] ) == d_func_map_rel_dom[it->first][i].end() ){ - d_func_map_rel_dom[it->first][i].push_back( d_arg_reps[n][i] ); - } - } - Trace("term-db-debug") << std::endl; - if( ee->hasTerm( n ) ){ - Trace("term-db-debug") << " and value : " << ee->getRepresentative( n ) << std::endl; - } - Node at = d_func_map_trie[ it->first ].addOrGetTerm( n, d_arg_reps[n] ); - if( at!=n && ee->areEqual( at, n ) ){ - NoMatchAttribute nma; - n.setAttribute(nma,true); - Trace("term-db-debug") << n << " is redundant." << std::endl; - congruentCount++; - }else{ - if( at!=n && ee->areDisequal( at, n, false ) ){ - std::vector< Node > lits; - lits.push_back( NodeManager::currentNM()->mkNode( at.getType().isBoolean() ? IFF : EQUAL, at, n ) ); - for( unsigned i=0; i<at.getNumChildren(); i++ ){ - if( at[i]!=n[i] ){ - lits.push_back( NodeManager::currentNM()->mkNode( at[i].getType().isBoolean() ? IFF : EQUAL, at[i], n[i] ).negate() ); - } - } - Node lem = lits.size()==1 ? lits[0] : NodeManager::currentNM()->mkNode( OR, lits ); - if( Trace.isOn("term-db-lemma") ){ - Trace("term-db-lemma") << "Disequal congruent terms : " << at << " " << n << "!!!!" << std::endl; - if( !d_quantEngine->getTheoryEngine()->needCheck() ){ - Trace("term-db-lemma") << " all theories passed with no lemmas." << std::endl; - } - Trace("term-db-lemma") << " add lemma : " << lem << std::endl; - } - d_quantEngine->addLemma( lem ); - d_consistent_ee = false; - return false; - } - nonCongruentCount++; - d_op_nonred_count[ it->first ]++; - } - }else{ - Trace("term-db-debug") << n << " is already redundant." << std::endl; - congruentCount++; - alreadyCongruentCount++; - } - }else{ - Trace("term-db-debug") << n << " is not relevant." << std::endl; - nonRelevantCount++; - } - } - } - Trace("term-db-stats") << "TermDb: Reset" << std::endl; - Trace("term-db-stats") << "Non-Congruent/Congruent/Non-Relevant = "; - Trace("term-db-stats") << nonCongruentCount << " / " << congruentCount << " (" << alreadyCongruentCount << ") / " << nonRelevantCount << std::endl; - if( Trace.isOn("term-db-index") ){ - Trace("term-db-index") << "functions : " << std::endl; - for( std::map< Node, std::vector< Node > >::iterator it = d_op_map.begin(); it != d_op_map.end(); ++it ){ - if( it->second.size()>0 ){ - Trace("term-db-index") << "- " << it->first << std::endl; - d_func_map_trie[ it->first ].debugPrint("term-db-index", it->second[0]); - } + computeUfTerms( it->first ); + if( !d_consistent_ee ){ + return false; } } +*/ return true; } TermArgTrie * TermDb::getTermArgTrie( Node f ) { + computeUfTerms( f ); std::map< Node, TermArgTrie >::iterator itut = d_func_map_trie.find( f ); if( itut!=d_func_map_trie.end() ){ return &itut->second; @@ -725,11 +770,18 @@ TermArgTrie * TermDb::getTermArgTrie( Node eqc, Node f ) { } TNode TermDb::getCongruentTerm( Node f, Node n ) { - computeArgReps( n ); - return d_func_map_trie[f].existsTerm( d_arg_reps[n] ); + computeUfTerms( f ); + std::map< Node, TermArgTrie >::iterator itut = d_func_map_trie.find( f ); + if( itut!=d_func_map_trie.end() ){ + computeArgReps( n ); + return itut->second.existsTerm( d_arg_reps[n] ); + }else{ + return TNode::null(); + } } TNode TermDb::getCongruentTerm( Node f, std::vector< TNode >& args ) { + computeUfTerms( f ); return d_func_map_trie[f].existsTerm( args ); } @@ -822,6 +874,7 @@ void TermDb::makeInstantiationConstantsFor( Node q ){ Debug("quantifiers-engine") << "Instantiation constants for " << q << " : " << std::endl; for( unsigned i=0; i<q[0].getNumChildren(); i++ ){ d_vars[q].push_back( q[0][i] ); + d_var_num[q][q[0][i]] = i; //make instantiation constants Node ic = NodeManager::currentNM()->mkInstConstant( q[0][i].getType() ); d_inst_constants_map[ic] = q; @@ -832,9 +885,8 @@ void TermDb::makeInstantiationConstantsFor( Node q ){ ic.setAttribute( ivna, i ); InstConstantAttribute ica; ic.setAttribute( ica, q ); - //also set the no-match attribute - NoMatchAttribute nma; - ic.setAttribute(nma,true); + //also set the term inactive + setTermInactive( ic ); } } } @@ -892,11 +944,6 @@ Node TermDb::getInstConstAttr( Node n ) { } InstConstantAttribute ica; n.setAttribute(ica, q); - if( !q.isNull() ){ - //also set the no-match attribute - NoMatchAttribute nma; - n.setAttribute(nma,true); - } } return n.getAttribute(InstConstantAttribute()); } @@ -1005,9 +1052,12 @@ Node TermDb::getCounterexampleLiteral( Node q ){ Node TermDb::getInstConstantNode( Node n, Node q ){ makeInstantiationConstantsFor( q ); - Node n2 = n.substitute( d_vars[q].begin(), d_vars[q].end(), - d_inst_constants[q].begin(), d_inst_constants[q].end() ); - return n2; + return n.substitute( d_vars[q].begin(), d_vars[q].end(), d_inst_constants[q].begin(), d_inst_constants[q].end() ); +} + +Node TermDb::getVariableNode( Node n, Node q ) { + makeInstantiationConstantsFor( q ); + return n.substitute( d_inst_constants[q].begin(), d_inst_constants[q].end(), d_vars[q].begin(), d_vars[q].end() ); } Node TermDb::getInstantiatedNode( Node n, Node q, std::vector< Node >& terms ) { @@ -1512,7 +1562,7 @@ struct sortTermOrder { //this function makes a canonical representation of a term ( // - orders variables left to right // - if apply_torder, then sort direct subterms of commutative operators -Node TermDb::getCanonicalTerm( TNode n, std::map< TypeNode, unsigned >& var_count, std::map< TNode, TNode >& subs, bool apply_torder ) { +Node TermDb::getCanonicalTerm( TNode n, std::map< TypeNode, unsigned >& var_count, std::map< TNode, TNode >& subs, bool apply_torder, std::map< TNode, Node >& visited ) { Trace("canon-term-debug") << "Get canonical term for " << n << std::endl; if( n.getKind()==BOUND_VARIABLE ){ std::map< TNode, TNode >::iterator it = subs.find( n ); @@ -1529,32 +1579,38 @@ Node TermDb::getCanonicalTerm( TNode n, std::map< TypeNode, unsigned >& var_coun return it->second; } }else if( n.getNumChildren()>0 ){ - //collect children - Trace("canon-term-debug") << "Collect children" << std::endl; - std::vector< Node > cchildren; - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - cchildren.push_back( n[i] ); - } - //if applicable, first sort by term order - if( apply_torder && isComm( n.getKind() ) ){ - Trace("canon-term-debug") << "Sort based on commutative operator " << n.getKind() << std::endl; - sortTermOrder sto; - sto.d_tdb = this; - std::sort( cchildren.begin(), cchildren.end(), sto ); - } - //now make canonical - Trace("canon-term-debug") << "Make canonical children" << std::endl; - for( unsigned i=0; i<cchildren.size(); i++ ){ - cchildren[i] = getCanonicalTerm( cchildren[i], var_count, subs, apply_torder ); - } - if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ - Trace("canon-term-debug") << "Insert operator " << n.getOperator() << std::endl; - cchildren.insert( cchildren.begin(), n.getOperator() ); - } - Trace("canon-term-debug") << "...constructing for " << n << "." << std::endl; - Node ret = NodeManager::currentNM()->mkNode( n.getKind(), cchildren ); - Trace("canon-term-debug") << "...constructed " << ret << " for " << n << "." << std::endl; - return ret; + std::map< TNode, Node >::iterator it = visited.find( n ); + if( it!=visited.end() ){ + return it->second; + }else{ + //collect children + Trace("canon-term-debug") << "Collect children" << std::endl; + std::vector< Node > cchildren; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + cchildren.push_back( n[i] ); + } + //if applicable, first sort by term order + if( apply_torder && isComm( n.getKind() ) ){ + Trace("canon-term-debug") << "Sort based on commutative operator " << n.getKind() << std::endl; + sortTermOrder sto; + sto.d_tdb = this; + std::sort( cchildren.begin(), cchildren.end(), sto ); + } + //now make canonical + Trace("canon-term-debug") << "Make canonical children" << std::endl; + for( unsigned i=0; i<cchildren.size(); i++ ){ + cchildren[i] = getCanonicalTerm( cchildren[i], var_count, subs, apply_torder, visited ); + } + if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ + Trace("canon-term-debug") << "Insert operator " << n.getOperator() << std::endl; + cchildren.insert( cchildren.begin(), n.getOperator() ); + } + Trace("canon-term-debug") << "...constructing for " << n << "." << std::endl; + Node ret = NodeManager::currentNM()->mkNode( n.getKind(), cchildren ); + Trace("canon-term-debug") << "...constructed " << ret << " for " << n << "." << std::endl; + visited[n] = ret; + return ret; + } }else{ Trace("canon-term-debug") << "...return 0-child term." << std::endl; return n; @@ -1564,7 +1620,8 @@ Node TermDb::getCanonicalTerm( TNode n, std::map< TypeNode, unsigned >& var_coun Node TermDb::getCanonicalTerm( TNode n, bool apply_torder ){ std::map< TypeNode, unsigned > var_count; std::map< TNode, TNode > subs; - return getCanonicalTerm( n, var_count, subs, apply_torder ); + std::map< TNode, Node > visited; + return getCanonicalTerm( n, var_count, subs, apply_torder, visited ); } void TermDb::getVtsTerms( std::vector< Node >& t, bool isFree, bool create, bool inc_delta ) { @@ -1790,6 +1847,18 @@ bool TermDb::getEnsureTypeCondition( Node n, TypeNode tn, std::vector< Node >& c } } +void TermDb::getRelevancyCondition( Node n, std::vector< Node >& cond ) { + if( n.getKind()==APPLY_SELECTOR_TOTAL ){ + unsigned scindex = Datatype::cindexOf(n.getOperator().toExpr()); + const Datatype& dt = ((DatatypeType)(n[0].getType()).toType()).getDatatype(); + Node rc = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[scindex].getTester() ), n[0] ).negate(); + if( std::find( cond.begin(), cond.end(), rc )==cond.end() ){ + cond.push_back( rc ); + } + getRelevancyCondition( n[0], cond ); + } +} + bool TermDb::containsTerm2( Node n, Node t, std::map< Node, bool >& visited ) { if( n==t ){ return true; @@ -1856,7 +1925,7 @@ bool TermDb::containsUninterpretedConstant( Node n ) { bool ret = false; if( n.getKind()==UNINTERPRETED_CONSTANT ){ ret = true; - }else{ + }else{ for( unsigned i=0; i<n.getNumChildren(); i++ ){ if( containsUninterpretedConstant( n[i] ) ){ ret = true; @@ -1884,7 +1953,8 @@ Node TermDb::simpleNegate( Node n ){ bool TermDb::isAssoc( Kind k ) { return k==PLUS || k==MULT || k==AND || k==OR || k==XOR || k==IFF || - k==BITVECTOR_PLUS || k==BITVECTOR_MULT || k==BITVECTOR_AND || k==BITVECTOR_OR || k==BITVECTOR_XOR || k==BITVECTOR_XNOR || k==BITVECTOR_CONCAT; + k==BITVECTOR_PLUS || k==BITVECTOR_MULT || k==BITVECTOR_AND || k==BITVECTOR_OR || k==BITVECTOR_XOR || k==BITVECTOR_XNOR || k==BITVECTOR_CONCAT || + k==STRING_CONCAT; } bool TermDb::isComm( Kind k ) { @@ -2184,11 +2254,15 @@ bool TermDb::isQAttrQuantElimPartial( Node q ) { } } -TermDbSygus::TermDbSygus(){ +TermDbSygus::TermDbSygus( context::Context* c, QuantifiersEngine* qe ) : d_quantEngine( qe ){ d_true = NodeManager::currentNM()->mkConst( true ); d_false = NodeManager::currentNM()->mkConst( false ); } +bool TermDbSygus::reset( Theory::Effort e ) { + return true; +} + TNode TermDbSygus::getVar( TypeNode tn, int i ) { while( i>=(int)d_fv[tn].size() ){ std::stringstream ss; @@ -2400,8 +2474,8 @@ Node TermDbSygus::sygusToBuiltin( Node n, TypeNode tn ) { std::map< Node, Node >::iterator it = d_sygus_to_builtin[tn].find( n ); if( it==d_sygus_to_builtin[tn].end() ){ Trace("sygus-db-debug") << "SygusToBuiltin : compute for " << n << ", type = " << tn << std::endl; - Assert( n.getKind()==APPLY_CONSTRUCTOR ); const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + Assert( n.getKind()==APPLY_CONSTRUCTOR ); unsigned i = Datatype::indexOf( n.getOperator().toExpr() ); Assert( n.getNumChildren()==dt[i].getNumArgs() ); std::map< TypeNode, int > var_count; @@ -3051,6 +3125,7 @@ void TermDbSygus::printSygusTerm( std::ostream& out, Node n, std::vector< Node > std::string name = std::string( str.begin() + found +1, str.end() ); out << name; }else{ + Trace("ajr-temp") << "[[print operator " << op << "]]" << std::endl; out << op; } if( n.getNumChildren()>0 ){ @@ -3118,3 +3193,91 @@ void TermDbSygus::printSygusTerm( std::ostream& out, Node n, std::vector< Node > out << n; } } + +Node TermDbSygus::getAnchor( Node n ) { + if( n.getKind()==APPLY_SELECTOR_TOTAL ){ + return getAnchor( n[0] ); + }else{ + return n; + } +} + +void TermDbSygus::registerEvalTerm( Node n ) { + if( options::sygusDirectEval() ){ + if( n.getKind()==APPLY_UF && !n.getType().isBoolean() ){ + Trace("sygus-eager") << "TermDbSygus::eager: Register eval term : " << n << std::endl; + TypeNode tn = n[0].getType(); + if( datatypes::DatatypesRewriter::isTypeDatatype(tn) ){ + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + if( dt.isSygus() ){ + Node f = n.getOperator(); + Trace("sygus-eager") << "...the evaluation function is : " << f << std::endl; + if( n[0].getKind()!=APPLY_CONSTRUCTOR ){ + d_evals[n[0]].push_back( n ); + TypeNode tn = n[0].getType(); + Assert( datatypes::DatatypesRewriter::isTypeDatatype(tn) ); + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + Node var_list = Node::fromExpr( dt.getSygusVarList() ); + Assert( dt.isSygus() ); + d_eval_args[n[0]].push_back( std::vector< Node >() ); + for( unsigned j=1; j<n.getNumChildren(); j++ ){ + if( var_list[j-1].getType().isBoolean() ){ + //TODO: remove this case when boolean term conversion is eliminated + Node c = NodeManager::currentNM()->mkConst(BitVector(1u, 1u)); + d_eval_args[n[0]].back().push_back( n[j].eqNode( c ) ); + }else{ + d_eval_args[n[0]].back().push_back( n[j] ); + } + } + Node a = getAnchor( n[0] ); + d_subterms[a][n[0]] = true; + } + } + } + } + } +} + +void TermDbSygus::registerModelValue( Node a, Node v, std::vector< Node >& lems ) { + std::map< Node, std::map< Node, bool > >::iterator its = d_subterms.find( a ); + if( its!=d_subterms.end() ){ + Trace("sygus-eager") << "registerModelValue : " << a << ", has " << its->second.size() << " registered subterms." << std::endl; + for( std::map< Node, bool >::iterator itss = its->second.begin(); itss != its->second.end(); ++itss ){ + Node n = itss->first; + Trace("sygus-eager-debug") << "...process : " << n << std::endl; + std::map< Node, std::vector< std::vector< Node > > >::iterator it = d_eval_args.find( n ); + if( it!=d_eval_args.end() && !it->second.empty() ){ + TNode at = a; + TNode vt = v; + Node vn = n.substitute( at, vt ); + vn = Rewriter::rewrite( vn ); + unsigned start = d_node_mv_args_proc[n][vn]; + Node antec = n.eqNode( vn ).negate(); + TypeNode tn = n.getType(); + Assert( datatypes::DatatypesRewriter::isTypeDatatype(tn) ); + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + Assert( dt.isSygus() ); + Trace("sygus-eager") << "TermDbSygus::eager: Register model value : " << vn << " for " << n << std::endl; + Trace("sygus-eager") << "...it has " << it->second.size() << " evaluations, already processed " << start << "." << std::endl; + Node bTerm = d_quantEngine->getTermDatabaseSygus()->sygusToBuiltin( vn, tn ); + Trace("sygus-eager") << "Built-in term : " << bTerm << std::endl; + std::vector< Node > vars; + Node var_list = Node::fromExpr( dt.getSygusVarList() ); + for( unsigned j=0; j<var_list.getNumChildren(); j++ ){ + vars.push_back( var_list[j] ); + } + //for each evaluation + for( unsigned i=start; i<it->second.size(); i++ ){ + Assert( vars.size()==it->second[i].size() ); + Node sBTerm = bTerm.substitute( vars.begin(), vars.end(), it->second[i].begin(), it->second[i].end() ); + Node lem = NodeManager::currentNM()->mkNode( n.getType().isBoolean() ? IFF : EQUAL, d_evals[n][i], sBTerm ); + lem = NodeManager::currentNM()->mkNode( OR, antec, lem ); + Trace("sygus-eager") << "Lemma : " << lem << std::endl; + lems.push_back( lem ); + } + d_node_mv_args_proc[n][vn] = it->second.size(); + } + } + } +} + diff --git a/src/theory/quantifiers/term_database.h b/src/theory/quantifiers/term_database.h index a62b343a2..7ab3668eb 100644..100755 --- a/src/theory/quantifiers/term_database.h +++ b/src/theory/quantifiers/term_database.h @@ -47,15 +47,6 @@ typedef expr::Attribute< SygusAttributeId, bool > SygusAttribute; struct SynthesisAttributeId {}; typedef expr::Attribute< SynthesisAttributeId, bool > SynthesisAttribute; -/** Attribute true for nodes that should not be used for matching */ -struct NoMatchAttributeId {}; -/** use the special for boolean flag */ -typedef expr::Attribute< NoMatchAttributeId, - bool, - expr::attr::NullCleanupStrategy, - true // context dependent - > NoMatchAttribute; - // attribute for "contains instantiation constants from" struct InstConstantAttributeId {}; typedef expr::Attribute<InstConstantAttributeId, Node> InstConstantAttribute; @@ -179,6 +170,7 @@ class TermDb : public QuantifiersUtil { friend class ::CVC4::theory::quantifiers::ConjectureGenerator; friend class ::CVC4::theory::quantifiers::TermGenEnv; typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap; + typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap; private: /** reference to the quantifiers engine */ QuantifiersEngine* d_quantEngine; @@ -211,6 +203,8 @@ private: std::map< Node, std::vector< Node > > d_op_map; /** map from type nodes to terms of that type */ std::map< TypeNode, std::vector< Node > > d_type_map; + /** inactive map */ + NodeBoolMap d_inactive_map; /** count number of non-redundant ground terms per operator */ std::map< Node, int > d_op_nonred_count; @@ -228,7 +222,7 @@ private: /** set has term */ void setHasTerm( Node n ); /** evaluate term */ - Node evaluateTerm2( TNode n, std::map< TNode, Node >& visited, EqualityQuery * qy ); + Node evaluateTerm2( TNode n, std::map< TNode, Node >& visited, EqualityQuery * qy, bool useEntailmentTests ); TNode getEntailedTerm2( TNode n, std::map< TNode, TNode >& subs, bool subsRep, bool hasSubs, EqualityQuery * qy ); bool isEntailed2( TNode n, std::map< TNode, TNode >& subs, bool subsRep, bool hasSubs, bool pol, EqualityQuery * qy ); public: @@ -254,18 +248,23 @@ public: void computeArgReps( TNode n ); /** compute uf eqc terms */ void computeUfEqcTerms( TNode f ); + /** compute uf terms */ + void computeUfTerms( TNode f ); /** in relevant domain */ bool inRelevantDomain( TNode f, unsigned i, TNode r ); /** evaluate a term under a substitution. Return representative in EE if possible. * subsRep is whether subs contains only representatives */ - Node evaluateTerm( TNode n, EqualityQuery * qy = NULL ); + Node evaluateTerm( TNode n, EqualityQuery * qy = NULL, bool useEntailmentTests = false ); /** get entailed term, does not construct new terms, less aggressive */ TNode getEntailedTerm( TNode n, EqualityQuery * qy = NULL ); TNode getEntailedTerm( TNode n, std::map< TNode, TNode >& subs, bool subsRep, EqualityQuery * qy = NULL ); /** is entailed (incomplete check) */ bool isEntailed( TNode n, bool pol, EqualityQuery * qy = NULL ); bool isEntailed( TNode n, std::map< TNode, TNode >& subs, bool subsRep, bool pol, EqualityQuery * qy = NULL ); + /** is active */ + bool isTermActive( Node n ); + void setTermInactive( Node n ); /** has term */ bool hasTermCurrent( Node n, bool useMode = true ); /** is term eligble for instantiation? */ @@ -274,7 +273,7 @@ public: Node getEligibleTermInEqc( TNode r ); /** is inst closure */ bool isInstClosure( Node r ); - + //for model basis private: //map from types to model basis terms @@ -301,6 +300,7 @@ public: private: /** map from universal quantifiers to the list of variables */ std::map< Node, std::vector< Node > > d_vars; + std::map< Node, std::map< Node, unsigned > > d_var_num; /** map from universal quantifiers to the list of instantiation constants */ std::map< Node, std::vector< Node > > d_inst_constants; /** map from universal quantifiers to their inst constant body */ @@ -312,6 +312,8 @@ private: /** make instantiation constants for */ void makeInstantiationConstantsFor( Node q ); public: + /** get variable number */ + unsigned getVariableNum( Node q, Node v ) { return d_var_num[q][v]; } /** get the i^th instantiation constant of q */ Node getInstantiationConstant( Node q, int i ) const; /** get number of instantiation constants for q */ @@ -327,6 +329,7 @@ public: instantiation. */ Node getInstConstantNode( Node n, Node q ); + Node getVariableNode( Node n, Node q ); /** get substituted node */ Node getInstantiatedNode( Node n, Node q, std::vector< Node >& terms ); @@ -419,7 +422,8 @@ private: //free variables std::map< TypeNode, std::vector< Node > > d_cn_free_var; // get canonical term, return null if it contains a term apart from handled signature - Node getCanonicalTerm( TNode n, std::map< TypeNode, unsigned >& var_count, std::map< TNode, TNode >& subs, bool apply_torder ); + Node getCanonicalTerm( TNode n, std::map< TypeNode, unsigned >& var_count, std::map< TNode, TNode >& subs, bool apply_torder, + std::map< TNode, Node >& visited ); public: /** get id for operator */ int getIdForOperator( Node op ); @@ -461,6 +465,8 @@ public: static Node ensureType( Node n, TypeNode tn ); /** get ensure type condition */ static bool getEnsureTypeCondition( Node n, TypeNode tn, std::vector< Node >& cond ); + /** get relevancy condition */ + static void getRelevancyCondition( Node n, std::vector< Node >& cond ); private: //helper for contains term static bool containsTerm2( Node n, Node t, std::map< Node, bool >& visited ); @@ -541,6 +547,8 @@ public: class TermDbSygus { private: + /** reference to the quantifiers engine */ + QuantifiersEngine* d_quantEngine; std::map< TypeNode, std::vector< Node > > d_fv; std::map< Node, TypeNode > d_fv_stype; std::map< Node, int > d_fv_num; @@ -554,7 +562,6 @@ public: private: std::map< TypeNode, std::map< int, Node > > d_generic_base; std::map< TypeNode, std::vector< Node > > d_generic_templ; - Node getGenericBase( TypeNode tn, const Datatype& dt, int c ); bool getMatch( Node p, Node n, std::map< int, Node >& s ); bool getMatch2( Node p, Node n, std::map< int, Node >& s, std::vector< int >& new_s ); public: @@ -581,7 +588,11 @@ private: std::map< TypeNode, std::map< Node, Node > > d_sygus_to_builtin; std::map< TypeNode, std::map< Node, Node > > d_builtin_const_to_sygus; public: - TermDbSygus(); + TermDbSygus( context::Context* c, QuantifiersEngine* qe ); + ~TermDbSygus(){} + bool reset( Theory::Effort e ); + std::string identify() const { return "TermDbSygus"; } + bool isRegistered( TypeNode tn ); TypeNode sygusToBuiltinType( TypeNode tn ); int getKindArg( TypeNode tn, Kind k ); @@ -615,6 +626,7 @@ public: /** get value */ Node getTypeMaxValue( TypeNode tn ); TypeNode getSygusTypeForVar( Node v ); + Node getGenericBase( TypeNode tn, const Datatype& dt, int c ); Node mkGeneric( const Datatype& dt, int c, std::map< TypeNode, int >& var_count, std::map< int, Node >& pre ); Node sygusToBuiltin( Node n, TypeNode tn ); Node builtinToSygusConst( Node c, TypeNode tn, int rcons_depth = 0 ); @@ -633,6 +645,18 @@ public: static Kind getOperatorKind( Node op ); /** print sygus term */ static void printSygusTerm( std::ostream& out, Node n, std::vector< Node >& lvs ); + + /** get anchor */ + static Node getAnchor( Node n ); +//for eager instantiation +private: + std::map< Node, std::map< Node, bool > > d_subterms; + std::map< Node, std::vector< Node > > d_evals; + std::map< Node, std::vector< std::vector< Node > > > d_eval_args; + std::map< Node, std::map< Node, unsigned > > d_node_mv_args_proc; +public: + void registerEvalTerm( Node n ); + void registerModelValue( Node n, Node v, std::vector< Node >& lems ); }; }/* CVC4::theory::quantifiers namespace */ diff --git a/src/theory/quantifiers/theory_quantifiers.cpp b/src/theory/quantifiers/theory_quantifiers.cpp index efe40aaa8..7ad13b3a8 100644..100755 --- a/src/theory/quantifiers/theory_quantifiers.cpp +++ b/src/theory/quantifiers/theory_quantifiers.cpp @@ -72,8 +72,11 @@ void TheoryQuantifiers::notifyEq(TNode lhs, TNode rhs) { void TheoryQuantifiers::preRegisterTerm(TNode n) { Debug("quantifiers-prereg") << "TheoryQuantifiers::preRegisterTerm() " << n << endl; - if( n.getKind()==FORALL && !TermDb::hasInstConstAttr(n) ){ - getQuantifiersEngine()->registerQuantifier( n ); + if( n.getKind()==FORALL ){ + if( !options::cbqi() || options::recurseCbqi() || !TermDb::hasInstConstAttr(n) ){ + getQuantifiersEngine()->registerQuantifier( n ); + Debug("quantifiers-prereg") << "TheoryQuantifiers::preRegisterTerm() done " << n << endl; + } } } diff --git a/src/theory/quantifiers/theory_quantifiers.h b/src/theory/quantifiers/theory_quantifiers.h index 6775e0536..6775e0536 100644..100755 --- a/src/theory/quantifiers/theory_quantifiers.h +++ b/src/theory/quantifiers/theory_quantifiers.h diff --git a/src/theory/quantifiers/theory_quantifiers_type_rules.h b/src/theory/quantifiers/theory_quantifiers_type_rules.h index 6ba57afb4..6ba57afb4 100644..100755 --- a/src/theory/quantifiers/theory_quantifiers_type_rules.h +++ b/src/theory/quantifiers/theory_quantifiers_type_rules.h diff --git a/src/theory/quantifiers/trigger.cpp b/src/theory/quantifiers/trigger.cpp index 38635b37b..ee091919d 100644..100755 --- a/src/theory/quantifiers/trigger.cpp +++ b/src/theory/quantifiers/trigger.cpp @@ -43,9 +43,8 @@ void TriggerTermInfo::init( Node q, Node n, int reqPol, Node reqPolEq ){ } /** trigger class constructor */ -Trigger::Trigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int matchOption ) - : d_quantEngine( qe ), d_f( f ) -{ +Trigger::Trigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes ) + : d_quantEngine( qe ), d_f( f ) { d_nodes.insert( d_nodes.begin(), nodes.begin(), nodes.end() ); Trace("trigger") << "Trigger for " << f << ": " << std::endl; for( unsigned i=0; i<d_nodes.size(); i++ ){ @@ -53,7 +52,7 @@ Trigger::Trigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int } if( d_nodes.size()==1 ){ if( isSimpleTrigger( d_nodes[0] ) ){ - d_mg = new InstMatchGeneratorSimple( f, d_nodes[0] ); + d_mg = new InstMatchGeneratorSimple( f, d_nodes[0], qe ); }else{ d_mg = InstMatchGenerator::mkInstMatchGenerator( f, d_nodes[0], qe ); d_mg->setActiveAdd(true); @@ -87,7 +86,7 @@ Trigger::Trigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int } Trigger::~Trigger() { - if(d_mg != NULL) { delete d_mg; } + delete d_mg; } void Trigger::resetInstantiationRound(){ @@ -112,6 +111,10 @@ int Trigger::addTerm( Node t ){ return d_mg->addTerm( d_f, t, d_quantEngine ); } +Node Trigger::getInstPattern(){ + return NodeManager::currentNM()->mkNode( INST_PATTERN, d_nodes ); +} + int Trigger::addInstantiations( InstMatch& baseMatch ){ int addedLemmas = d_mg->addInstantiations( d_f, baseMatch, d_quantEngine ); if( addedLemmas>0 ){ @@ -124,77 +127,85 @@ int Trigger::addInstantiations( InstMatch& baseMatch ){ return addedLemmas; } -Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int matchOption, bool keepAll, int trOption ){ - std::vector< Node > trNodes; - if( !keepAll ){ - //only take nodes that contribute variables to the trigger when added - std::vector< Node > temp; - temp.insert( temp.begin(), nodes.begin(), nodes.end() ); - std::map< Node, bool > vars; - std::map< Node, std::vector< Node > > patterns; - size_t varCount = 0; - std::map< Node, std::vector< Node > > varContains; - quantifiers::TermDb::getVarContains( f, temp, varContains ); - for( unsigned i=0; i<temp.size(); i++ ){ - bool foundVar = false; +bool Trigger::mkTriggerTerms( Node q, std::vector< Node >& nodes, unsigned n_vars, std::vector< Node >& trNodes ) { + //only take nodes that contribute variables to the trigger when added + std::vector< Node > temp; + temp.insert( temp.begin(), nodes.begin(), nodes.end() ); + std::map< Node, bool > vars; + std::map< Node, std::vector< Node > > patterns; + size_t varCount = 0; + std::map< Node, std::vector< Node > > varContains; + quantifiers::TermDb::getVarContains( q, temp, varContains ); + for( unsigned i=0; i<temp.size(); i++ ){ + bool foundVar = false; + for( unsigned j=0; j<varContains[ temp[i] ].size(); j++ ){ + Node v = varContains[ temp[i] ][j]; + Assert( quantifiers::TermDb::getInstConstAttr(v)==q ); + if( vars.find( v )==vars.end() ){ + varCount++; + vars[ v ] = true; + foundVar = true; + } + } + if( foundVar ){ + trNodes.push_back( temp[i] ); for( unsigned j=0; j<varContains[ temp[i] ].size(); j++ ){ Node v = varContains[ temp[i] ][j]; - Assert( quantifiers::TermDb::getInstConstAttr(v)==f ); - if( vars.find( v )==vars.end() ){ - varCount++; - vars[ v ] = true; - foundVar = true; - } - } - if( foundVar ){ - trNodes.push_back( temp[i] ); - for( unsigned j=0; j<varContains[ temp[i] ].size(); j++ ){ - Node v = varContains[ temp[i] ][j]; - patterns[ v ].push_back( temp[i] ); - } - } - if( varCount==f[0].getNumChildren() ){ - break; + patterns[ v ].push_back( temp[i] ); } } - if( varCount<f[0].getNumChildren() && !options::partialTriggers() ){ - Trace("trigger-debug") << "Don't consider trigger since it does not contain all variables in " << f << std::endl; - for( unsigned i=0; i<nodes.size(); i++) { - Trace("trigger-debug") << nodes[i] << " "; - } - Trace("trigger-debug") << std::endl; + if( varCount==n_vars ){ + break; + } + } + if( varCount<n_vars ){ + Trace("trigger-debug") << "Don't consider trigger since it does not contain specified number of variables." << std::endl; + for( unsigned i=0; i<nodes.size(); i++) { + Trace("trigger-debug") << nodes[i] << " "; + } + Trace("trigger-debug") << std::endl; - //do not generate multi-trigger if it does not contain all variables - return NULL; - }else{ - //now, minimize the trigger - for( unsigned i=0; i<trNodes.size(); i++ ){ - bool keepPattern = false; - Node n = trNodes[i]; + //do not generate multi-trigger if it does not contain all variables + return false; + }else{ + //now, minimize the trigger + for( unsigned i=0; i<trNodes.size(); i++ ){ + bool keepPattern = false; + Node n = trNodes[i]; + for( unsigned j=0; j<varContains[ n ].size(); j++ ){ + Node v = varContains[ n ][j]; + if( patterns[v].size()==1 ){ + keepPattern = true; + break; + } + } + if( !keepPattern ){ + //remove from pattern vector for( unsigned j=0; j<varContains[ n ].size(); j++ ){ Node v = varContains[ n ][j]; - if( patterns[v].size()==1 ){ - keepPattern = true; - break; - } - } - if( !keepPattern ){ - //remove from pattern vector - for( unsigned j=0; j<varContains[ n ].size(); j++ ){ - Node v = varContains[ n ][j]; - for( unsigned k=0; k<patterns[v].size(); k++ ){ - if( patterns[v][k]==n ){ - patterns[v].erase( patterns[v].begin() + k, patterns[v].begin() + k + 1 ); - break; - } + for( unsigned k=0; k<patterns[v].size(); k++ ){ + if( patterns[v][k]==n ){ + patterns[v].erase( patterns[v].begin() + k, patterns[v].begin() + k + 1 ); + break; } } - //remove from trigger nodes - trNodes.erase( trNodes.begin() + i, trNodes.begin() + i + 1 ); - i--; } + //remove from trigger nodes + trNodes.erase( trNodes.begin() + i, trNodes.begin() + i + 1 ); + i--; } } + } + return true; +} + +Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, bool keepAll, int trOption, unsigned use_n_vars ){ + std::vector< Node > trNodes; + if( !keepAll ){ + unsigned n_vars = use_n_vars==0 ? f[0].getNumChildren() : use_n_vars; + if( !mkTriggerTerms( f, nodes, n_vars, trNodes ) ){ + return NULL; + } }else{ trNodes.insert( trNodes.begin(), nodes.begin(), nodes.end() ); } @@ -211,15 +222,15 @@ Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& } } } - Trigger* t = new Trigger( qe, f, trNodes, matchOption ); + Trigger* t = new Trigger( qe, f, trNodes ); qe->getTriggerDatabase()->addTrigger( trNodes, t ); return t; } -Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, Node n, int matchOption, bool keepAll, int trOption ){ +Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, Node n, bool keepAll, int trOption, unsigned use_n_vars ){ std::vector< Node > nodes; nodes.push_back( n ); - return mkTrigger( qe, f, nodes, matchOption, keepAll, trOption ); + return mkTrigger( qe, f, nodes, keepAll, trOption, use_n_vars ); } bool Trigger::isUsable( Node n, Node q ){ @@ -273,7 +284,7 @@ bool Trigger::isUsableEqTerms( Node q, Node n1, Node n2 ) { return true; } } - }else if( isAtomicTrigger( n1 ) && isUsable( n1, q ) ){ + }else if( isUsableAtomicTrigger( n1, q ) ){ if( options::relationalTriggers() && n2.getKind()==INST_CONSTANT && !quantifiers::TermDb::containsTerm( n1, n2 ) ){ return true; }else if( !quantifiers::TermDb::hasInstConstAttr(n2) ){ @@ -328,24 +339,25 @@ Node Trigger::getIsUsableTrigger( Node n, Node q ) { return rtr2; } }else{ - bool usable = quantifiers::TermDb::getInstConstAttr(n)==q && isAtomicTrigger( n ) && isUsable( n, q ); - Trace("trigger-debug") << n << " usable : " << (quantifiers::TermDb::getInstConstAttr(n)==q) << " " << isAtomicTrigger( n ) << " " << isUsable( n, q ) << std::endl; - if( usable ){ + Trace("trigger-debug") << n << " usable : " << ( quantifiers::TermDb::getInstConstAttr(n)==q ) << " " << isAtomicTrigger( n ) << " " << isUsable( n, q ) << std::endl; + if( isUsableAtomicTrigger( n, q ) ){ return pol ? n : NodeManager::currentNM()->mkNode( IFF, n, NodeManager::currentNM()->mkConst( true ) ).notNode(); } } return Node::null(); } +bool Trigger::isUsableAtomicTrigger( Node n, Node q ) { + return quantifiers::TermDb::getInstConstAttr( n )==q && isAtomicTrigger( n ) && isUsable( n, q ); +} + bool Trigger::isUsableTrigger( Node n, Node q ){ Node nu = getIsUsableTrigger( n, q ); return !nu.isNull(); } bool Trigger::isAtomicTrigger( Node n ){ - Kind k = n.getKind(); - return ( k==APPLY_UF && !n.getOperator().getAttribute(NoMatchAttribute()) ) || - ( k!=APPLY_UF && isAtomicTriggerKind( k ) ); + return isAtomicTriggerKind( n.getKind() ); } bool Trigger::isAtomicTriggerKind( Kind k ) { @@ -363,8 +375,13 @@ bool Trigger::isRelationalTriggerKind( Kind k ) { } bool Trigger::isCbqiKind( Kind k ) { - return quantifiers::TermDb::isBoolConnective( k ) || k==PLUS || k==GEQ || k==EQUAL || k==MULT || - k==APPLY_CONSTRUCTOR || k==APPLY_SELECTOR_TOTAL || k==APPLY_TESTER; + if( quantifiers::TermDb::isBoolConnective( k ) || k==PLUS || k==GEQ || k==EQUAL || k==MULT ){ + return true; + }else{ + //CBQI typically works for satisfaction-complete theories + TheoryId t = kindToTheoryId( k ); + return t==THEORY_BV || t==THEORY_DATATYPES; + } } bool Trigger::isSimpleTrigger( Node n ){ diff --git a/src/theory/quantifiers/trigger.h b/src/theory/quantifiers/trigger.h index 41f2a1c38..a3da4d398 100644..100755 --- a/src/theory/quantifiers/trigger.h +++ b/src/theory/quantifiers/trigger.h @@ -76,6 +76,8 @@ class Trigger { int addTerm( Node t ); /** return whether this is a multi-trigger */ bool isMultiTrigger() { return d_nodes.size()>1; } + /** get inst pattern list */ + Node getInstPattern(); /** add all available instantiations exhaustively, in any equivalence class if limitInst>0, limitInst is the max # of instantiations to try */ @@ -84,8 +86,6 @@ class Trigger { ie : quantifier engine; f : forall something .... nodes : (multi-)trigger - matchOption : which policy to use for creating matches - (one of InstMatchGenerator::MATCH_GEN_* ) keepAll: don't remove unneeded patterns; trOption : policy for dealing with triggers that already existed (see below) @@ -95,18 +95,19 @@ class Trigger { TR_GET_OLD, //return a previous trigger if it had already been created TR_RETURN_NULL //return null if a duplicate is found }; - static Trigger* mkTrigger( QuantifiersEngine* qe, Node f, - std::vector< Node >& nodes, int matchOption = 0, - bool keepAll = true, int trOption = TR_MAKE_NEW ); - static Trigger* mkTrigger( QuantifiersEngine* qe, Node f, Node n, - int matchOption = 0, bool keepAll = true, - int trOption = TR_MAKE_NEW ); + //nodes input, trNodes output + static bool mkTriggerTerms( Node q, std::vector< Node >& nodes, unsigned n_vars, std::vector< Node >& trNodes ); + static Trigger* mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, + bool keepAll = true, int trOption = TR_MAKE_NEW, unsigned use_n_vars = 0 ); + static Trigger* mkTrigger( QuantifiersEngine* qe, Node f, Node n, bool keepAll = true, + int trOption = TR_MAKE_NEW, unsigned use_n_vars = 0 ); static void collectPatTerms( Node q, Node n, std::vector< Node >& patTerms, quantifiers::TriggerSelMode tstrt, std::vector< Node >& exclude, std::map< Node, TriggerTermInfo >& tinfo, bool filterInst = false ); /** is usable trigger */ static bool isUsableTrigger( Node n, Node q ); static Node getIsUsableTrigger( Node n, Node q ); + static bool isUsableAtomicTrigger( Node n, Node q ); static bool isAtomicTrigger( Node n ); static bool isAtomicTriggerKind( Kind k ); static bool isRelationalTrigger( Node n ); @@ -135,7 +136,7 @@ class Trigger { } private: /** trigger constructor */ - Trigger( QuantifiersEngine* ie, Node f, std::vector< Node >& nodes, int matchOption = 0 ); + Trigger( QuantifiersEngine* ie, Node f, std::vector< Node >& nodes ); /** is subterm of trigger usable */ static bool isUsable( Node n, Node q ); diff --git a/src/theory/quantifiers_engine.cpp b/src/theory/quantifiers_engine.cpp index 6d3b17254..d81efe1da 100644 --- a/src/theory/quantifiers_engine.cpp +++ b/src/theory/quantifiers_engine.cpp @@ -57,6 +57,7 @@ using namespace CVC4::theory::inst; QuantifiersEngine::QuantifiersEngine(context::Context* c, context::UserContext* u, TheoryEngine* te): d_te( te ), + d_conflict_c(c, false), //d_quants(u), d_quants_red(u), d_lemmas_produced_c(u), @@ -88,7 +89,6 @@ QuantifiersEngine::QuantifiersEngine(context::Context* c, context::UserContext* d_tr_trie = new inst::TriggerTrie; d_curr_effort_level = QEFFORT_NONE; d_conflict = false; - d_num_added_lemmas_round = 0; d_hasAddedLemma = false; //don't add true lemma d_lemmas_produced_c[d_term_db->d_true] = true; @@ -352,7 +352,7 @@ void QuantifiersEngine::check( Theory::Effort e ){ std::vector< QuantifiersModule* > qm; if( d_model->checkNeeded() ){ needsCheck = needsCheck || e>=Theory::EFFORT_LAST_CALL; //always need to check at or above last call - for( int i=0; i<(int)d_modules.size(); i++ ){ + for( unsigned i=0; i<d_modules.size(); i++ ){ if( d_modules[i]->needsCheck( e ) ){ qm.push_back( d_modules[i] ); needsCheck = true; @@ -366,7 +366,6 @@ void QuantifiersEngine::check( Theory::Effort e ){ } d_conflict = false; - d_num_added_lemmas_round = 0; d_hasAddedLemma = false; bool setIncomplete = false; if( e==Theory::EFFORT_LAST_CALL ){ @@ -380,6 +379,8 @@ void QuantifiersEngine::check( Theory::Effort e ){ Trace("quant-engine-debug2") << "Quantifiers Engine call to check, level = " << e << ", needsCheck=" << needsCheck << std::endl; if( needsCheck ){ + //this will fail if a set of instances is marked as a conflict, but is not + Assert( !d_conflict_c.get() ); //flush previous lemmas (for instance, if was interupted), or other lemmas to process flushLemmas(); if( d_hasAddedLemma ){ @@ -393,6 +394,12 @@ void QuantifiersEngine::check( Theory::Effort e ){ } d_recorded_inst.clear(); } + + double clSet = 0; + if( Trace.isOn("quant-engine") ){ + clSet = double(clock())/double(CLOCKS_PER_SEC); + Trace("quant-engine") << ">>>>> Quantifiers Engine Round, effort = " << e << " <<<<<" << std::endl; + } if( Trace.isOn("quant-engine-debug") ){ Trace("quant-engine-debug") << "Quantifiers Engine check, level = " << e << std::endl; @@ -453,7 +460,6 @@ void QuantifiersEngine::check( Theory::Effort e ){ flushLemmas(); if( d_hasAddedLemma ){ return; - } if( e==Theory::EFFORT_LAST_CALL ){ @@ -545,6 +551,13 @@ void QuantifiersEngine::check( Theory::Effort e ){ } } } + if( Trace.isOn("quant-engine") ){ + double clSet2 = double(clock())/double(CLOCKS_PER_SEC); + Trace("quant-engine") << "Finished quantifiers engine, total time = " << (clSet2-clSet); + Trace("quant-engine") << ", added lemma = " << d_hasAddedLemma; + Trace("quant-engine") << std::endl; + } + Trace("quant-engine-debug2") << "Finished quantifiers engine check." << std::endl; }else{ Trace("quant-engine-debug2") << "Quantifiers Engine does not need check." << std::endl; @@ -564,7 +577,7 @@ void QuantifiersEngine::check( Theory::Effort e ){ } } if( setIncomplete ){ - Trace("quant-engine-debug") << "Set incomplete flag." << std::endl; + Trace("quant-engine") << "Set incomplete flag." << std::endl; getOutputChannel().setIncomplete(); } //output debug stats @@ -621,6 +634,7 @@ bool QuantifiersEngine::registerQuantifier( Node f ){ //check whether we should apply a reduction if( reduceQuantifier( f ) ){ + Trace("quant") << "...reduced." << std::endl; d_quants[f] = false; return false; }else{ @@ -628,6 +642,7 @@ bool QuantifiersEngine::registerQuantifier( Node f ){ d_term_db->makeInstantiationConstantsFor( f ); d_term_db->computeAttributes( f ); for( unsigned i=0; i<d_modules.size(); i++ ){ + Trace("quant-debug") << "pre-register with " << d_modules[i]->identify() << "..." << std::endl; d_modules[i]->preRegisterQuantifier( f ); } QuantifiersModule * qm = getOwner( f ); @@ -640,11 +655,11 @@ bool QuantifiersEngine::registerQuantifier( Node f ){ } //register with each module for( unsigned i=0; i<d_modules.size(); i++ ){ + Trace("quant-debug") << "register with " << d_modules[i]->identify() << "..." << std::endl; d_modules[i]->registerQuantifier( f ); } + //TODO: remove this Node ceBody = d_term_db->getInstConstantBody( f ); - //generate the phase requirements - //d_phase_reqs[f] = new QuantPhaseReq( ceBody, true ); //also register it with the strong solver //if( options::finiteModelFind() ){ // ((uf::TheoryUF*)d_te->theoryOf( THEORY_UF ))->getStrongSolver()->registerQuantifier( f ); @@ -874,8 +889,9 @@ Node QuantifiersEngine::getSubstitute( Node n, std::vector< Node >& terms ){ Node QuantifiersEngine::getInstantiation( Node q, std::vector< Node >& vars, std::vector< Node >& terms, bool doVts ){ Node body; + Assert( vars.size()==terms.size() ); //process partial instantiation if necessary - if( d_term_db->d_vars[q].size()!=vars.size() ){ + if( q[0].getNumChildren()!=vars.size() ){ body = q[ 1 ].substitute( vars.begin(), vars.end(), terms.begin(), terms.end() ); std::vector< Node > uninst_vars; //doing a partial instantiation, must add quantifier for all uninstantiated variables @@ -884,6 +900,8 @@ Node QuantifiersEngine::getInstantiation( Node q, std::vector< Node >& vars, std uninst_vars.push_back( q[0][i] ); } } + Trace("partial-inst") << "Partially instantiating with " << vars.size() << " / " << q[0].getNumChildren() << " for " << q << std::endl; + Assert( !uninst_vars.empty() ); Node bvl = NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, uninst_vars ); body = NodeManager::currentNM()->mkNode( FORALL, bvl, body ); Trace("partial-inst") << "Partial instantiation : " << q << std::endl; @@ -922,6 +940,7 @@ Node QuantifiersEngine::getInstantiation( Node q, InstMatch& m, bool doVts ){ } Node QuantifiersEngine::getInstantiation( Node q, std::vector< Node >& terms, bool doVts ) { + Assert( d_term_db->d_vars.find( q )!=d_term_db->d_vars.end() ); return getInstantiation( q, d_term_db->d_vars[q], terms, doVts ); } @@ -956,7 +975,6 @@ bool QuantifiersEngine::addLemma( Node lem, bool doCache, bool doRewrite ){ d_lemmas_produced_c[ lem ] = true; d_lemmas_waiting.push_back( lem ); Trace("inst-add-debug") << "Added lemma" << std::endl; - d_num_added_lemmas_round++; return true; }else{ Trace("inst-add-debug") << "Duplicate." << std::endl; @@ -965,7 +983,6 @@ bool QuantifiersEngine::addLemma( Node lem, bool doCache, bool doRewrite ){ }else{ //do not need to rewrite, will be rewritten after sending d_lemmas_waiting.push_back( lem ); - d_num_added_lemmas_round++; return true; } } @@ -997,6 +1014,7 @@ bool QuantifiersEngine::addInstantiation( Node q, std::vector< Node >& terms, bo Assert( !d_conflict ); Assert( terms.size()==q[0].getNumChildren() ); Trace("inst-add-debug") << "For quantified formula " << q << ", add instantiation: " << std::endl; + std::vector< Node > rlv_cond; for( unsigned i=0; i<terms.size(); i++ ){ Trace("inst-add-debug") << " " << q[0][i]; Trace("inst-add-debug2") << " -> " << terms[i]; @@ -1014,20 +1032,29 @@ bool QuantifiersEngine::addInstantiation( Node q, std::vector< Node >& terms, bo if( terms[i].isNull() ){ Trace("inst-add-debug") << " --> Failed to make term vector, due to term/type restrictions." << std::endl; return false; + }else{ + //get relevancy conditions + if( options::instRelevantCond() ){ + quantifiers::TermDb::getRelevancyCondition( terms[i], rlv_cond ); + } } #ifdef CVC4_ASSERTIONS bool bad_inst = false; if( quantifiers::TermDb::containsUninterpretedConstant( terms[i] ) ){ + Trace("inst") << "***& inst contains uninterpreted constant : " << terms[i] << std::endl; bad_inst = true; }else if( !terms[i].getType().isSubtypeOf( q[0][i].getType() ) ){ + Trace("inst") << "***& inst bad type : " << terms[i] << " " << terms[i].getType() << "/" << q[0][i].getType() << std::endl; bad_inst = true; }else if( options::cbqi() ){ Node icf = quantifiers::TermDb::getInstConstAttr(terms[i]); if( !icf.isNull() ){ if( icf==q ){ + Trace("inst") << "***& inst contains inst constant attr : " << terms[i] << std::endl; + bad_inst = true; + }else if( quantifiers::TermDb::containsTerms( terms[i], d_term_db->d_inst_constants[q] ) ){ + Trace("inst") << "***& inst contains inst constants : " << terms[i] << std::endl; bad_inst = true; - }else{ - bad_inst = quantifiers::TermDb::containsTerms( terms[i], d_term_db->d_inst_constants[q] ); } } } @@ -1079,13 +1106,21 @@ bool QuantifiersEngine::addInstantiation( Node q, std::vector< Node >& terms, bo Trace("inst-add-debug") << "Constructing instantiation..." << std::endl; Assert( d_term_db->d_vars[q].size()==terms.size() ); Node body = getInstantiation( q, d_term_db->d_vars[q], terms, doVts ); //do virtual term substitution + Node orig_body = body; body = quantifiers::QuantifiersRewriter::preprocess( body, true ); Trace("inst-debug") << "...preprocess to " << body << std::endl; //construct the lemma Trace("inst-assert") << "(assert " << body << ")" << std::endl; body = Rewriter::rewrite(body); - Node lem = NodeManager::currentNM()->mkNode( kind::OR, q.negate(), body ); + Node lem; + if( rlv_cond.empty() ){ + lem = NodeManager::currentNM()->mkNode( kind::OR, q.negate(), body ); + }else{ + rlv_cond.push_back( q.negate() ); + rlv_cond.push_back( body ); + lem = NodeManager::currentNM()->mkNode( kind::OR, rlv_cond ); + } lem = Rewriter::rewrite(lem); //check for lemma duplication @@ -1106,15 +1141,20 @@ bool QuantifiersEngine::addInstantiation( Node q, std::vector< Node >& terms, bo } } if( options::instMaxLevel()!=-1 ){ - uint64_t maxInstLevel = 0; - for( unsigned i=0; i<terms.size(); i++ ){ - if( terms[i].hasAttribute(InstLevelAttribute()) ){ - if( terms[i].getAttribute(InstLevelAttribute())>maxInstLevel ){ - maxInstLevel = terms[i].getAttribute(InstLevelAttribute()); + if( doVts ){ + //virtual term substitution/instantiation level features are incompatible + Assert( false ); + }else{ + uint64_t maxInstLevel = 0; + for( unsigned i=0; i<terms.size(); i++ ){ + if( terms[i].hasAttribute(InstLevelAttribute()) ){ + if( terms[i].getAttribute(InstLevelAttribute())>maxInstLevel ){ + maxInstLevel = terms[i].getAttribute(InstLevelAttribute()); + } } } + setInstantiationLevelAttr( orig_body, q[1], maxInstLevel+1 ); } - setInstantiationLevelAttr( body, q[1], maxInstLevel+1 ); } if( d_curr_effort_level>QEFFORT_CONFLICT && d_curr_effort_level<QEFFORT_NONE ){ //notify listeners @@ -1122,6 +1162,7 @@ bool QuantifiersEngine::addInstantiation( Node q, std::vector< Node >& terms, bo if( !d_inst_notify[j]->notifyInstantiation( d_curr_effort_level, q, lem, terms, body ) ){ Trace("inst-add-debug") << "...we are in conflict." << std::endl; d_conflict = true; + d_conflict_c = true; Assert( !d_lemmas_waiting.empty() ); break; } @@ -1205,9 +1246,19 @@ quantifiers::UserPatMode QuantifiersEngine::getInstUserPatMode() { void QuantifiersEngine::flushLemmas(){ if( !d_lemmas_waiting.empty() ){ + //filter based on notify classes + if( !d_inst_notify.empty() ){ + unsigned prev_lem_sz = d_lemmas_waiting.size(); + for( unsigned j=0; j<d_inst_notify.size(); j++ ){ + d_inst_notify[j]->filterInstantiations(); + } + if( prev_lem_sz!=d_lemmas_waiting.size() ){ + Trace("quant-engine") << "...filtered instances : " << d_lemmas_waiting.size() << " / " << prev_lem_sz << std::endl; + } + } //take default output channel if none is provided d_hasAddedLemma = true; - for( int i=0; i<(int)d_lemmas_waiting.size(); i++ ){ + for( unsigned i=0; i<d_lemmas_waiting.size(); i++ ){ Trace("qe-lemma") << "Lemma : " << d_lemmas_waiting[i] << std::endl; getOutputChannel().lemma( d_lemmas_waiting[i], false, true ); } @@ -1403,7 +1454,7 @@ bool EqualityQueryQuantifiersEngine::processInferences( Theory::Effort e ) { Trace("quant-engine-ee-proc") << " explanation : " << eq_exp << std::endl; Assert( ee->hasTerm( eq[0] ) ); Assert( ee->hasTerm( eq[1] ) ); - if( ee->areDisequal( eq[0], eq[1], false ) ){ + if( areDisequal( eq[0], eq[1] ) ){ Trace("quant-engine-ee-proc") << "processInferences : Conflict : " << eq << std::endl; if( Trace.isOn("term-db-lemma") ){ Trace("term-db-lemma") << "Disequal terms, equal by normalization : " << eq[0] << " " << eq[1] << "!!!!" << std::endl; @@ -1445,11 +1496,10 @@ bool EqualityQueryQuantifiersEngine::areEqual( Node a, Node b ){ }else{ eq::EqualityEngine* ee = getEngine(); if( ee->hasTerm( a ) && ee->hasTerm( b ) ){ - if( ee->areEqual( a, b ) ){ - return true; - } + return ee->areEqual( a, b ); + }else{ + return false; } - return false; } } @@ -1459,11 +1509,10 @@ bool EqualityQueryQuantifiersEngine::areDisequal( Node a, Node b ){ }else{ eq::EqualityEngine* ee = getEngine(); if( ee->hasTerm( a ) && ee->hasTerm( b ) ){ - if( ee->areDisequal( a, b, false ) ){ - return true; - } + return ee->areDisequal( a, b, false ); + }else{ + return a.isConst() && b.isConst(); } - return false; } } diff --git a/src/theory/quantifiers_engine.h b/src/theory/quantifiers_engine.h index 4ee66f9e7..1f4a04218 100644 --- a/src/theory/quantifiers_engine.h +++ b/src/theory/quantifiers_engine.h @@ -48,6 +48,7 @@ class InstantiationNotify { public: InstantiationNotify(){} virtual bool notifyInstantiation( unsigned quant_e, Node q, Node lem, std::vector< Node >& terms, Node body ) = 0; + virtual void filterInstantiations() = 0; }; namespace quantifiers { @@ -156,8 +157,7 @@ private: //this information is reset during check unsigned d_curr_effort_level; /** are we in conflict */ bool d_conflict; - /** number of lemmas we actually added this round (for debugging) */ - unsigned d_num_added_lemmas_round; + context::CDO< bool > d_conflict_c; /** has added lemma this round */ bool d_hasAddedLemma; private: @@ -295,9 +295,9 @@ private: bool removeInstantiationInternal( Node q, std::vector< Node >& terms ); /** set instantiation level attr */ static void setInstantiationLevelAttr( Node n, Node qn, uint64_t level ); +public: /** flush lemmas */ void flushLemmas(); -public: /** get instantiation */ Node getInstantiation( Node q, std::vector< Node >& vars, std::vector< Node >& terms, bool doVts = false ); /** get instantiation */ @@ -330,8 +330,6 @@ public: bool inConflict() { return d_conflict; } /** get number of waiting lemmas */ unsigned getNumLemmasWaiting() { return d_lemmas_waiting.size(); } - /** get number of waiting lemmas */ - unsigned getNumLemmasAddedThisRound() { return d_num_added_lemmas_round; } /** get needs check */ bool getInstWhenNeedsCheck( Theory::Effort e ); /** get user pat mode */ diff --git a/src/theory/rep_set.cpp b/src/theory/rep_set.cpp index d7178a8c1..184553ba7 100644 --- a/src/theory/rep_set.cpp +++ b/src/theory/rep_set.cpp @@ -127,19 +127,11 @@ int RepSet::getNumRelevantGroundReps( TypeNode t ) { } void RepSet::toStream(std::ostream& out){ -#if 0 - for( std::map< TypeNode, std::vector< Node > >::iterator it = d_type_reps.begin(); it != d_type_reps.end(); ++it ){ - out << it->first << " : " << std::endl; - for( int i=0; i<(int)it->second.size(); i++ ){ - out << " " << i << ": " << it->second[i] << std::endl; - } - } -#else for( std::map< TypeNode, std::vector< Node > >::iterator it = d_type_reps.begin(); it != d_type_reps.end(); ++it ){ if( !it->first.isFunction() && !it->first.isPredicate() ){ out << "(" << it->first << " " << it->second.size(); out << " ("; - for( int i=0; i<(int)it->second.size(); i++ ){ + for( unsigned i=0; i<it->second.size(); i++ ){ if( i>0 ){ out << " "; } out << it->second[i]; } @@ -147,7 +139,6 @@ void RepSet::toStream(std::ostream& out){ out << ")" << std::endl; } } -#endif } @@ -157,10 +148,11 @@ RepSetIterator::RepSetIterator( QuantifiersEngine * qe, RepSet* rs ) : d_qe(qe), int RepSetIterator::domainSize( int i ) { Assert(i>=0); - if( d_enum_type[i]==ENUM_DOMAIN_ELEMENTS ){ - return d_domain[i].size(); + int v = d_var_order[i]; + if( d_enum_type[v]==ENUM_DOMAIN_ELEMENTS ){ + return d_domain[v].size(); }else{ - return d_domain[i][0]; + return d_domain[v][0]; } } @@ -188,15 +180,15 @@ bool RepSetIterator::setFunctionDomain( Node op ){ bool RepSetIterator::initialize(){ Trace("rsi") << "Initialize rep set iterator..." << std::endl; - for( size_t i=0; i<d_types.size(); i++ ){ + for( unsigned v=0; v<d_types.size(); v++ ){ d_index.push_back( 0 ); //store default index order - d_index_order.push_back( i ); - d_var_order[i] = i; + d_index_order.push_back( v ); + d_var_order[v] = v; //store default domain d_domain.push_back( RepDomain() ); - TypeNode tn = d_types[i]; - Trace("rsi") << "Var #" << i << " is type " << tn << "..." << std::endl; + TypeNode tn = d_types[v]; + Trace("rsi") << "Var #" << v << " is type " << tn << "..." << std::endl; if( tn.isSort() ){ //must ensure uninterpreted type is non-empty. if( !d_rep_set->hasType( tn ) ){ @@ -209,59 +201,59 @@ bool RepSetIterator::initialize(){ Trace("mkVar") << "RepSetIterator:: Make variable " << var << " : " << tn << std::endl; d_rep_set->add( tn, var ); } - }else if( tn.isInteger() ){ - bool inc = false; - //check if it is bound + }else{ + bool inc = true; + //check if it is bound by bounded integer module if( d_owner.getKind()==FORALL && d_qe && d_qe->getBoundedIntegers() ){ - if( d_qe->getBoundedIntegers()->isBoundVar( d_owner, d_owner[0][i] ) ){ + unsigned bvt = d_qe->getBoundedIntegers()->getBoundVarType( d_owner, d_owner[0][v] ); + if( bvt==quantifiers::BoundedIntegers::BOUND_INT_RANGE ){ Trace("rsi") << " variable is bounded integer." << std::endl; - d_enum_type.push_back( ENUM_RANGE ); - }else{ - inc = true; + d_enum_type.push_back( ENUM_INT_RANGE ); + inc = false; + }else if( bvt==quantifiers::BoundedIntegers::BOUND_SET_MEMBER ){ + Trace("rsi") << " variable is bounded by set membership." << std::endl; + d_enum_type.push_back( ENUM_SET_MEMBERS ); + inc = false; } - }else{ - inc = true; } if( inc ){ //check if it is otherwise bound - if( d_bounds[0].find(i)!=d_bounds[0].end() && d_bounds[1].find(i)!=d_bounds[1].end() ){ + if( d_bounds[0].find( v )!=d_bounds[0].end() && d_bounds[1].find( v )!=d_bounds[1].end() ){ Trace("rsi") << " variable is bounded." << std::endl; - d_enum_type.push_back( ENUM_RANGE ); + d_enum_type.push_back( ENUM_INT_RANGE ); + }else if( d_qe->getTermDatabase()->mayComplete( tn ) ){ + Trace("rsi") << " do complete, since cardinality is small (" << tn.getCardinality() << ")..." << std::endl; + d_rep_set->complete( tn ); + //must have succeeded + Assert( d_rep_set->hasType( tn ) ); }else{ Trace("rsi") << " variable cannot be bounded." << std::endl; - Trace("fmf-incomplete") << "Incomplete because of integer quantification of " << d_owner[0][i] << "." << std::endl; + Trace("fmf-incomplete") << "Incomplete because of quantification of type " << tn << std::endl; d_incomplete = true; } } - //enumerate if the sort is reasonably small - }else if( d_qe->getTermDatabase()->mayComplete( tn ) ){ - Trace("rsi") << " do complete, since cardinality is small (" << tn.getCardinality() << ")..." << std::endl; - d_rep_set->complete( tn ); - //must have succeeded - Assert( d_rep_set->hasType( tn ) ); - }else{ - Trace("rsi") << " variable cannot be bounded." << std::endl; - Trace("fmf-incomplete") << "Incomplete because of quantification of type " << tn << std::endl; - d_incomplete = true; } //if we have yet to determine the type of enumeration - if( d_enum_type.size()<=i ){ + if( d_enum_type.size()<=v ){ d_enum_type.push_back( ENUM_DOMAIN_ELEMENTS ); if( d_rep_set->hasType( tn ) ){ - for( size_t j=0; j<d_rep_set->d_type_reps[tn].size(); j++ ){ - d_domain[i].push_back( j ); + for( unsigned j=0; j<d_rep_set->d_type_reps[tn].size(); j++ ){ + d_domain[v].push_back( j ); } }else{ + Assert( d_incomplete ); return false; } } } //must set a variable index order based on bounded integers - if (d_owner.getKind()==FORALL && d_qe && d_qe->getBoundedIntegers()) { + if( d_owner.getKind()==FORALL && d_qe && d_qe->getBoundedIntegers() ){ Trace("bound-int-rsi") << "Calculating variable order..." << std::endl; std::vector< int > varOrder; - for( unsigned i=0; i<d_qe->getBoundedIntegers()->getNumBoundVars(d_owner); i++ ){ - varOrder.push_back(d_qe->getBoundedIntegers()->getBoundVarNum(d_owner,i)); + for( unsigned i=0; i<d_qe->getBoundedIntegers()->getNumBoundVars( d_owner ); i++ ){ + Node v = d_qe->getBoundedIntegers()->getBoundVar( d_owner, i ); + Trace("bound-int-rsi") << " bound var #" << i << " is " << v << std::endl; + varOrder.push_back( d_qe->getTermDatabase()->getVariableNum( d_owner, v ) ); } for( unsigned i=0; i<d_owner[0].getNumChildren(); i++) { if( !d_qe->getBoundedIntegers()->isBoundVar(d_owner, d_owner[0][i])) { @@ -283,14 +275,10 @@ bool RepSetIterator::initialize(){ Trace("bound-int-rsi") << indexOrder[i] << " "; } Trace("bound-int-rsi") << std::endl; - setIndexOrder(indexOrder); + setIndexOrder( indexOrder ); } //now reset the indices - for (unsigned i=0; i<d_index.size(); i++) { - if (!resetIndex(i, true)){ - break; - } - } + do_reset_increment( -1, true ); return true; } @@ -298,46 +286,40 @@ void RepSetIterator::setIndexOrder( std::vector< int >& indexOrder ){ d_index_order.clear(); d_index_order.insert( d_index_order.begin(), indexOrder.begin(), indexOrder.end() ); //make the d_var_order mapping - for( int i=0; i<(int)d_index_order.size(); i++ ){ + for( unsigned i=0; i<d_index_order.size(); i++ ){ d_var_order[d_index_order[i]] = i; } } -/* -void RepSetIterator::setDomain( std::vector< RepDomain >& domain ){ - d_domain.clear(); - d_domain.insert( d_domain.begin(), domain.begin(), domain.end() ); - //we are done if a domain is empty - for( int i=0; i<(int)d_domain.size(); i++ ){ - if( d_domain[i].empty() ){ - d_index.clear(); - } - } -} -*/ -bool RepSetIterator::resetIndex( int i, bool initial ) { + +int RepSetIterator::resetIndex( int i, bool initial ) { d_index[i] = 0; - int ii = d_index_order[i]; - Trace("bound-int-rsi") << "Reset " << i << " " << ii << " " << initial << std::endl; + int v = d_var_order[i]; + Trace("bound-int-rsi") << "Reset " << i << ", var order = " << v << ", initial = " << initial << std::endl; //determine the current range - if( d_enum_type[ii]==ENUM_RANGE ){ - if( initial || ( d_qe->getBoundedIntegers() && !d_qe->getBoundedIntegers()->isGroundRange( d_owner, d_owner[0][ii] ) ) ){ - Trace("bound-int-rsi") << "Getting range of " << d_owner[0][ii] << std::endl; + if( initial || ( d_owner.getKind()==FORALL && d_qe && d_qe->getBoundedIntegers() && + d_qe->getBoundedIntegers()->isBoundVar( d_owner, d_owner[0][v] ) && + !d_qe->getBoundedIntegers()->isGroundRange( d_owner, d_owner[0][v] ) ) ){ + Trace("bound-int-rsi") << "Getting range of " << d_owner[0][v] << std::endl; + if( d_enum_type[v]==ENUM_INT_RANGE ){ Node actual_l; Node l, u; - if( d_qe->getBoundedIntegers() && d_qe->getBoundedIntegers()->isBoundVar( d_owner, d_owner[0][ii] ) ){ - d_qe->getBoundedIntegers()->getBoundValues( d_owner, d_owner[0][ii], this, l, u ); + if( d_qe->getBoundedIntegers() ){ + unsigned bvt = d_qe->getBoundedIntegers()->getBoundVarType( d_owner, d_owner[0][v] ); + if( bvt==quantifiers::BoundedIntegers::BOUND_INT_RANGE ){ + d_qe->getBoundedIntegers()->getBoundValues( d_owner, d_owner[0][v], this, l, u ); + } } for( unsigned b=0; b<2; b++ ){ - if( d_bounds[b].find(ii)!=d_bounds[b].end() ){ - Trace("bound-int-rsi") << "May further limit bound(" << b << ") based on " << d_bounds[b][ii] << std::endl; - if( b==0 && (l.isNull() || d_bounds[b][ii].getConst<Rational>() > l.getConst<Rational>()) ){ + if( d_bounds[b].find(v)!=d_bounds[b].end() ){ + Trace("bound-int-rsi") << "May further limit bound(" << b << ") based on " << d_bounds[b][v] << std::endl; + if( b==0 && (l.isNull() || d_bounds[b][v].getConst<Rational>() > l.getConst<Rational>()) ){ if( !l.isNull() ){ //bound was limited externally, record that the value lower bound is not equal to the term lower bound - actual_l = NodeManager::currentNM()->mkNode( MINUS, d_bounds[b][ii], l ); + actual_l = NodeManager::currentNM()->mkNode( MINUS, d_bounds[b][v], l ); } - l = d_bounds[b][ii]; - }else if( b==1 && (u.isNull() || d_bounds[b][ii].getConst<Rational>() <= u.getConst<Rational>()) ){ - u = NodeManager::currentNM()->mkNode( MINUS, d_bounds[b][ii], + l = d_bounds[b][v]; + }else if( b==1 && (u.isNull() || d_bounds[b][v].getConst<Rational>() <= u.getConst<Rational>()) ){ + u = NodeManager::currentNM()->mkNode( MINUS, d_bounds[b][v], NodeManager::currentNM()->mkConst( Rational(1) ) ); u = Rewriter::rewrite( u ); } @@ -346,73 +328,109 @@ bool RepSetIterator::resetIndex( int i, bool initial ) { if( l.isNull() || u.isNull() ){ //failed, abort the iterator - d_index.clear(); - return false; + return -1; }else{ - Trace("bound-int-rsi") << "Can limit bounds of " << d_owner[0][ii] << " to " << l << "..." << u << std::endl; + Trace("bound-int-rsi") << "Can limit bounds of " << d_owner[0][v] << " to " << l << "..." << u << std::endl; Node range = Rewriter::rewrite( NodeManager::currentNM()->mkNode( MINUS, u, l ) ); Node ra = Rewriter::rewrite( NodeManager::currentNM()->mkNode( LEQ, range, NodeManager::currentNM()->mkConst( Rational( 9999 ) ) ) ); - d_domain[ii].clear(); + d_domain[v].clear(); Node tl = l; Node tu = u; - if( d_qe->getBoundedIntegers() && d_qe->getBoundedIntegers()->isBoundVar( d_owner, d_owner[0][ii] ) ){ - d_qe->getBoundedIntegers()->getBounds( d_owner, d_owner[0][ii], this, tl, tu ); + if( d_qe->getBoundedIntegers() && d_qe->getBoundedIntegers()->isBoundVar( d_owner, d_owner[0][v] ) ){ + d_qe->getBoundedIntegers()->getBounds( d_owner, d_owner[0][v], this, tl, tu ); } - d_lower_bounds[ii] = tl; + d_lower_bounds[v] = tl; if( !actual_l.isNull() ){ //if bound was limited externally, must consider the offset - d_lower_bounds[ii] = Rewriter::rewrite( NodeManager::currentNM()->mkNode( PLUS, tl, actual_l ) ); + d_lower_bounds[v] = Rewriter::rewrite( NodeManager::currentNM()->mkNode( PLUS, tl, actual_l ) ); } - if( ra==NodeManager::currentNM()->mkConst(true) ){ + if( ra==d_qe->getTermDatabase()->d_true ){ long rr = range.getConst<Rational>().getNumerator().getLong()+1; Trace("bound-int-rsi") << "Actual bound range is " << rr << std::endl; - d_domain[ii].push_back( (int)rr ); + d_domain[v].push_back( (int)rr ); + if( rr<=0 ){ + return 0; + } }else{ - Trace("fmf-incomplete") << "Incomplete because of integer quantification, bounds are too big for " << d_owner[0][ii] << "." << std::endl; - d_incomplete = true; - d_domain[ii].push_back( 0 ); + Trace("fmf-incomplete") << "Incomplete because of integer quantification, bounds are too big for " << d_owner[0][v] << "." << std::endl; + return -1; } } - }else{ - Trace("bound-int-rsi") << d_owner[0][ii] << " has ground range, skip." << std::endl; + }else if( d_enum_type[v]==ENUM_SET_MEMBERS ){ + Assert( d_qe->getBoundedIntegers()->getBoundVarType( d_owner, d_owner[0][v] )==quantifiers::BoundedIntegers::BOUND_SET_MEMBER ); + Node srv = d_qe->getBoundedIntegers()->getSetRangeValue( d_owner, d_owner[0][v], this ); + if( srv.isNull() ){ + return -1; + } + Trace("bound-int-rsi") << "Bounded by set membership : " << srv << std::endl; + d_domain[v].clear(); + d_setm_bounds[v].clear(); + if( srv.getKind()!=EMPTYSET ){ + //TODO: need term model, not value model + while( srv.getKind()==UNION ){ + Assert( srv[1].getKind()==kind::SINGLETON ); + d_setm_bounds[v].push_back( srv[1][0] ); + srv = srv[0]; + } + d_setm_bounds[v].push_back( srv[0] ); + d_domain[v].push_back( d_setm_bounds[v].size() ); + }else{ + d_domain[v].push_back( 0 ); + return 0; + } } } - return true; + return 1; } -int RepSetIterator::increment2( int counter ){ +int RepSetIterator::increment2( int i ){ Assert( !isFinished() ); #ifdef DISABLE_EVAL_SKIP_MULTIPLE - counter = (int)d_index.size()-1; + i = (int)d_index.size()-1; #endif //increment d_index - if( counter>=0){ - Trace("rsi-debug") << "domain size of " << counter << " is " << domainSize(counter) << std::endl; + if( i>=0){ + Trace("rsi-debug") << "domain size of " << i << " is " << domainSize(i) << std::endl; } - while( counter>=0 && d_index[counter]>=(int)(domainSize(counter)-1) ){ - counter--; - if( counter>=0){ - Trace("rsi-debug") << "domain size of " << counter << " is " << domainSize(counter) << std::endl; + while( i>=0 && d_index[i]>=(int)(domainSize(i)-1) ){ + i--; + if( i>=0){ + Trace("rsi-debug") << "domain size of " << i << " is " << domainSize(i) << std::endl; } } - if( counter==-1 ){ + if( i==-1 ){ d_index.clear(); + return -1; }else{ - d_index[counter]++; - bool emptyDomain = false; - for( int i=(int)d_index.size()-1; i>counter; i-- ){ - if (!resetIndex(i)){ - break; - }else if( domainSize(i)<=0 ){ - emptyDomain = true; - } + Trace("rsi-debug") << "increment " << i << std::endl; + d_index[i]++; + return do_reset_increment( i ); + } +} + +int RepSetIterator::do_reset_increment( int i, bool initial ) { + bool emptyDomain = false; + for( unsigned ii=(i+1); ii<d_index.size(); ii++ ){ + int ri_res = resetIndex( ii, initial ); + if( ri_res==-1 ){ + //failed + d_index.clear(); + d_incomplete = true; + break; + }else if( ri_res==0 ){ + emptyDomain = true; } + //force next iteration if currently an empty domain if( emptyDomain ){ - Trace("rsi-debug") << "This is an empty domain, increment again." << std::endl; - return increment(); + d_index[ii] = domainSize(ii)-1; } } - return counter; + if( emptyDomain ){ + Trace("rsi-debug") << "This is an empty domain, increment." << std::endl; + return increment(); + }else{ + return i; + } } int RepSetIterator::increment(){ @@ -427,33 +445,38 @@ bool RepSetIterator::isFinished(){ return d_index.empty(); } -Node RepSetIterator::getTerm( int i ){ - Trace("rsi-debug") << "rsi : get term " << i << ", index order = " << d_index_order[i] << std::endl; - //int index = d_index_order[i]; - int index = i; - if( d_enum_type[index]==ENUM_DOMAIN_ELEMENTS ){ - TypeNode tn = d_types[index]; +Node RepSetIterator::getCurrentTerm( int v ){ + Trace("rsi-debug") << "rsi : get term " << v << ", index order = " << d_index_order[v] << std::endl; + int ii = d_index_order[v]; + int curr = d_index[ii]; + if( d_enum_type[v]==ENUM_DOMAIN_ELEMENTS ){ + TypeNode tn = d_types[v]; Assert( d_rep_set->d_type_reps.find( tn )!=d_rep_set->d_type_reps.end() ); - return d_rep_set->d_type_reps[tn][d_domain[index][d_index[index]]]; + Assert( 0<=d_domain[v][curr] && d_domain[v][curr]<(int)d_rep_set->d_type_reps[tn].size() ); + return d_rep_set->d_type_reps[tn][d_domain[v][curr]]; + }else if( d_enum_type[v]==ENUM_SET_MEMBERS ){ + Assert( 0<=curr && curr<(int)d_setm_bounds[v].size() ); + return d_setm_bounds[v][curr]; }else{ - Trace("rsi-debug") << "Get, with offset : " << index << " " << d_lower_bounds[index] << " " << d_index[index] << std::endl; - Node t = NodeManager::currentNM()->mkNode(PLUS, d_lower_bounds[index], - NodeManager::currentNM()->mkConst( Rational(d_index[index]) ) ); + Trace("rsi-debug") << "Get, with offset : " << v << " " << d_lower_bounds[v] << " " << curr << std::endl; + Assert( !d_lower_bounds[v].isNull() ); + Node t = NodeManager::currentNM()->mkNode(PLUS, d_lower_bounds[v], NodeManager::currentNM()->mkConst( Rational(curr) ) ); t = Rewriter::rewrite( t ); return t; } } void RepSetIterator::debugPrint( const char* c ){ - for( int i=0; i<(int)d_index.size(); i++ ){ - Debug( c ) << i << " : " << d_index[i] << " : " << getTerm( i ) << std::endl; + for( unsigned v=0; v<d_index.size(); v++ ){ + Debug( c ) << v << " : " << getCurrentTerm( v ) << std::endl; } } void RepSetIterator::debugPrintSmall( const char* c ){ Debug( c ) << "RI: "; - for( int i=0; i<(int)d_index.size(); i++ ){ - Debug( c ) << d_index[i] << ": " << getTerm( i ) << " "; + for( unsigned v=0; v<d_index.size(); v++ ){ + Debug( c ) << v << ": " << getCurrentTerm( v ) << " "; } Debug( c ) << std::endl; } + diff --git a/src/theory/rep_set.h b/src/theory/rep_set.h index 08fc7dd52..ee927de86 100644 --- a/src/theory/rep_set.h +++ b/src/theory/rep_set.h @@ -64,22 +64,42 @@ class RepSetIterator { public: enum { ENUM_DOMAIN_ELEMENTS, - ENUM_RANGE, + ENUM_INT_RANGE, + ENUM_SET_MEMBERS, }; private: QuantifiersEngine * d_qe; //initialize function bool initialize(); - //for enum ranges + //for int ranges std::map< int, Node > d_lower_bounds; + //for set ranges + std::map< int, std::vector< Node > > d_setm_bounds; //domain size int domainSize( int i ); //node this is rep set iterator is for Node d_owner; - //reset index - bool resetIndex( int i, bool initial = false ); + //reset index, 1:success, 0:empty, -1:fail + int resetIndex( int i, bool initial = false ); /** set index order */ void setIndexOrder( std::vector< int >& indexOrder ); + /** do reset increment the iterator at index=counter */ + int do_reset_increment( int counter, bool initial = false ); + //ordering for variables we are indexing over + // for example, given reps = { a, b } and quantifier forall( x, y, z ) P( x, y, z ) with d_index_order = { 2, 0, 1 }, + // then we consider instantiations in this order: + // a/x a/y a/z + // a/x b/y a/z + // b/x a/y a/z + // b/x b/y a/z + // ... + std::vector< int > d_index_order; + //variables to index they are considered at + // for example, if d_index_order = { 2, 0, 1 } + // then d_var_order = { 0 -> 1, 1 -> 2, 2 -> 0 } + std::map< int, int > d_var_order; + //are we only considering a strict subset of the domain of the quantifier? + bool d_incomplete; public: RepSetIterator( QuantifiersEngine * qe, RepSet* rs ); ~RepSetIterator(){} @@ -92,43 +112,34 @@ public: RepSet* d_rep_set; //enumeration type? std::vector< int > d_enum_type; - //index we are considering + //current tuple we are considering std::vector< int > d_index; //types we are considering std::vector< TypeNode > d_types; //domain we are considering std::vector< RepDomain > d_domain; - //are we only considering a strict subset of the domain of the quantifier? - bool d_incomplete; - //ordering for variables we are indexing over - // for example, given reps = { a, b } and quantifier forall( x, y, z ) P( x, y, z ) with d_index_order = { 2, 0, 1 }, - // then we consider instantiations in this order: - // a/x a/y a/z - // a/x b/y a/z - // b/x a/y a/z - // b/x b/y a/z - // ... - std::vector< int > d_index_order; - //variables to index they are considered at - // for example, if d_index_order = { 2, 0, 1 } - // then d_var_order = { 0 -> 1, 1 -> 2, 2 -> 0 } - std::map< int, int > d_var_order; //intervals std::map< int, Node > d_bounds[2]; public: /** increment the iterator at index=counter */ - int increment2( int counter ); + int increment2( int i ); /** increment the iterator */ int increment(); /** is the iterator finished? */ bool isFinished(); /** get the i_th term we are considering */ - Node getTerm( int i ); + Node getCurrentTerm( int v ); /** get the number of terms we are considering */ int getNumTerms() { return (int)d_index_order.size(); } /** debug print */ void debugPrint( const char* c ); void debugPrintSmall( const char* c ); + //get index order, returns var # + int getIndexOrder( int v ) { return d_index_order[v]; } + //get variable order, returns index # + int getVariableOrder( int i ) { return d_var_order[i]; } + //is incomplete + bool isIncomplete() { return d_incomplete; } };/* class RepSetIterator */ }/* CVC4::theory namespace */ diff --git a/src/theory/sep/kinds b/src/theory/sep/kinds new file mode 100644 index 000000000..6c4ad33db --- /dev/null +++ b/src/theory/sep/kinds @@ -0,0 +1,37 @@ +# kinds -*- sh -*- +# +# For documentation on this file format, please refer to +# src/theory/builtin/kinds. +# + +theory THEORY_SEP ::CVC4::theory::sep::TheorySep "theory/sep/theory_sep.h" +typechecker "theory/sep/theory_sep_type_rules.h" + +properties polite stable-infinite parametric +properties check propagate presolve getNextDecisionRequest + +rewriter ::CVC4::theory::sep::TheorySepRewriter "theory/sep/theory_sep_rewriter.h" + +# constants +constant SEP_NIL_REF \ + ::CVC4::NilRef \ + ::CVC4::NilRefHashFunction \ + "expr/sepconst.h" \ + "the nil reference constant; payload is an instance of the CVC4::NilRef class" + +variable SEP_NIL "separation nil" + +operator SEP_EMP 1 "separation empty heap" +operator SEP_PTO 2 "points to relation" +operator SEP_STAR 2: "separation star" +operator SEP_WAND 2 "separation magic wand" +operator SEP_LABEL 2 "separation label" + +typerule SEP_NIL_REF ::CVC4::theory::sep::SepNilRefTypeRule +typerule SEP_EMP ::CVC4::theory::sep::SepEmpTypeRule +typerule SEP_PTO ::CVC4::theory::sep::SepPtoTypeRule +typerule SEP_STAR ::CVC4::theory::sep::SepStarTypeRule +typerule SEP_WAND ::CVC4::theory::sep::SepWandTypeRule +typerule SEP_LABEL ::CVC4::theory::sep::SepLabelTypeRule + +endtheory diff --git a/src/theory/sep/theory_sep.cpp b/src/theory/sep/theory_sep.cpp new file mode 100644 index 000000000..836a04afa --- /dev/null +++ b/src/theory/sep/theory_sep.cpp @@ -0,0 +1,1534 @@ +/********************* */ +/*! \file theory_sep.cpp + ** \verbatim + ** Original author: Andrew Reynolds + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2014 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of the theory of sep. + ** + ** Implementation of the theory of sep. + **/ + + +#include "theory/sep/theory_sep.h" +#include "theory/valuation.h" +#include "expr/kind.h" +#include <map> +#include "theory/rewriter.h" +#include "theory/theory_model.h" +#include "options/sep_options.h" +#include "options/smt_options.h" +#include "smt/logic_exception.h" +#include "theory/quantifiers_engine.h" +#include "theory/quantifiers/term_database.h" + +using namespace std; + +namespace CVC4 { +namespace theory { +namespace sep { + +TheorySep::TheorySep(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : + Theory(THEORY_SEP, c, u, out, valuation, logicInfo), + d_notify(*this), + d_equalityEngine(d_notify, c, "theory::sep::TheorySep", true), + d_conflict(c, false), + d_reduce(u), + d_infer(c), + d_infer_exp(c), + d_spatial_assertions(c) +{ + d_true = NodeManager::currentNM()->mkConst<bool>(true); + d_false = NodeManager::currentNM()->mkConst<bool>(false); + + // The kinds we are treating as function application in congruence + d_equalityEngine.addFunctionKind(kind::SEP_PTO); + //d_equalityEngine.addFunctionKind(kind::SEP_STAR); +} + +TheorySep::~TheorySep() { + for( std::map< Node, HeapAssertInfo * >::iterator it = d_eqc_info.begin(); it != d_eqc_info.end(); ++it ){ + delete it->second; + } +} + +void TheorySep::setMasterEqualityEngine(eq::EqualityEngine* eq) { + d_equalityEngine.setMasterEqualityEngine(eq); +} + +Node TheorySep::mkAnd( std::vector< TNode >& assumptions ) { + if( assumptions.empty() ){ + return d_true; + }else if( assumptions.size()==1 ){ + return assumptions[0]; + }else{ + return NodeManager::currentNM()->mkNode( kind::AND, assumptions ); + } +} + +///////////////////////////////////////////////////////////////////////////// +// PREPROCESSING +///////////////////////////////////////////////////////////////////////////// + + + +Node TheorySep::ppRewrite(TNode term) { + Trace("sep-pp") << "ppRewrite : " << term << std::endl; +/* + Node s_atom = term.getKind()==kind::NOT ? term[0] : term; + if( s_atom.getKind()==kind::SEP_PTO || s_atom.getKind()==kind::SEP_STAR || s_atom.getKind()==kind::SEP_WAND || s_atom.getKind()==kind::SEP_EMP ){ + //get the reference type (will compute d_type_references) + int card = 0; + TypeNode tn = getReferenceType( s_atom, card ); + Trace("sep-pp") << " reference type is " << tn << ", card is " << card << std::endl; + } +*/ + return term; +} + +//must process assertions at preprocess so that quantified assertions are processed properly +void TheorySep::processAssertions( std::vector< Node >& assertions ) { + std::map< Node, bool > visited; + for( unsigned i=0; i<assertions.size(); i++ ){ + processAssertion( assertions[i], visited ); + } +} + +void TheorySep::processAssertion( Node n, std::map< Node, bool >& visited ) { + if( visited.find( n )==visited.end() ){ + visited[n] = true; + if( n.getKind()==kind::SEP_PTO || n.getKind()==kind::SEP_STAR || n.getKind()==kind::SEP_WAND || n.getKind()==kind::SEP_EMP ){ + //get the reference type (will compute d_type_references) + int card = 0; + TypeNode tn = getReferenceType( n, card ); + Trace("sep-pp") << " reference type is " << tn << ", card is " << card << std::endl; + }else{ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + processAssertion( n[i], visited ); + } + } + } +} + +Theory::PPAssertStatus TheorySep::ppAssert(TNode in, SubstitutionMap& outSubstitutions) { + + return PP_ASSERT_STATUS_UNSOLVED; +} + + +///////////////////////////////////////////////////////////////////////////// +// T-PROPAGATION / REGISTRATION +///////////////////////////////////////////////////////////////////////////// + + +bool TheorySep::propagate(TNode literal) +{ + Debug("sep") << "TheorySep::propagate(" << literal << ")" << std::endl; + // If already in conflict, no more propagation + if (d_conflict) { + Debug("sep") << "TheorySep::propagate(" << literal << "): already in conflict" << std::endl; + return false; + } + bool ok = d_out->propagate(literal); + if (!ok) { + d_conflict = true; + } + return ok; +}/* TheorySep::propagate(TNode) */ + + +void TheorySep::explain(TNode literal, std::vector<TNode>& assumptions) { + if( literal.getKind()==kind::SEP_LABEL || + ( literal.getKind()==kind::NOT && literal[0].getKind()==kind::SEP_LABEL ) ){ + //labelled assertions are never given to equality engine and should only come from the outside + assumptions.push_back( literal ); + }else{ + // Do the work + bool polarity = literal.getKind() != kind::NOT; + TNode atom = polarity ? literal : literal[0]; + if (atom.getKind() == kind::EQUAL || atom.getKind() == kind::IFF) { + d_equalityEngine.explainEquality( atom[0], atom[1], polarity, assumptions, NULL ); + } else { + d_equalityEngine.explainPredicate( atom, polarity, assumptions ); + } + } +} + +void TheorySep::preRegisterTermRec(TNode t, std::map< TNode, bool >& visited ) { + if( visited.find( t )==visited.end() ){ + visited[t] = true; + Trace("sep-prereg-debug") << "Preregister : " << t << std::endl; + if( t.getKind()==kind::SEP_NIL ){ + Trace("sep-prereg") << "Preregister nil : " << t << std::endl; + //per type, all nil variable references are equal + TypeNode tn = t.getType(); + std::map< TypeNode, Node >::iterator it = d_nil_ref.find( tn ); + if( it==d_nil_ref.end() ){ + Trace("sep-prereg") << "...set as reference." << std::endl; + d_nil_ref[tn] = t; + }else{ + Node nr = it->second; + Trace("sep-prereg") << "...reference is " << nr << "." << std::endl; + if( t!=nr ){ + if( d_reduce.find( t )==d_reduce.end() ){ + d_reduce.insert( t ); + Node lem = NodeManager::currentNM()->mkNode( tn.isBoolean() ? kind::IFF : kind::EQUAL, t, nr ); + Trace("sep-lemma") << "Sep::Lemma: nil ref eq : " << lem << std::endl; + d_out->lemma( lem ); + } + } + } + }else{ + for( unsigned i=0; i<t.getNumChildren(); i++ ){ + preRegisterTermRec( t[i], visited ); + } + } + } +} + +void TheorySep::preRegisterTerm(TNode term){ + std::map< TNode, bool > visited; + preRegisterTermRec( term, visited ); +} + + +void TheorySep::propagate(Effort e){ + +} + + +Node TheorySep::explain(TNode literal) +{ + Debug("sep") << "TheorySep::explain(" << literal << ")" << std::endl; + std::vector<TNode> assumptions; + explain(literal, assumptions); + return mkAnd(assumptions); +} + + +///////////////////////////////////////////////////////////////////////////// +// SHARING +///////////////////////////////////////////////////////////////////////////// + + +void TheorySep::addSharedTerm(TNode t) { + Debug("sep") << "TheorySep::addSharedTerm(" << t << ")" << std::endl; + d_equalityEngine.addTriggerTerm(t, THEORY_SEP); +} + + +EqualityStatus TheorySep::getEqualityStatus(TNode a, TNode b) { + Assert(d_equalityEngine.hasTerm(a) && d_equalityEngine.hasTerm(b)); + if (d_equalityEngine.areEqual(a, b)) { + // The terms are implied to be equal + return EQUALITY_TRUE; + } + else if (d_equalityEngine.areDisequal(a, b, false)) { + // The terms are implied to be dis-equal + return EQUALITY_FALSE; + } + return EQUALITY_UNKNOWN;//FALSE_IN_MODEL; +} + + +void TheorySep::computeCareGraph() { + Debug("sharing") << "Theory::computeCareGraph<" << getId() << ">()" << endl; + for (unsigned i = 0; i < d_sharedTerms.size(); ++ i) { + TNode a = d_sharedTerms[i]; + TypeNode aType = a.getType(); + for (unsigned j = i + 1; j < d_sharedTerms.size(); ++ j) { + TNode b = d_sharedTerms[j]; + if (b.getType() != aType) { + // We don't care about the terms of different types + continue; + } + switch (d_valuation.getEqualityStatus(a, b)) { + case EQUALITY_TRUE_AND_PROPAGATED: + case EQUALITY_FALSE_AND_PROPAGATED: + // If we know about it, we should have propagated it, so we can skip + break; + default: + // Let's split on it + addCarePair(a, b); + break; + } + } + } +} + + +///////////////////////////////////////////////////////////////////////////// +// MODEL GENERATION +///////////////////////////////////////////////////////////////////////////// + + +void TheorySep::collectModelInfo( TheoryModel* m, bool fullModel ) +{ + // Send the equality engine information to the model + m->assertEqualityEngine( &d_equalityEngine ); + + if( fullModel ){ + for( std::map< TypeNode, Node >::iterator it = d_base_label.begin(); it != d_base_label.end(); ++it ){ + Trace("sep-model") << "; Model for heap, type = " << it->first << " : " << std::endl; + computeLabelModel( it->second, d_tmodel ); + if( d_label_model[it->second].d_heap_locs_model.empty() ){ + Trace("sep-model") << "; [empty]" << std::endl; + }else{ + for( unsigned j=0; j<d_label_model[it->second].d_heap_locs_model.size(); j++ ){ + Assert( d_label_model[it->second].d_heap_locs_model[j].getKind()==kind::SINGLETON ); + Node l = d_label_model[it->second].d_heap_locs_model[j][0]; + Trace("sep-model") << "; " << l << " -> "; + if( d_pto_model[l].isNull() ){ + Trace("sep-model") << "_"; + }else{ + Trace("sep-model") << d_pto_model[l]; + } + Trace("sep-model") << std::endl; + } + } + Node nil = getNilRef( it->first ); + Node vnil = d_valuation.getModel()->getRepresentative( nil ); + Trace("sep-model") << "; sep.nil = " << vnil << std::endl; + Trace("sep-model") << std::endl; + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// NOTIFICATIONS +///////////////////////////////////////////////////////////////////////////// + + +void TheorySep::presolve() { + Trace("sep-pp") << "Presolving" << std::endl; + //TODO: cleanup if incremental? +} + + +///////////////////////////////////////////////////////////////////////////// +// MAIN SOLVER +///////////////////////////////////////////////////////////////////////////// + + +void TheorySep::check(Effort e) { + if (done() && !fullEffort(e) && e != EFFORT_LAST_CALL) { + return; + } + + getOutputChannel().spendResource(options::theoryCheckStep()); + + TimerStat::CodeTimer checkTimer(d_checkTime); + Trace("sep-check") << "Sep::check(): " << e << endl; + + while( !done() && !d_conflict ){ + // Get all the assertions + Assertion assertion = get(); + TNode fact = assertion.assertion; + + Trace("sep-assert") << "TheorySep::check(): processing " << fact << std::endl; + + bool polarity = fact.getKind() != kind::NOT; + TNode atom = polarity ? fact : fact[0]; + if( atom.getKind()==kind::SEP_EMP ){ + TypeNode tn = atom[0].getType(); + if( d_emp_arg.find( tn )==d_emp_arg.end() ){ + d_emp_arg[tn] = atom[0]; + }else{ + //normalize argument TODO + } + } + TNode s_atom = atom.getKind()==kind::SEP_LABEL ? atom[0] : atom; + TNode s_lbl = atom.getKind()==kind::SEP_LABEL ? atom[1] : TNode::null(); + bool is_spatial = s_atom.getKind()==kind::SEP_STAR || s_atom.getKind()==kind::SEP_WAND || s_atom.getKind()==kind::SEP_PTO || s_atom.getKind()==kind::SEP_EMP; + if( is_spatial && s_lbl.isNull() ){ + if( d_reduce.find( fact )==d_reduce.end() ){ + Trace("sep-lemma-debug") << "Reducing unlabelled assertion " << atom << std::endl; + d_reduce.insert( fact ); + //introduce top-level label, add iff + int card; + TypeNode refType = getReferenceType( s_atom, card ); + Trace("sep-lemma-debug") << "...reference type is : " << refType << ", card is " << card << std::endl; + Node b_lbl = getBaseLabel( refType ); + Node s_atom_new = NodeManager::currentNM()->mkNode( kind::SEP_LABEL, s_atom, b_lbl ); + Node lem; + if( polarity ){ + lem = NodeManager::currentNM()->mkNode( kind::OR, s_atom.negate(), s_atom_new ); + }else{ + lem = NodeManager::currentNM()->mkNode( kind::OR, s_atom, s_atom_new.negate() ); + } + Trace("sep-lemma-debug") << "Sep::Lemma : base reduction : " << lem << std::endl; + d_out->lemma( lem ); + } + }else{ + //do reductions + if( is_spatial ){ + if( d_reduce.find( fact )==d_reduce.end() ){ + Trace("sep-lemma-debug") << "Reducing assertion " << fact << std::endl; + d_reduce.insert( fact ); + Node conc; + std::map< Node, Node >::iterator its = d_red_conc[s_lbl].find( s_atom ); + if( its==d_red_conc[s_lbl].end() ){ + //make conclusion based on type of assertion + if( s_atom.getKind()==kind::SEP_STAR || s_atom.getKind()==kind::SEP_WAND ){ + std::vector< Node > children; + std::vector< Node > c_lems; + int card; + TypeNode tn = getReferenceType( s_atom, card ); + Assert( d_reference_bound.find( tn )!=d_reference_bound.end() ); + c_lems.push_back( NodeManager::currentNM()->mkNode( kind::SUBSET, s_lbl, d_reference_bound[tn] ) ); + if( options::sepPreciseBound() ){ + //more precise bound + Trace("sep-bound") << "Propagate Bound(" << s_lbl << ") = "; + Assert( d_lbl_reference_bound.find( s_lbl )!=d_lbl_reference_bound.end() ); + for( unsigned j=0; j<d_lbl_reference_bound[s_lbl].size(); j++ ){ + Trace("sep-bound") << d_lbl_reference_bound[s_lbl][j] << " "; + } + Trace("sep-bound") << std::endl << " to children of " << s_atom << std::endl; + //int rb_start = 0; + for( unsigned j=0; j<s_atom.getNumChildren(); j++ ){ + int ccard = 0; + getReferenceType( s_atom, ccard, j ); + Node c_lbl = getLabel( s_atom, j, s_lbl ); + Trace("sep-bound") << " for " << c_lbl << ", card = " << ccard << " : "; + std::vector< Node > bound_loc; + bound_loc.insert( bound_loc.end(), d_references[s_atom][j].begin(), d_references[s_atom][j].end() ); +/* //this is unsound + for( int k=0; k<ccard; k++ ){ + Assert( rb_start<(int)d_lbl_reference_bound[s_lbl].size() ); + d_lbl_reference_bound[c_lbl].push_back( d_lbl_reference_bound[s_lbl][rb_start] ); + Trace("sep-bound") << d_lbl_reference_bound[s_lbl][rb_start] << " "; + bound_loc.push_back( d_lbl_reference_bound[s_lbl][rb_start] ); + rb_start++; + } +*/ + //carry all locations for now + bound_loc.insert( bound_loc.end(), d_lbl_reference_bound[s_lbl].begin(), d_lbl_reference_bound[s_lbl].end() ); + Trace("sep-bound") << std::endl; + Node bound_v = mkUnion( tn, bound_loc ); + Trace("sep-bound") << " ...bound value : " << bound_v << std::endl; + children.push_back( NodeManager::currentNM()->mkNode( kind::SUBSET, c_lbl, bound_v ) ); + } + Trace("sep-bound") << "Done propagate Bound(" << s_lbl << ")" << std::endl; + } + std::vector< Node > labels; + getLabelChildren( s_atom, s_lbl, children, labels ); + Node empSet = NodeManager::currentNM()->mkConst(EmptySet(s_lbl.getType().toType())); + Assert( children.size()>1 ); + if( s_atom.getKind()==kind::SEP_STAR ){ + //reduction for heap : union, pairwise disjoint + Node ulem = NodeManager::currentNM()->mkNode( kind::UNION, labels[0], labels[1] ); + for( unsigned i=2; i<labels.size(); i++ ){ + ulem = NodeManager::currentNM()->mkNode( kind::UNION, ulem, labels[i] ); + } + ulem = s_lbl.eqNode( ulem ); + Trace("sep-lemma-debug") << "Sep::Lemma : star reduction, union : " << ulem << std::endl; + c_lems.push_back( ulem ); + for( unsigned i=0; i<labels.size(); i++ ){ + for( unsigned j=(i+1); j<labels.size(); j++ ){ + Node s = NodeManager::currentNM()->mkNode( kind::INTERSECTION, labels[i], labels[j] ); + Node ilem = s.eqNode( empSet ); + Trace("sep-lemma-debug") << "Sep::Lemma : star reduction, disjoint : " << ilem << std::endl; + c_lems.push_back( ilem ); + } + } + }else{ + Node ulem = NodeManager::currentNM()->mkNode( kind::UNION, s_lbl, labels[0] ); + ulem = ulem.eqNode( labels[1] ); + Trace("sep-lemma-debug") << "Sep::Lemma : wand reduction, union : " << ulem << std::endl; + c_lems.push_back( ulem ); + Node s = NodeManager::currentNM()->mkNode( kind::INTERSECTION, s_lbl, labels[0] ); + Node ilem = s.eqNode( empSet ); + Trace("sep-lemma-debug") << "Sep::Lemma : wand reduction, disjoint : " << ilem << std::endl; + c_lems.push_back( ilem ); + } + //send out definitional lemmas for introduced sets + for( unsigned j=0; j<c_lems.size(); j++ ){ + Trace("sep-lemma") << "Sep::Lemma : definition : " << c_lems[j] << std::endl; + d_out->lemma( c_lems[j] ); + } + //children.insert( children.end(), c_lems.begin(), c_lems.end() ); + conc = NodeManager::currentNM()->mkNode( kind::AND, children ); + }else if( s_atom.getKind()==kind::SEP_PTO ){ + Node ss = NodeManager::currentNM()->mkNode( kind::SINGLETON, s_atom[0] ); + if( s_lbl!=ss ){ + conc = s_lbl.eqNode( ss ); + } + Node ssn = NodeManager::currentNM()->mkNode( kind::EQUAL, s_atom[0], getNilRef(s_atom[0].getType()) ).negate(); + conc = conc.isNull() ? ssn : NodeManager::currentNM()->mkNode( kind::AND, conc, ssn ); + }else{ + //labeled emp should be rewritten + Assert( false ); + } + d_red_conc[s_lbl][s_atom] = conc; + }else{ + conc = its->second; + } + if( !conc.isNull() ){ + bool use_polarity = s_atom.getKind()==kind::SEP_WAND ? !polarity : polarity; + if( !use_polarity ){ + // introduce guard, assert positive version + Trace("sep-lemma-debug") << "Negated spatial constraint asserted to sep theory: " << fact << std::endl; + Node lit = Rewriter::rewrite( NodeManager::currentNM()->mkSkolem( "G", NodeManager::currentNM()->booleanType() ) ); + lit = getValuation().ensureLiteral( lit ); + d_neg_guard[s_lbl][s_atom] = lit; + Trace("sep-lemma-debug") << "Neg guard : " << s_lbl << " " << s_atom << " " << lit << std::endl; + AlwaysAssert( !lit.isNull() ); + d_out->requirePhase( lit, true ); + d_neg_guards.push_back( lit ); + d_guard_to_assertion[lit] = s_atom; + //Node lem = NodeManager::currentNM()->mkNode( kind::IFF, lit, conc ); + Node lem = NodeManager::currentNM()->mkNode( kind::OR, lit.negate(), conc ); + Trace("sep-lemma") << "Sep::Lemma : (neg) reduction : " << lem << std::endl; + d_out->lemma( lem ); + }else{ + //reduce based on implication + Node ant = atom; + if( polarity ){ + ant = atom.negate(); + } + Node lem = NodeManager::currentNM()->mkNode( kind::OR, ant, conc ); + Trace("sep-lemma") << "Sep::Lemma : reduction : " << lem << std::endl; + d_out->lemma( lem ); + } + }else{ + Trace("sep-lemma-debug") << "Trivial conclusion, do not add lemma." << std::endl; + } + } + } + //assert to equality engine + if( !is_spatial ){ + Trace("sep-assert") << "Asserting " << atom << ", pol = " << polarity << " to EE..." << std::endl; + if( s_atom.getKind()==kind::EQUAL ){ + d_equalityEngine.assertEquality(atom, polarity, fact); + }else{ + d_equalityEngine.assertPredicate(atom, polarity, fact); + } + Trace("sep-assert") << "Done asserting " << atom << " to EE." << std::endl; + }else if( s_atom.getKind()==kind::SEP_PTO ){ + Node pto_lbl = NodeManager::currentNM()->mkNode( kind::SINGLETON, s_atom[0] ); + if( polarity && s_lbl!=pto_lbl ){ + //also propagate equality + Node eq = s_lbl.eqNode( pto_lbl ); + Trace("sep-assert") << "Asserting implied equality " << eq << " to EE..." << std::endl; + d_equalityEngine.assertEquality(eq, true, fact); + Trace("sep-assert") << "Done asserting implied equality " << eq << " to EE." << std::endl; + } + //associate the equivalence class of the lhs with this pto + Node r = getRepresentative( s_lbl ); + HeapAssertInfo * ei = getOrMakeEqcInfo( r, true ); + addPto( ei, r, atom, polarity ); + } + //maybe propagate + doPendingFacts(); + //add to spatial assertions + if( !d_conflict && is_spatial ){ + d_spatial_assertions.push_back( fact ); + } + } + } + + if( e == EFFORT_LAST_CALL && !d_conflict && !d_valuation.needCheck() ){ + Trace("sep-process") << "Checking heap at full effort..." << std::endl; + d_label_model.clear(); + d_tmodel.clear(); + d_pto_model.clear(); + Trace("sep-process") << "---Locations---" << std::endl; + for( std::map< TypeNode, std::vector< Node > >::iterator itt = d_type_references_all.begin(); itt != d_type_references_all.end(); ++itt ){ + for( unsigned k=0; k<itt->second.size(); k++ ){ + Node t = itt->second[k]; + Trace("sep-process") << " " << t << " = "; + if( d_valuation.getModel()->hasTerm( t ) ){ + Node v = d_valuation.getModel()->getRepresentative( t ); + Trace("sep-process") << v << std::endl; + d_tmodel[v] = t; + }else{ + Trace("sep-process") << "?" << std::endl; + } + } + } + Trace("sep-process") << "---" << std::endl; + //build positive/negative assertion lists for labels + std::map< Node, bool > assert_active; + //get the inactive assertions + std::map< Node, std::vector< Node > > lbl_to_assertions; + for( NodeList::const_iterator i = d_spatial_assertions.begin(); i != d_spatial_assertions.end(); ++i ) { + Node fact = (*i); + bool polarity = fact.getKind() != kind::NOT; + TNode atom = polarity ? fact : fact[0]; + Assert( atom.getKind()==kind::SEP_LABEL ); + TNode s_atom = atom[0]; + TNode s_lbl = atom[1]; + lbl_to_assertions[s_lbl].push_back( fact ); + //check whether assertion is active : either polarity=true, or guard is not asserted false + assert_active[fact] = true; + bool use_polarity = s_atom.getKind()==kind::SEP_WAND ? !polarity : polarity; + if( use_polarity ){ + if( s_atom.getKind()==kind::SEP_PTO ){ + Node vv = d_valuation.getModel()->getRepresentative( s_atom[0] ); + if( d_pto_model.find( vv )==d_pto_model.end() ){ + Trace("sep-inst") << "Pto : " << s_atom[0] << " (" << vv << ") -> " << s_atom[1] << std::endl; + d_pto_model[vv] = s_atom[1]; + } + } + }else{ + if( d_neg_guard[s_lbl].find( s_atom )!=d_neg_guard[s_lbl].end() ){ + //check if the guard is asserted positively + Node guard = d_neg_guard[s_lbl][s_atom]; + bool value; + if( getValuation().hasSatValue( guard, value ) ) { + assert_active[fact] = value; + } + } + } + } + //(recursively) set inactive sub-assertions + for( NodeList::const_iterator i = d_spatial_assertions.begin(); i != d_spatial_assertions.end(); ++i ) { + Node fact = (*i); + if( !assert_active[fact] ){ + setInactiveAssertionRec( fact, lbl_to_assertions, assert_active ); + } + } + //set up model information based on active assertions + for( NodeList::const_iterator i = d_spatial_assertions.begin(); i != d_spatial_assertions.end(); ++i ) { + Node fact = (*i); + bool polarity = fact.getKind() != kind::NOT; + TNode atom = polarity ? fact : fact[0]; + TNode s_atom = atom[0]; + TNode s_lbl = atom[1]; + if( assert_active[fact] ){ + computeLabelModel( s_lbl, d_tmodel ); + } + } + //debug print + if( Trace.isOn("sep-process") ){ + Trace("sep-process") << "--- Current spatial assertions : " << std::endl; + for( NodeList::const_iterator i = d_spatial_assertions.begin(); i != d_spatial_assertions.end(); ++i ) { + Node fact = (*i); + Trace("sep-process") << " " << fact; + if( !assert_active[fact] ){ + Trace("sep-process") << " [inactive]"; + } + Trace("sep-process") << std::endl; + } + Trace("sep-process") << "---" << std::endl; + } + if(Trace.isOn("sep-eqc")) { + eq::EqClassesIterator eqcs2_i = eq::EqClassesIterator( &d_equalityEngine ); + Trace("sep-eqc") << "EQC:" << std::endl; + while( !eqcs2_i.isFinished() ){ + Node eqc = (*eqcs2_i); + eq::EqClassIterator eqc2_i = eq::EqClassIterator( eqc, &d_equalityEngine ); + Trace("sep-eqc") << "Eqc( " << eqc << " ) : { "; + while( !eqc2_i.isFinished() ) { + if( (*eqc2_i)!=eqc ){ + Trace("sep-eqc") << (*eqc2_i) << " "; + } + ++eqc2_i; + } + Trace("sep-eqc") << " } " << std::endl; + ++eqcs2_i; + } + Trace("sep-eqc") << std::endl; + } + + if( options::sepCheckNeg() ){ + //get active labels + std::map< Node, bool > active_lbl; + if( options::sepMinimalRefine() ){ + for( NodeList::const_iterator i = d_spatial_assertions.begin(); i != d_spatial_assertions.end(); ++i ) { + Node fact = (*i); + bool polarity = fact.getKind() != kind::NOT; + TNode atom = polarity ? fact : fact[0]; + TNode s_atom = atom[0]; + bool use_polarity = s_atom.getKind()==kind::SEP_WAND ? !polarity : polarity; + if( !use_polarity ){ + Assert( assert_active.find( fact )!=assert_active.end() ); + if( assert_active[fact] ){ + Assert( atom.getKind()==kind::SEP_LABEL ); + TNode s_lbl = atom[1]; + if( d_label_map[s_atom].find( s_lbl )!=d_label_map[s_atom].end() ){ + Trace("sep-process-debug") << "Active lbl : " << s_lbl << std::endl; + active_lbl[s_lbl] = true; + } + } + } + } + } + + //process spatial assertions + bool addedLemma = false; + bool needAddLemma = false; + for( NodeList::const_iterator i = d_spatial_assertions.begin(); i != d_spatial_assertions.end(); ++i ) { + Node fact = (*i); + bool polarity = fact.getKind() != kind::NOT; + TNode atom = polarity ? fact : fact[0]; + TNode s_atom = atom[0]; + + bool use_polarity = s_atom.getKind()==kind::SEP_WAND ? !polarity : polarity; + Trace("sep-process-debug") << " check atom : " << s_atom << " use polarity " << use_polarity << std::endl; + if( !use_polarity ){ + Assert( assert_active.find( fact )!=assert_active.end() ); + if( assert_active[fact] ){ + Assert( atom.getKind()==kind::SEP_LABEL ); + TNode s_lbl = atom[1]; + Trace("sep-process") << "--> Active negated atom : " << s_atom << ", lbl = " << s_lbl << std::endl; + //add refinement lemma + if( d_label_map[s_atom].find( s_lbl )!=d_label_map[s_atom].end() ){ + needAddLemma = true; + int card; + TypeNode tn = getReferenceType( s_atom, card ); + tn = NodeManager::currentNM()->mkSetType(tn); + //tn = NodeManager::currentNM()->mkSetType(NodeManager::currentNM()->mkRefType(tn)); + Node o_b_lbl_mval = d_label_model[s_lbl].getValue( tn ); + Trace("sep-process") << " Model for " << s_lbl << " : " << o_b_lbl_mval << std::endl; + + //get model values + std::map< int, Node > mvals; + for( std::map< int, Node >::iterator itl = d_label_map[s_atom][s_lbl].begin(); itl != d_label_map[s_atom][s_lbl].end(); ++itl ){ + Node sub_lbl = itl->second; + int sub_index = itl->first; + computeLabelModel( sub_lbl, d_tmodel ); + Node lbl_mval = d_label_model[sub_lbl].getValue( tn ); + Trace("sep-process-debug") << " child " << sub_index << " : " << sub_lbl << ", mval = " << lbl_mval << std::endl; + mvals[sub_index] = lbl_mval; + } + + // Now, assert model-instantiated implication based on the negation + Assert( d_label_model.find( s_lbl )!=d_label_model.end() ); + std::vector< Node > conc; + bool inst_success = true; + if( options::sepExp() ){ + //old refinement lemmas + for( std::map< int, Node >::iterator itl = d_label_map[s_atom][s_lbl].begin(); itl != d_label_map[s_atom][s_lbl].end(); ++itl ){ + int sub_index = itl->first; + std::map< Node, Node > visited; + Node c = applyLabel( s_atom[itl->first], mvals[sub_index], visited ); + Trace("sep-process-debug") << " applied inst : " << c << std::endl; + if( s_atom.getKind()==kind::SEP_STAR || sub_index==0 ){ + conc.push_back( c.negate() ); + }else{ + conc.push_back( c ); + } + } + }else{ + //new refinement + std::map< Node, Node > visited; + Node inst = instantiateLabel( s_atom, s_lbl, s_lbl, o_b_lbl_mval, visited, d_pto_model, d_tmodel, tn, active_lbl ); + Trace("sep-inst-debug") << " applied inst : " << inst << std::endl; + if( inst.isNull() ){ + inst_success = false; + }else{ + inst = Rewriter::rewrite( inst ); + if( inst==( polarity ? d_true : d_false ) ){ + inst_success = false; + } + conc.push_back( polarity ? inst : inst.negate() ); + } + } + if( inst_success ){ + std::vector< Node > lemc; + Node pol_atom = atom; + if( polarity ){ + pol_atom = atom.negate(); + } + lemc.push_back( pol_atom ); + //lemc.push_back( s_lbl.eqNode( o_b_lbl_mval ).negate() ); + //lemc.push_back( NodeManager::currentNM()->mkNode( kind::SUBSET, o_b_lbl_mval, s_lbl ).negate() ); + lemc.insert( lemc.end(), conc.begin(), conc.end() ); + Node lem = NodeManager::currentNM()->mkNode( kind::OR, lemc ); + if( std::find( d_refinement_lem[s_atom][s_lbl].begin(), d_refinement_lem[s_atom][s_lbl].end(), lem )==d_refinement_lem[s_atom][s_lbl].end() ){ + d_refinement_lem[s_atom][s_lbl].push_back( lem ); + Trace("sep-process") << "-----> refinement lemma (#" << d_refinement_lem[s_atom][s_lbl].size() << ") : " << lem << std::endl; + Trace("sep-lemma") << "Sep::Lemma : negated star/wand refinement : " << lem << std::endl; + d_out->lemma( lem ); + addedLemma = true; + }else{ + Trace("sep-process") << "*** repeated refinement lemma : " << lem << std::endl; + } + } + }else{ + Trace("sep-process-debug") << " no children." << std::endl; + Assert( s_atom.getKind()==kind::SEP_PTO ); + } + }else{ + Trace("sep-process-debug") << "--> inactive negated assertion " << s_atom << std::endl; + } + } + } + if( !addedLemma ){ + if( needAddLemma ){ + Trace("sep-process") << "WARNING : could not find refinement lemma!!!" << std::endl; + Assert( false ); + d_out->setIncomplete(); + } + } + } + } + Trace("sep-check") << "Sep::check(): " << e << " done, conflict=" << d_conflict.get() << endl; +} + + +Node TheorySep::getNextDecisionRequest() { + for( unsigned i=0; i<d_neg_guards.size(); i++ ){ + Node g = d_neg_guards[i]; + bool success = true; + if( getLogicInfo().isQuantified() ){ + Assert( d_guard_to_assertion.find( g )!= d_guard_to_assertion.end() ); + Node a = d_guard_to_assertion[g]; + Node q = quantifiers::TermDb::getInstConstAttr( a ); + if( !q.isNull() ){ + //must wait to decide on counterexample literal from quantified formula + Node cel = getQuantifiersEngine()->getTermDatabase()->getCounterexampleLiteral( q ); + bool value; + if( d_valuation.hasSatValue( cel, value ) ){ + success = value; + }else{ + Trace("sep-dec-debug") << "TheorySep::getNextDecisionRequest : wait to decide on " << g << " until " << cel << " is set " << std::endl; + success = false; + } + } + } + if( success ){ + bool value; + if( !d_valuation.hasSatValue( g, value ) ) { + Trace("sep-dec") << "TheorySep::getNextDecisionRequest : " << g << " (" << i << "/" << d_neg_guards.size() << ")" << std::endl; + return g; + } + } + } + Trace("sep-dec-debug") << "TheorySep::getNextDecisionRequest : null" << std::endl; + return Node::null(); +} + +void TheorySep::conflict(TNode a, TNode b) { + Trace("sep-conflict") << "Sep::conflict : " << a << " " << b << std::endl; + Node conflictNode; + if (a.getKind() == kind::CONST_BOOLEAN) { + conflictNode = explain(a.iffNode(b)); + } else { + conflictNode = explain(a.eqNode(b)); + } + d_conflict = true; + d_out->conflict( conflictNode ); +} + + +TheorySep::HeapAssertInfo::HeapAssertInfo( context::Context* c ) : d_pto(c), d_has_neg_pto(c,false) { + +} + +TheorySep::HeapAssertInfo * TheorySep::getOrMakeEqcInfo( Node n, bool doMake ) { + std::map< Node, HeapAssertInfo* >::iterator e_i = d_eqc_info.find( n ); + if( e_i==d_eqc_info.end() ){ + if( doMake ){ + HeapAssertInfo* ei = new HeapAssertInfo( getSatContext() ); + d_eqc_info[n] = ei; + return ei; + }else{ + return NULL; + } + }else{ + return (*e_i).second; + } +} + +TypeNode TheorySep::getReferenceType( Node atom, int& card, int index ) { + Trace("sep-type") << "getReference type " << atom << " " << index << std::endl; + Assert( atom.getKind()==kind::SEP_PTO || atom.getKind()==kind::SEP_STAR || atom.getKind()==kind::SEP_WAND || atom.getKind()==kind::SEP_EMP || index!=-1 ); + std::map< int, TypeNode >::iterator it = d_reference_type[atom].find( index ); + if( it==d_reference_type[atom].end() ){ + card = 0; + TypeNode tn; + if( index==-1 && ( atom.getKind()==kind::SEP_STAR || atom.getKind()==kind::SEP_WAND ) ){ + for( unsigned i=0; i<atom.getNumChildren(); i++ ){ + int cardc = 0; + TypeNode ctn = getReferenceType( atom, cardc, i ); + if( !ctn.isNull() ){ + tn = ctn; + } + for( unsigned j=0; j<d_references[atom][i].size(); j++ ){ + if( std::find( d_references[atom][index].begin(), d_references[atom][index].end(), d_references[atom][i][j] )==d_references[atom][index].end() ){ + d_references[atom][index].push_back( d_references[atom][i][j] ); + } + } + card = card + cardc; + } + }else{ + Node n = index==-1 ? atom : atom[index]; + //will compute d_references as well + std::map< Node, int > visited; + tn = getReferenceType2( atom, card, index, n, visited ); + } + if( tn.isNull() && index==-1 ){ + tn = NodeManager::currentNM()->booleanType(); + } + d_reference_type[atom][index] = tn; + d_reference_type_card[atom][index] = card; + Trace("sep-type") << "...ref type return " << card << " for " << atom << " " << index << std::endl; + //add to d_type_references + if( index==-1 ){ + //only contributes if top-level (index=-1) + for( unsigned i=0; i<d_references[atom][index].size(); i++ ){ + Assert( !d_references[atom][index][i].isNull() ); + if( std::find( d_type_references[tn].begin(), d_type_references[tn].end(), d_references[atom][index][i] )==d_type_references[tn].end() ){ + d_type_references[tn].push_back( d_references[atom][index][i] ); + } + } + // update maximum cardinality value + if( card>(int)d_card_max[tn] ){ + d_card_max[tn] = card; + } + } + return tn; + }else{ + Assert( d_reference_type_card[atom].find( index )!=d_reference_type_card[atom].end() ); + card = d_reference_type_card[atom][index]; + return it->second; + } +} + +TypeNode TheorySep::getReferenceType2( Node atom, int& card, int index, Node n, std::map< Node, int >& visited ) { + if( visited.find( n )==visited.end() ){ + Trace("sep-type-debug") << "visit : " << n << " : " << atom << " " << index << std::endl; + visited[n] = -1; + if( n.getKind()==kind::SEP_PTO ){ + TypeNode tn1 = n[0].getType(); + TypeNode tn2 = n[1].getType(); + if( quantifiers::TermDb::hasBoundVarAttr( n[0] ) ){ + d_reference_bound_invalid[tn1] = true; + }else{ + if( std::find( d_references[atom][index].begin(), d_references[atom][index].end(), n[0] )==d_references[atom][index].end() ){ + d_references[atom][index].push_back( n[0] ); + } + } + std::map< TypeNode, TypeNode >::iterator itt = d_loc_to_data_type.find( tn1 ); + if( itt==d_loc_to_data_type.end() ){ + Trace("sep-type") << "Sep: assume location type " << tn1 << " is associated with data type " << tn2 << " (from " << atom << ")" << std::endl; + d_loc_to_data_type[tn1] = tn2; + }else{ + if( itt->second!=tn2 ){ + std::stringstream ss; + ss << "ERROR: location type " << tn1 << " is already associated with data type " << itt->second << ", offending atom is " << atom << " with data type " << tn2 << std::endl; + throw LogicException(ss.str()); + Assert( false ); + } + } + card = 1; + visited[n] = card; + return tn1; + //return n[1].getType(); + }else if( n.getKind()==kind::SEP_EMP ){ + card = 1; + visited[n] = card; + return n[0].getType(); + }else if( n.getKind()==kind::SEP_STAR || n.getKind()==kind::SEP_WAND ){ + Assert( n!=atom ); + //get the references + card = 0; + TypeNode tn = getReferenceType( n, card ); + for( unsigned j=0; j<d_references[n][-1].size(); j++ ){ + if( std::find( d_references[atom][index].begin(), d_references[atom][index].end(), d_references[n][-1][j] )==d_references[atom][index].end() ){ + d_references[atom][index].push_back( d_references[n][-1][j] ); + } + } + visited[n] = card; + return tn; + }else{ + card = 0; + TypeNode otn; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + int cardc = 0; + TypeNode tn = getReferenceType2( atom, cardc, index, n[i], visited ); + if( !tn.isNull() ){ + otn = tn; + } + card = cardc>card ? cardc : card; + } + visited[n] = card; + return otn; + } + }else{ + Trace("sep-type-debug") << "already visit : " << n << " : " << atom << " " << index << std::endl; + card = 0; + return TypeNode::null(); + } +} +/* + +int TheorySep::getCardinality( Node n, std::vector< Node >& refs ) { + std::map< Node, int > visited; + return getCardinality2( n, refs, visited ); +} + +int TheorySep::getCardinality2( Node n, std::vector< Node >& refs, std::map< Node, int >& visited ) { + std::map< Node, int >::iterator it = visited.find( n ); + if( it!=visited.end() ){ + return it->second; + }else{ + + + } +} +*/ + +Node TheorySep::getBaseLabel( TypeNode tn ) { + std::map< TypeNode, Node >::iterator it = d_base_label.find( tn ); + if( it==d_base_label.end() ){ + Trace("sep") << "Make base label for " << tn << std::endl; + std::stringstream ss; + ss << "__Lb"; + TypeNode ltn = NodeManager::currentNM()->mkSetType(tn); + //TypeNode ltn = NodeManager::currentNM()->mkSetType(NodeManager::currentNM()->mkRefType(tn)); + Node n_lbl = NodeManager::currentNM()->mkSkolem( ss.str(), ltn, "" ); + d_base_label[tn] = n_lbl; + //make reference bound + Trace("sep") << "Make reference bound label for " << tn << std::endl; + std::stringstream ss2; + ss2 << "__Lu"; + d_reference_bound[tn] = NodeManager::currentNM()->mkSkolem( ss2.str(), ltn, "" ); + d_type_references_all[tn].insert( d_type_references_all[tn].end(), d_type_references[tn].begin(), d_type_references[tn].end() ); + //add a reference type for maximum occurrences of empty in a constraint + unsigned n_emp = d_card_max[tn]>d_card_max[TypeNode::null()] ? d_card_max[tn] : d_card_max[TypeNode::null()]; + for( unsigned r=0; r<n_emp; r++ ){ + Node e = NodeManager::currentNM()->mkSkolem( "e", tn, "cardinality bound element for seplog" ); + //d_type_references_all[tn].push_back( NodeManager::currentNM()->mkSkolem( "e", NodeManager::currentNM()->mkRefType(tn) ) ); + if( options::sepDisequalC() ){ + //ensure that it is distinct from all other references so far + for( unsigned j=0; j<d_type_references_all[tn].size(); j++ ){ + Node eq = NodeManager::currentNM()->mkNode( e.getType().isBoolean() ? kind::IFF : kind::EQUAL, e, d_type_references_all[tn][j] ); + d_out->lemma( eq.negate() ); + } + } + d_type_references_all[tn].push_back( e ); + d_lbl_reference_bound[d_base_label[tn]].push_back( e ); + } + //construct bound + d_reference_bound_max[tn] = mkUnion( tn, d_type_references_all[tn] ); + Trace("sep-bound") << "overall bound for " << d_base_label[tn] << " : " << d_reference_bound_max[tn] << std::endl; + + if( d_reference_bound_invalid.find( tn )==d_reference_bound_invalid.end() ){ + Node slem = NodeManager::currentNM()->mkNode( kind::SUBSET, d_reference_bound[tn], d_reference_bound_max[tn] ); + Trace("sep-lemma") << "Sep::Lemma: reference bound for " << tn << " : " << slem << std::endl; + d_out->lemma( slem ); + }else{ + Trace("sep-bound") << "reference cannot be bound (possibly due to quantified pto)." << std::endl; + } + //slem = NodeManager::currentNM()->mkNode( kind::SUBSET, d_base_label[tn], d_reference_bound_max[tn] ); + //Trace("sep-lemma") << "Sep::Lemma: base reference bound for " << tn << " : " << slem << std::endl; + //d_out->lemma( slem ); + return n_lbl; + }else{ + return it->second; + } +} + +Node TheorySep::getNilRef( TypeNode tn ) { + std::map< TypeNode, Node >::iterator it = d_nil_ref.find( tn ); + if( it==d_nil_ref.end() ){ + Node nil = NodeManager::currentNM()->mkSepNil( tn ); + d_nil_ref[tn] = nil; + return nil; + }else{ + return it->second; + } +} + +Node TheorySep::mkUnion( TypeNode tn, std::vector< Node >& locs ) { + Node u; + if( locs.empty() ){ + TypeNode ltn = NodeManager::currentNM()->mkSetType(tn); + return NodeManager::currentNM()->mkConst(EmptySet(ltn.toType())); + }else{ + for( unsigned i=0; i<locs.size(); i++ ){ + Node s = locs[i]; + Assert( !s.isNull() ); + s = NodeManager::currentNM()->mkNode( kind::SINGLETON, s ); + if( u.isNull() ){ + u = s; + }else{ + u = NodeManager::currentNM()->mkNode( kind::UNION, s, u ); + } + } + return u; + } +} + +Node TheorySep::getLabel( Node atom, int child, Node lbl ) { + std::map< int, Node >::iterator it = d_label_map[atom][lbl].find( child ); + if( it==d_label_map[atom][lbl].end() ){ + int card; + TypeNode refType = getReferenceType( atom, card ); + std::stringstream ss; + ss << "__Lc" << child; + TypeNode ltn = NodeManager::currentNM()->mkSetType(refType); + //TypeNode ltn = NodeManager::currentNM()->mkSetType(NodeManager::currentNM()->mkRefType(refType)); + Node n_lbl = NodeManager::currentNM()->mkSkolem( ss.str(), ltn, "" ); + d_label_map[atom][lbl][child] = n_lbl; + d_label_map_parent[n_lbl] = lbl; + return n_lbl; + }else{ + return (*it).second; + } +} + +Node TheorySep::applyLabel( Node n, Node lbl, std::map< Node, Node >& visited ) { + Assert( n.getKind()!=kind::SEP_LABEL ); + if( n.getKind()==kind::SEP_STAR || n.getKind()==kind::SEP_WAND || n.getKind()==kind::SEP_PTO || n.getKind()==kind::SEP_EMP ){ + return NodeManager::currentNM()->mkNode( kind::SEP_LABEL, n, lbl ); + }else if( !n.getType().isBoolean() || n.getNumChildren()==0 ){ + return n; + }else{ + std::map< Node, Node >::iterator it = visited.find( n ); + if( it==visited.end() ){ + std::vector< Node > children; + if (n.getMetaKind() == kind::metakind::PARAMETERIZED) { + children.push_back( n.getOperator() ); + } + bool childChanged = false; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + Node aln = applyLabel( n[i], lbl, visited ); + children.push_back( aln ); + childChanged = childChanged || aln!=n[i]; + } + Node ret = n; + if( childChanged ){ + ret = NodeManager::currentNM()->mkNode( n.getKind(), children ); + } + visited[n] = ret; + return ret; + }else{ + return it->second; + } + } +} + +Node TheorySep::instantiateLabel( Node n, Node o_lbl, Node lbl, Node lbl_v, std::map< Node, Node >& visited, std::map< Node, Node >& pto_model, std::map< Node, Node >& tmodel, + TypeNode rtn, std::map< Node, bool >& active_lbl, unsigned ind ) { + Trace("sep-inst-debug") << "Instantiate label " << n << " " << lbl << " " << lbl_v << std::endl; + if( options::sepMinimalRefine() && lbl!=o_lbl && active_lbl.find( lbl )!=active_lbl.end() ){ + Trace("sep-inst") << "...do not instantiate " << o_lbl << " since it has an active sublabel " << lbl << std::endl; + return Node::null(); + }else{ + if( Trace.isOn("sep-inst") ){ + if( n.getKind()==kind::SEP_STAR || n.getKind()==kind::SEP_WAND || n.getKind()==kind::SEP_PTO || n.getKind()==kind::SEP_EMP ){ + for( unsigned j=0; j<ind; j++ ){ Trace("sep-inst") << " "; } + Trace("sep-inst") << n << "[" << lbl << "] :: " << lbl_v << std::endl; + } + } + Assert( n.getKind()!=kind::SEP_LABEL ); + if( n.getKind()==kind::SEP_STAR || n.getKind()==kind::SEP_WAND ){ + if( lbl==o_lbl ){ + std::vector< Node > children; + children.resize( n.getNumChildren() ); + Assert( d_label_map[n].find( lbl )!=d_label_map[n].end() ); + for( std::map< int, Node >::iterator itl = d_label_map[n][lbl].begin(); itl != d_label_map[n][lbl].end(); ++itl ){ + Node sub_lbl = itl->second; + int sub_index = itl->first; + Assert( sub_index>=0 && sub_index<(int)children.size() ); + Trace("sep-inst-debug") << "Sublabel " << sub_index << " is " << sub_lbl << std::endl; + Node lbl_mval; + if( n.getKind()==kind::SEP_WAND && sub_index==1 ){ + Assert( d_label_map[n][lbl].find( 0 )!=d_label_map[n][lbl].end() ); + Node sub_lbl_0 = d_label_map[n][lbl][0]; + computeLabelModel( sub_lbl_0, tmodel ); + Assert( d_label_model.find( sub_lbl_0 )!=d_label_model.end() ); + lbl_mval = NodeManager::currentNM()->mkNode( kind::UNION, lbl, d_label_model[sub_lbl_0].getValue( rtn ) ); + }else{ + computeLabelModel( sub_lbl, tmodel ); + Assert( d_label_model.find( sub_lbl )!=d_label_model.end() ); + lbl_mval = d_label_model[sub_lbl].getValue( rtn ); + } + Trace("sep-inst-debug") << "Sublabel value is " << lbl_mval << std::endl; + children[sub_index] = instantiateLabel( n[sub_index], o_lbl, sub_lbl, lbl_mval, visited, pto_model, tmodel, rtn, active_lbl, ind+1 ); + if( children[sub_index].isNull() ){ + return Node::null(); + } + } + if( n.getKind()==kind::SEP_STAR ){ + Assert( children.size()>1 ); + return NodeManager::currentNM()->mkNode( kind::AND, children ); + }else{ + return NodeManager::currentNM()->mkNode( kind::OR, children[0].negate(), children[1] ); + } + }else{ + //nested star/wand, label it and return + return NodeManager::currentNM()->mkNode( kind::SEP_LABEL, n, lbl_v ); + } + }else if( n.getKind()==kind::SEP_PTO ){ + //check if this pto reference is in the base label, if not, then it does not need to be added as an assumption + Assert( d_label_model.find( o_lbl )!=d_label_model.end() ); + Node vr = d_valuation.getModel()->getRepresentative( n[0] ); + Node svr = NodeManager::currentNM()->mkNode( kind::SINGLETON, vr ); + bool inBaseHeap = std::find( d_label_model[o_lbl].d_heap_locs_model.begin(), d_label_model[o_lbl].d_heap_locs_model.end(), svr )!=d_label_model[o_lbl].d_heap_locs_model.end(); + Trace("sep-inst-debug") << "Is in base (non-instantiating) heap : " << inBaseHeap << " for value ref " << vr << " in " << o_lbl << std::endl; + std::vector< Node > children; + if( inBaseHeap ){ + Node s = NodeManager::currentNM()->mkNode( kind::SINGLETON, n[0] ); + children.push_back( NodeManager::currentNM()->mkNode( kind::SEP_LABEL, NodeManager::currentNM()->mkNode( kind::SEP_PTO, n[0], n[1] ), s ) ); + }else{ + //look up value of data + std::map< Node, Node >::iterator it = pto_model.find( vr ); + if( it!=pto_model.end() ){ + if( n[1]!=it->second ){ + children.push_back( NodeManager::currentNM()->mkNode( n[1].getType().isBoolean() ? kind::IFF : kind::EQUAL, n[1], it->second ) ); + } + }else{ + Trace("sep-inst-debug") << "Data for " << vr << " was not specified, do not add condition." << std::endl; + } + } + children.push_back( NodeManager::currentNM()->mkNode( kind::EQUAL, NodeManager::currentNM()->mkNode( kind::SINGLETON, n[0] ), lbl_v ) ); + Node ret = children.empty() ? NodeManager::currentNM()->mkConst( true ) : ( children.size()==1 ? children[0] : NodeManager::currentNM()->mkNode( kind::AND, children ) ); + Trace("sep-inst-debug") << "Return " << ret << std::endl; + return ret; + }else if( n.getKind()==kind::SEP_EMP ){ + //return NodeManager::currentNM()->mkConst( lbl_v.getKind()==kind::EMPTYSET ); + return lbl_v.eqNode( NodeManager::currentNM()->mkConst(EmptySet(lbl_v.getType().toType())) ); + }else{ + std::map< Node, Node >::iterator it = visited.find( n ); + if( it==visited.end() ){ + std::vector< Node > children; + if (n.getMetaKind() == kind::metakind::PARAMETERIZED) { + children.push_back( n.getOperator() ); + } + bool childChanged = false; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + Node aln = instantiateLabel( n[i], o_lbl, lbl, lbl_v, visited, pto_model, tmodel, rtn, active_lbl, ind ); + if( aln.isNull() ){ + return Node::null(); + }else{ + children.push_back( aln ); + childChanged = childChanged || aln!=n[i]; + } + } + Node ret = n; + if( childChanged ){ + ret = NodeManager::currentNM()->mkNode( n.getKind(), children ); + } + //careful about caching + //visited[n] = ret; + return ret; + }else{ + return it->second; + } + } + } +} + +void TheorySep::setInactiveAssertionRec( Node fact, std::map< Node, std::vector< Node > >& lbl_to_assertions, std::map< Node, bool >& assert_active ) { + Trace("sep-process-debug") << "setInactiveAssertionRec::inactive : " << fact << std::endl; + assert_active[fact] = false; + bool polarity = fact.getKind() != kind::NOT; + TNode atom = polarity ? fact : fact[0]; + TNode s_atom = atom[0]; + TNode s_lbl = atom[1]; + if( s_atom.getKind()==kind::SEP_WAND || s_atom.getKind()==kind::SEP_STAR ){ + for( unsigned j=0; j<s_atom.getNumChildren(); j++ ){ + Node lblc = getLabel( s_atom, j, s_lbl ); + for( unsigned k=0; k<lbl_to_assertions[lblc].size(); k++ ){ + setInactiveAssertionRec( lbl_to_assertions[lblc][k], lbl_to_assertions, assert_active ); + } + } + } +} + +void TheorySep::getLabelChildren( Node s_atom, Node lbl, std::vector< Node >& children, std::vector< Node >& labels ) { + for( unsigned i=0; i<s_atom.getNumChildren(); i++ ){ + Node lblc = getLabel( s_atom, i, lbl ); + Assert( !lblc.isNull() ); + std::map< Node, Node > visited; + Node lc = applyLabel( s_atom[i], lblc, visited ); + Assert( !lc.isNull() ); + if( i==1 && s_atom.getKind()==kind::SEP_WAND ){ + lc = lc.negate(); + } + children.push_back( lc ); + labels.push_back( lblc ); + } + Assert( children.size()>1 ); +} + +void TheorySep::computeLabelModel( Node lbl, std::map< Node, Node >& tmodel ) { + if( !d_label_model[lbl].d_computed ){ + d_label_model[lbl].d_computed = true; + + //we must get the value of lbl from the model: this is being run at last call, after the model is constructed + //Assert(...); TODO + Node v_val = d_valuation.getModel()->getRepresentative( lbl ); + Trace("sep-process") << "Model value (from valuation) for " << lbl << " : " << v_val << std::endl; + if( v_val.getKind()!=kind::EMPTYSET ){ + while( v_val.getKind()==kind::UNION ){ + Assert( v_val[1].getKind()==kind::SINGLETON ); + d_label_model[lbl].d_heap_locs_model.push_back( v_val[1] ); + v_val = v_val[0]; + } + Assert( v_val.getKind()==kind::SINGLETON ); + d_label_model[lbl].d_heap_locs_model.push_back( v_val ); + } + //end hack + for( unsigned j=0; j<d_label_model[lbl].d_heap_locs_model.size(); j++ ){ + Node u = d_label_model[lbl].d_heap_locs_model[j]; + Assert( u.getKind()==kind::SINGLETON ); + u = u[0]; + Node tt; + std::map< Node, Node >::iterator itm = tmodel.find( u ); + if( itm==tmodel.end() ) { + //Trace("sep-process") << "WARNING: could not find symbolic term in model for " << u << std::endl; + //Assert( false ); + //tt = u; + //TypeNode tn = u.getType().getRefConstituentType(); + TypeNode tn = u.getType(); + Trace("sep-process") << "WARNING: could not find symbolic term in model for " << u << ", cref type " << tn << std::endl; + Assert( d_type_references_all.find( tn )!=d_type_references_all.end() && !d_type_references_all[tn].empty() ); + tt = d_type_references_all[tn][0]; + }else{ + tt = itm->second; + } + Node stt = NodeManager::currentNM()->mkNode( kind::SINGLETON, tt ); + Trace("sep-process-debug") << "...model : add " << tt << " for " << u << " in lbl " << lbl << std::endl; + d_label_model[lbl].d_heap_locs.push_back( stt ); + } + } +} + +Node TheorySep::getRepresentative( Node t ) { + if( d_equalityEngine.hasTerm( t ) ){ + return d_equalityEngine.getRepresentative( t ); + }else{ + return t; + } +} + +bool TheorySep::hasTerm( Node a ){ + return d_equalityEngine.hasTerm( a ); +} + +bool TheorySep::areEqual( Node a, Node b ){ + if( a==b ){ + return true; + }else if( hasTerm( a ) && hasTerm( b ) ){ + return d_equalityEngine.areEqual( a, b ); + }else{ + return false; + } +} + +bool TheorySep::areDisequal( Node a, Node b ){ + if( a==b ){ + return false; + }else if( hasTerm( a ) && hasTerm( b ) ){ + if( d_equalityEngine.areDisequal( a, b, false ) ){ + return true; + } + } + return false; +} + + +void TheorySep::eqNotifyPreMerge(TNode t1, TNode t2) { + +} + +void TheorySep::eqNotifyPostMerge(TNode t1, TNode t2) { + HeapAssertInfo * e2 = getOrMakeEqcInfo( t2, false ); + if( e2 && ( !e2->d_pto.get().isNull() || e2->d_has_neg_pto.get() ) ){ + HeapAssertInfo * e1 = getOrMakeEqcInfo( t1, true ); + if( !e2->d_pto.get().isNull() ){ + if( !e1->d_pto.get().isNull() ){ + Trace("sep-pto-debug") << "While merging " << t1 << " " << t2 << ", merge pto." << std::endl; + mergePto( e1->d_pto.get(), e2->d_pto.get() ); + }else{ + e1->d_pto.set( e2->d_pto.get() ); + } + } + e1->d_has_neg_pto.set( e1->d_has_neg_pto.get() || e2->d_has_neg_pto.get() ); + //validate + validatePto( e1, t1 ); + } +} + +void TheorySep::validatePto( HeapAssertInfo * ei, Node ei_n ) { + if( !ei->d_pto.get().isNull() && ei->d_has_neg_pto.get() ){ + for( NodeList::const_iterator i = d_spatial_assertions.begin(); i != d_spatial_assertions.end(); ++i ) { + Node fact = (*i); + bool polarity = fact.getKind() != kind::NOT; + if( !polarity ){ + TNode atom = polarity ? fact : fact[0]; + Assert( atom.getKind()==kind::SEP_LABEL ); + TNode s_atom = atom[0]; + if( s_atom.getKind()==kind::SEP_PTO ){ + if( areEqual( atom[1], ei_n ) ){ + addPto( ei, ei_n, atom, false ); + } + } + } + } + //we have now processed all pending negated pto + ei->d_has_neg_pto.set( false ); + } +} + +void TheorySep::addPto( HeapAssertInfo * ei, Node ei_n, Node p, bool polarity ) { + Trace("sep-pto") << "Add pto " << p << ", pol = " << polarity << " to eqc " << ei_n << std::endl; + if( !ei->d_pto.get().isNull() ){ + if( polarity ){ + Trace("sep-pto-debug") << "...eqc " << ei_n << " already has pto " << ei->d_pto.get() << ", merge." << std::endl; + mergePto( ei->d_pto.get(), p ); + }else{ + Node pb = ei->d_pto.get(); + Trace("sep-pto") << "Process positive/negated pto " << " " << pb << " " << p << std::endl; + Assert( pb.getKind()==kind::SEP_LABEL && pb[0].getKind()==kind::SEP_PTO ); + Assert( p.getKind()==kind::SEP_LABEL && p[0].getKind()==kind::SEP_PTO ); + Assert( areEqual( pb[1], p[1] ) ); + std::vector< Node > exp; + if( pb[1]!=p[1] ){ + exp.push_back( pb[1].eqNode( p[1] ) ); + } + exp.push_back( pb ); + exp.push_back( p.negate() ); + std::vector< Node > conc; + if( pb[0][1]!=p[0][1] ){ + conc.push_back( pb[0][1].eqNode( p[0][1] ).negate() ); + } + if( pb[1]!=p[1] ){ + conc.push_back( pb[1].eqNode( p[1] ).negate() ); + } + Node n_conc = conc.empty() ? d_false : ( conc.size()==1 ? conc[0] : NodeManager::currentNM()->mkNode( kind::OR, conc ) ); + sendLemma( exp, n_conc, "PTO_NEG_PROP" ); + } + }else{ + if( polarity ){ + ei->d_pto.set( p ); + validatePto( ei, ei_n ); + }else{ + ei->d_has_neg_pto.set( true ); + } + } +} + +void TheorySep::mergePto( Node p1, Node p2 ) { + Trace("sep-lemma-debug") << "Merge pto " << p1 << " " << p2 << std::endl; + Assert( p1.getKind()==kind::SEP_LABEL && p1[0].getKind()==kind::SEP_PTO ); + Assert( p2.getKind()==kind::SEP_LABEL && p2[0].getKind()==kind::SEP_PTO ); + if( !areEqual( p1[0][1], p2[0][1] ) ){ + std::vector< Node > exp; + if( p1[1]!=p2[1] ){ + Assert( areEqual( p1[1], p2[1] ) ); + exp.push_back( p1[1].eqNode( p2[1] ) ); + } + exp.push_back( p1 ); + exp.push_back( p2 ); + sendLemma( exp, p1[0][1].eqNode( p2[0][1] ), "PTO_PROP" ); + } +} + +void TheorySep::sendLemma( std::vector< Node >& ant, Node conc, const char * c, bool infer ) { + Trace("sep-lemma-debug") << "Do rewrite on inference : " << conc << std::endl; + conc = Rewriter::rewrite( conc ); + Trace("sep-lemma-debug") << "Got : " << conc << std::endl; + if( conc!=d_true ){ + if( infer && conc!=d_false ){ + Node ant_n; + if( ant.empty() ){ + ant_n = d_true; + }else if( ant.size()==1 ){ + ant_n = ant[0]; + }else{ + ant_n = NodeManager::currentNM()->mkNode( kind::AND, ant ); + } + Trace("sep-lemma") << "Sep::Infer: " << conc << " from " << ant_n << " by " << c << std::endl; + d_pending_exp.push_back( ant_n ); + d_pending.push_back( conc ); + d_infer.push_back( ant_n ); + d_infer_exp.push_back( conc ); + }else{ + std::vector< TNode > ant_e; + for( unsigned i=0; i<ant.size(); i++ ){ + Trace("sep-lemma-debug") << "Explain : " << ant[i] << std::endl; + explain( ant[i], ant_e ); + } + Node ant_n; + if( ant_e.empty() ){ + ant_n = d_true; + }else if( ant_e.size()==1 ){ + ant_n = ant_e[0]; + }else{ + ant_n = NodeManager::currentNM()->mkNode( kind::AND, ant_e ); + } + if( conc==d_false ){ + Trace("sep-lemma") << "Sep::Conflict: " << ant_n << " by " << c << std::endl; + d_out->conflict( ant_n ); + d_conflict = true; + }else{ + Trace("sep-lemma") << "Sep::Lemma: " << conc << " from " << ant_n << " by " << c << std::endl; + d_pending_exp.push_back( ant_n ); + d_pending.push_back( conc ); + d_pending_lem.push_back( d_pending.size()-1 ); + } + } + } +} + +void TheorySep::doPendingFacts() { + if( d_pending_lem.empty() ){ + for( unsigned i=0; i<d_pending.size(); i++ ){ + if( d_conflict ){ + break; + } + Node atom = d_pending[i].getKind()==kind::NOT ? d_pending[i][0] : d_pending[i]; + bool pol = d_pending[i].getKind()!=kind::NOT; + Trace("sep-pending") << "Sep : Assert to EE : " << atom << ", pol = " << pol << std::endl; + if( atom.getKind()==kind::EQUAL ){ + d_equalityEngine.assertEquality(atom, pol, d_pending_exp[i]); + }else{ + d_equalityEngine.assertPredicate(atom, pol, d_pending_exp[i]); + } + } + }else{ + for( unsigned i=0; i<d_pending_lem.size(); i++ ){ + if( d_conflict ){ + break; + } + int index = d_pending_lem[i]; + Node lem = NodeManager::currentNM()->mkNode( kind::IMPLIES, d_pending_exp[index], d_pending[index] ); + d_out->lemma( lem ); + Trace("sep-pending") << "Sep : Lemma : " << lem << std::endl; + } + } + d_pending_exp.clear(); + d_pending.clear(); + d_pending_lem.clear(); +} + +void TheorySep::debugPrintHeap( HeapInfo& heap, const char * c ) { + Trace(c) << "[" << std::endl; + Trace(c) << " "; + for( unsigned j=0; j<heap.d_heap_locs.size(); j++ ){ + Trace(c) << heap.d_heap_locs[j] << " "; + } + Trace(c) << std::endl; + Trace(c) << "]" << std::endl; +} + +Node TheorySep::HeapInfo::getValue( TypeNode tn ) { + Assert( d_heap_locs.size()==d_heap_locs_model.size() ); + if( d_heap_locs.empty() ){ + return NodeManager::currentNM()->mkConst(EmptySet(tn.toType())); + }else if( d_heap_locs.size()==1 ){ + return d_heap_locs[0]; + }else{ + Node curr = NodeManager::currentNM()->mkNode( kind::UNION, d_heap_locs[0], d_heap_locs[1] ); + for( unsigned j=2; j<d_heap_locs.size(); j++ ){ + curr = NodeManager::currentNM()->mkNode( kind::UNION, curr, d_heap_locs[j] ); + } + return curr; + } +} + +}/* CVC4::theory::sep namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/sep/theory_sep.h b/src/theory/sep/theory_sep.h new file mode 100644 index 000000000..852a36721 --- /dev/null +++ b/src/theory/sep/theory_sep.h @@ -0,0 +1,294 @@ +/********************* */ +/*! \file theory_sep.h + ** \verbatim + ** Original author: Andrew Reynolds + ** Major contributors: Dejan Jovanovic, Clark Barrett + ** Minor contributors (to current version): Tim King, Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2014 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory of sep + ** + ** Theory of sep. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__SEP__THEORY_SEP_H +#define __CVC4__THEORY__SEP__THEORY_SEP_H + +#include "theory/theory.h" +#include "util/statistics_registry.h" +#include "theory/uf/equality_engine.h" +#include "context/cdchunk_list.h" +#include "context/cdhashmap.h" +#include "context/cdhashset.h" +#include "context/cdqueue.h" + +namespace CVC4 { +namespace theory { + +class TheoryModel; + +namespace sep { + +class TheorySep : public Theory { + typedef context::CDChunkList<Node> NodeList; + typedef context::CDHashSet<Node, NodeHashFunction> NodeSet; + typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; + + ///////////////////////////////////////////////////////////////////////////// + // MISC + ///////////////////////////////////////////////////////////////////////////// + + private: + + /** True node for predicates = true */ + Node d_true; + + /** True node for predicates = false */ + Node d_false; + + Node mkAnd( std::vector< TNode >& assumptions ); + + void processAssertion( Node n, std::map< Node, bool >& visited ); + + public: + + TheorySep(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); + ~TheorySep(); + + void setMasterEqualityEngine(eq::EqualityEngine* eq); + + std::string identify() const { return std::string("TheorySep"); } + + ///////////////////////////////////////////////////////////////////////////// + // PREPROCESSING + ///////////////////////////////////////////////////////////////////////////// + + public: + + PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions); + Node ppRewrite(TNode atom); + + void processAssertions( std::vector< Node >& assertions ); + ///////////////////////////////////////////////////////////////////////////// + // T-PROPAGATION / REGISTRATION + ///////////////////////////////////////////////////////////////////////////// + + private: + + /** Should be called to propagate the literal. */ + bool propagate(TNode literal); + + /** Explain why this literal is true by adding assumptions */ + void explain(TNode literal, std::vector<TNode>& assumptions); + + void preRegisterTermRec(TNode t, std::map< TNode, bool >& visited ); + public: + + void preRegisterTerm(TNode t); + void propagate(Effort e); + Node explain(TNode n); + + public: + + void addSharedTerm(TNode t); + EqualityStatus getEqualityStatus(TNode a, TNode b); + void computeCareGraph(); + + ///////////////////////////////////////////////////////////////////////////// + // MODEL GENERATION + ///////////////////////////////////////////////////////////////////////////// + + public: + + void collectModelInfo(TheoryModel* m, bool fullModel); + + ///////////////////////////////////////////////////////////////////////////// + // NOTIFICATIONS + ///////////////////////////////////////////////////////////////////////////// + + private: + public: + + Node getNextDecisionRequest(); + + void presolve(); + void shutdown() { } + + ///////////////////////////////////////////////////////////////////////////// + // MAIN SOLVER + ///////////////////////////////////////////////////////////////////////////// + public: + + void check(Effort e); + + private: + + // NotifyClass: template helper class for d_equalityEngine - handles call-back from congruence closure module + class NotifyClass : public eq::EqualityEngineNotify { + TheorySep& d_sep; + public: + NotifyClass(TheorySep& sep): d_sep(sep) {} + + bool eqNotifyTriggerEquality(TNode equality, bool value) { + Debug("sep::propagate") << "NotifyClass::eqNotifyTriggerEquality(" << equality << ", " << (value ? "true" : "false") << ")" << std::endl; + // Just forward to sep + if (value) { + return d_sep.propagate(equality); + } else { + return d_sep.propagate(equality.notNode()); + } + } + + bool eqNotifyTriggerPredicate(TNode predicate, bool value) { + Unreachable(); + } + + bool eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) { + Debug("sep::propagate") << "NotifyClass::eqNotifyTriggerTermEquality(" << t1 << ", " << t2 << ", " << (value ? "true" : "false") << ")" << std::endl; + if (value) { + // Propagate equality between shared terms + return d_sep.propagate(t1.eqNode(t2)); + } else { + return d_sep.propagate(t1.eqNode(t2).notNode()); + } + return true; + } + + void eqNotifyConstantTermMerge(TNode t1, TNode t2) { + Debug("sep::propagate") << "NotifyClass::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << ")" << std::endl; + d_sep.conflict(t1, t2); + } + + void eqNotifyNewClass(TNode t) { } + void eqNotifyPreMerge(TNode t1, TNode t2) { d_sep.eqNotifyPreMerge( t1, t2 ); } + void eqNotifyPostMerge(TNode t1, TNode t2) { d_sep.eqNotifyPostMerge( t1, t2 ); } + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { } + }; + + /** The notify class for d_equalityEngine */ + NotifyClass d_notify; + + /** Equaltity engine */ + eq::EqualityEngine d_equalityEngine; + + /** Are we in conflict? */ + context::CDO<bool> d_conflict; + std::vector< Node > d_pending_exp; + std::vector< Node > d_pending; + std::vector< int > d_pending_lem; + + /** list of all refinement lemms */ + std::map< Node, std::map< Node, std::vector< Node > > > d_refinement_lem; + + /** Conflict when merging constants */ + void conflict(TNode a, TNode b); + + //cache for positive polarity start reduction + NodeSet d_reduce; + std::map< Node, std::map< Node, Node > > d_red_conc; + std::map< Node, std::map< Node, Node > > d_neg_guard; + std::vector< Node > d_neg_guards; + std::map< Node, Node > d_guard_to_assertion; + //cache for references + std::map< Node, std::map< int, TypeNode > > d_reference_type; + std::map< Node, std::map< int, int > > d_reference_type_card; + std::map< Node, std::map< int, std::vector< Node > > > d_references; + /** inferences: maintained to ensure ref count for internally introduced nodes */ + NodeList d_infer; + NodeList d_infer_exp; + NodeList d_spatial_assertions; + + //currently fix one data type for each location type, throw error if using more than one + std::map< TypeNode, TypeNode > d_loc_to_data_type; + //information about types + std::map< TypeNode, Node > d_base_label; + std::map< TypeNode, Node > d_nil_ref; + //reference bound + std::map< TypeNode, Node > d_reference_bound; + std::map< TypeNode, Node > d_reference_bound_max; + std::map< TypeNode, bool > d_reference_bound_invalid; + std::map< TypeNode, std::vector< Node > > d_type_references; + std::map< TypeNode, std::vector< Node > > d_type_references_all; + std::map< TypeNode, unsigned > d_card_max; + //bounds for labels + std::map< Node, std::vector< Node > > d_lbl_reference_bound; + //for empty argument + std::map< TypeNode, Node > d_emp_arg; + //map from ( atom, label, child index ) -> label + std::map< Node, std::map< Node, std::map< int, Node > > > d_label_map; + std::map< Node, Node > d_label_map_parent; + + //term model + std::map< Node, Node > d_tmodel; + std::map< Node, Node > d_pto_model; + + class HeapAssertInfo { + public: + HeapAssertInfo( context::Context* c ); + ~HeapAssertInfo(){} + context::CDO< Node > d_pto; + context::CDO< bool > d_has_neg_pto; + }; + std::map< Node, HeapAssertInfo * > d_eqc_info; + HeapAssertInfo * getOrMakeEqcInfo( Node n, bool doMake = false ); + + //calculate the element type of the heap for spatial assertions + TypeNode getReferenceType( Node atom, int& card, int index = -1 ); + TypeNode getReferenceType2( Node atom, int& card, int index, Node n, std::map< Node, int >& visited); + //get the base label for the spatial assertion + Node getBaseLabel( TypeNode tn ); + Node getNilRef( TypeNode tn ); + Node getLabel( Node atom, int child, Node lbl ); + Node applyLabel( Node n, Node lbl, std::map< Node, Node >& visited ); + void getLabelChildren( Node atom, Node lbl, std::vector< Node >& children, std::vector< Node >& labels ); + + class HeapInfo { + public: + HeapInfo() : d_computed(false) {} + //information about the model + bool d_computed; + std::vector< Node > d_heap_locs; + std::vector< Node > d_heap_locs_model; + //get value + Node getValue( TypeNode tn ); + }; + //heap info ( label -> HeapInfo ) + std::map< Node, HeapInfo > d_label_model; + + void debugPrintHeap( HeapInfo& heap, const char * c ); + void validatePto( HeapAssertInfo * ei, Node ei_n ); + void addPto( HeapAssertInfo * ei, Node ei_n, Node p, bool polarity ); + void mergePto( Node p1, Node p2 ); + void computeLabelModel( Node lbl, std::map< Node, Node >& tmodel ); + Node instantiateLabel( Node n, Node o_lbl, Node lbl, Node lbl_v, std::map< Node, Node >& visited, std::map< Node, Node >& pto_model, std::map< Node, Node >& tmodel, + TypeNode rtn, std::map< Node, bool >& active_lbl, unsigned ind = 0 ); + void setInactiveAssertionRec( Node fact, std::map< Node, std::vector< Node > >& lbl_to_assertions, std::map< Node, bool >& assert_active ); + + Node mkUnion( TypeNode tn, std::vector< Node >& locs ); +private: + Node getRepresentative( Node t ); + bool hasTerm( Node a ); + bool areEqual( Node a, Node b ); + bool areDisequal( Node a, Node b ); + void eqNotifyPreMerge(TNode t1, TNode t2); + void eqNotifyPostMerge(TNode t1, TNode t2); + + void sendLemma( std::vector< Node >& ant, Node conc, const char * c, bool infer = false ); + void doPendingFacts(); +public: + eq::EqualityEngine* getEqualityEngine() { + return &d_equalityEngine; + } + +};/* class TheorySep */ + +}/* CVC4::theory::sep namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__SEP__THEORY_SEP_H */ diff --git a/src/theory/sep/theory_sep_rewriter.cpp b/src/theory/sep/theory_sep_rewriter.cpp new file mode 100644 index 000000000..d58c2c13d --- /dev/null +++ b/src/theory/sep/theory_sep_rewriter.cpp @@ -0,0 +1,184 @@ +/********************* */ +/*! \file theory_sep_rewriter.cpp + ** \verbatim + ** Original author: Andrew Reynolds + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2014 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "expr/attribute.h" +#include "theory/sep/theory_sep_rewriter.h" + +namespace CVC4 { +namespace theory { +namespace sep { + +void TheorySepRewriter::getStarChildren( Node n, std::vector< Node >& s_children, std::vector< Node >& ns_children ){ + Assert( n.getKind()==kind::SEP_STAR ); + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + if( n[i].getKind()==kind::SEP_EMP ){ + s_children.push_back( n[i] ); + }else if( n[i].getKind()==kind::SEP_STAR ){ + getStarChildren( n[i], s_children, ns_children ); + }else if( n[i].getKind()==kind::SEP_PTO ){ + s_children.push_back( n[i] ); + }else{ + std::vector< Node > temp_s_children; + getAndChildren( n[i], temp_s_children, ns_children ); + Node to_add; + if( temp_s_children.size()==0 ){ + to_add = NodeManager::currentNM()->mkConst( true ); + }else{ + //remove empty star + std::vector< Node > temp_s_children2; + for( unsigned i=0; i<temp_s_children.size(); i++ ){ + if( temp_s_children[i].getKind()!=kind::SEP_EMP ){ + temp_s_children2.push_back( temp_s_children[i] ); + } + } + if( temp_s_children2.size()==1 ){ + to_add = temp_s_children2[0]; + }else if( temp_s_children2.size()>1 ){ + to_add = NodeManager::currentNM()->mkNode( kind::AND, temp_s_children2 ); + } + } + if( !to_add.isNull() ){ + //flatten star + if( to_add.getKind()==kind::SEP_STAR ){ + getStarChildren( to_add, s_children, ns_children ); + }else if( std::find( s_children.begin(), s_children.end(), to_add )==s_children.end() ){ + s_children.push_back( to_add ); + } + } + } + } +} + +void TheorySepRewriter::getAndChildren( Node n, std::vector< Node >& s_children, std::vector< Node >& ns_children ) { + if( n.getKind()==kind::AND ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + getAndChildren( n[i], s_children, ns_children ); + } + }else{ + std::map< Node, bool > visited; + if( isSpatial( n, visited ) ){ + if( std::find( s_children.begin(), s_children.end(), n )==s_children.end() ){ + s_children.push_back( n ); + } + }else{ + if( std::find( ns_children.begin(), ns_children.end(), n )==ns_children.end() ){ + if( n!=NodeManager::currentNM()->mkConst(true) ){ + ns_children.push_back( n ); + } + } + } + } +} + +bool TheorySepRewriter::isSpatial( Node n, std::map< Node, bool >& visited ) { + if( visited.find( n )==visited.end() ){ + visited[n] = true; + if( n.getKind()==kind::SEP_STAR || n.getKind()==kind::SEP_PTO || n.getKind()==kind::SEP_EMP || n.getKind()==kind::SEP_LABEL ){ + return true; + }else if( n.getType().isBoolean() ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + if( isSpatial( n[i], visited ) ){ + return true; + } + } + } + } + return false; +} + +RewriteResponse TheorySepRewriter::postRewrite(TNode node) { + Trace("sep-postrewrite") << "Sep::postRewrite start " << node << std::endl; + Node retNode = node; + switch (node.getKind()) { + case kind::SEP_LABEL: { + if( node[0].getKind()==kind::SEP_PTO ){ + Node s = NodeManager::currentNM()->mkNode( kind::SINGLETON, node[0][0] ); + if( node[1]!=s ){ + Node c1 = node[1].eqNode( s ); + Node c2 = NodeManager::currentNM()->mkNode( kind::SEP_LABEL, NodeManager::currentNM()->mkNode( kind::SEP_PTO, node[0][0], node[0][1] ), s ); + retNode = NodeManager::currentNM()->mkNode( kind::AND, c1, c2 ); + } + } + if( node[0].getKind()==kind::SEP_EMP ){ + retNode = node[1].eqNode( NodeManager::currentNM()->mkConst(EmptySet(node[1].getType().toType())) ); + } + break; + } + case kind::SEP_PTO: { + break; + } + case kind::SEP_STAR: { + //flatten + std::vector< Node > s_children; + std::vector< Node > ns_children; + getStarChildren( node, s_children, ns_children ); + if( !s_children.empty() ){ + Node schild; + if( s_children.size()==1 ) { + schild = s_children[0]; + }else{ + schild = NodeManager::currentNM()->mkNode( kind::SEP_STAR, s_children ); + } + ns_children.push_back( schild ); + } + Assert( !ns_children.empty() ); + if( ns_children.size()==1 ){ + retNode = ns_children[0]; + }else{ + retNode = NodeManager::currentNM()->mkNode( kind::AND, ns_children ); + } + break; + } + case kind::EQUAL: + case kind::IFF: { + if(node[0] == node[1]) { + return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(true)); + } + else if (node[0].isConst() && node[1].isConst()) { + return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(false)); + } + if (node[0] > node[1]) { + Node newNode = NodeManager::currentNM()->mkNode(node.getKind(), node[1], node[0]); + return RewriteResponse(REWRITE_DONE, newNode); + } + break; + } + default: + break; + } + if( node!=retNode ){ + Trace("sep-rewrite") << "Sep::rewrite : " << node << " -> " << retNode << std::endl; + } + return RewriteResponse(node==retNode ? REWRITE_DONE : REWRITE_AGAIN_FULL, retNode); +} + + +/* +RewriteResponse TheorySepRewriter::preRewrite(TNode node) { + if( node.getKind()==kind::SEP_EMP ){ + Trace("sep-prerewrite") << "Sep::preRewrite emp star " << std::endl; + return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkNode( kind::SEP_EMP, NodeManager::currentNM()->mkConst( true ) ) ); + }else{ + Trace("sep-prerewrite") << "Sep::preRewrite returning " << node << std::endl; + return RewriteResponse(REWRITE_DONE, node); + } +} +*/ + +}/* CVC4::theory::sep namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/sep/theory_sep_rewriter.h b/src/theory/sep/theory_sep_rewriter.h new file mode 100644 index 000000000..02adbebe5 --- /dev/null +++ b/src/theory/sep/theory_sep_rewriter.h @@ -0,0 +1,53 @@ +/********************* */ +/*! \file theory_sep_rewriter.h + ** \verbatim + ** Original author: Andrew Reynolds + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2014 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__SEP__THEORY_SEP_REWRITER_H +#define __CVC4__THEORY__SEP__THEORY_SEP_REWRITER_H + +#include "theory/rewriter.h" +#include "theory/type_enumerator.h" + +namespace CVC4 { +namespace theory { +namespace sep { + + +class TheorySepRewriter { +private: + static void getStarChildren( Node n, std::vector< Node >& s_children, std::vector< Node >& ns_children ); + static void getAndChildren( Node n, std::vector< Node >& s_children, std::vector< Node >& ns_children ); + static bool isSpatial( Node n, std::map< Node, bool >& visited ); +public: + + static RewriteResponse postRewrite(TNode node); + static inline RewriteResponse preRewrite(TNode node) { + Trace("sep-prerewrite") << "Sep::preRewrite returning " << node << std::endl; + return RewriteResponse(REWRITE_DONE, node); + } + + static inline void init() {} + static inline void shutdown() {} + +};/* class TheorySepRewriter */ + +}/* CVC4::theory::sep namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__SEP__THEORY_SEP_REWRITER_H */ diff --git a/src/theory/sep/theory_sep_type_rules.h b/src/theory/sep/theory_sep_type_rules.h new file mode 100644 index 000000000..7d4eb303e --- /dev/null +++ b/src/theory/sep/theory_sep_type_rules.h @@ -0,0 +1,114 @@ +/********************* */ +/*! \file theory_sep_type_rules.h + ** \verbatim + ** Original author: Andrew Reynolds + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2014 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Typing and cardinality rules for the theory of sep + ** + ** Typing and cardinality rules for the theory of sep. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__SEP__THEORY_SEP_TYPE_RULES_H +#define __CVC4__THEORY__SEP__THEORY_SEP_TYPE_RULES_H + +#include "theory/type_enumerator.h" + +namespace CVC4 { +namespace theory { +namespace sep { + +class SepNilRefTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + return TypeNode::fromType( n.getConst<NilRef>().getType() ); + } +}; + +class SepEmpTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + Assert(n.getKind() == kind::SEP_EMP); + return nodeManager->booleanType(); + } +}; + +struct SepPtoTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + Assert(n.getKind() == kind::SEP_PTO); + if( check ) { + TypeNode refType = n[0].getType(check); + TypeNode ptType = n[1].getType(check); + } + return nodeManager->booleanType(); + } +};/* struct SepSelectTypeRule */ + +struct SepStarTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TypeNode btype = nodeManager->booleanType(); + Assert(n.getKind() == kind::SEP_STAR); + if( check ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + TypeNode ctype = n[i].getType( check ); + if( ctype!=btype ){ + throw TypeCheckingExceptionPrivate(n, "child of sep star is not Boolean"); + } + } + } + return btype; + } +};/* struct SepStarTypeRule */ + +struct SepWandTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TypeNode btype = nodeManager->booleanType(); + Assert(n.getKind() == kind::SEP_WAND); + if( check ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + TypeNode ctype = n[i].getType( check ); + if( ctype!=btype ){ + throw TypeCheckingExceptionPrivate(n, "child of sep magic wand is not Boolean"); + } + } + } + return btype; + } +};/* struct SepWandTypeRule */ + +struct SepLabelTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TypeNode btype = nodeManager->booleanType(); + Assert(n.getKind() == kind::SEP_LABEL); + if( check ){ + TypeNode ctype = n[0].getType( check ); + if( ctype!=btype ){ + throw TypeCheckingExceptionPrivate(n, "child of sep label is not Boolean"); + } + TypeNode stype = n[1].getType( check ); + if( !stype.isSet() ){ + throw TypeCheckingExceptionPrivate(n, "label of sep label is not a set"); + } + } + return btype; + } +};/* struct SepLabelTypeRule */ + +}/* CVC4::theory::sep namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__SEP__THEORY_SEP_TYPE_RULES_H */ diff --git a/src/theory/sets/kinds b/src/theory/sets/kinds index 3fb73749d..c92eab4bd 100644 --- a/src/theory/sets/kinds +++ b/src/theory/sets/kinds @@ -64,6 +64,7 @@ typerule PRODUCT ::CVC4::theory::sets::RelBinaryOperatorTypeRule typerule TRANSPOSE ::CVC4::theory::sets::RelTransposeTypeRule typerule TCLOSURE ::CVC4::theory::sets::RelTransClosureTypeRule + construle UNION ::CVC4::theory::sets::SetsBinaryOperatorTypeRule construle INTERSECTION ::CVC4::theory::sets::SetsBinaryOperatorTypeRule construle SETMINUS ::CVC4::theory::sets::SetsBinaryOperatorTypeRule diff --git a/src/theory/sets/theory_sets_private.h b/src/theory/sets/theory_sets_private.h index 217432670..37071eb2e 100644 --- a/src/theory/sets/theory_sets_private.h +++ b/src/theory/sets/theory_sets_private.h @@ -139,6 +139,20 @@ private: */ bool lemma(Node n, SetsLemmaTag t); + /** send out a lemma */ + enum SetsLemmaTag { + SETS_LEMMA_DISEQUAL, + SETS_LEMMA_MEMBER, + SETS_LEMMA_GRAPH, + SETS_LEMMA_OTHER + }; + + /** + * returns true if a lemmas was generated + * returns false otherwise (found in cache) + */ + bool lemma(Node n, SetsLemmaTag t); + class TermInfoManager { TheorySetsPrivate& d_theory; context::Context* d_context; diff --git a/src/theory/sets/theory_sets_type_rules.h b/src/theory/sets/theory_sets_type_rules.h index 92d6c9b6d..478dcbdb6 100644 --- a/src/theory/sets/theory_sets_type_rules.h +++ b/src/theory/sets/theory_sets_type_rules.h @@ -275,6 +275,7 @@ struct RelTransClosureTypeRule { } };/* struct RelTransClosureTypeRule */ + struct SetsProperties { inline static Cardinality computeCardinality(TypeNode type) { Assert(type.getKind() == kind::SET_TYPE); diff --git a/src/theory/strings/regexp_operation.cpp b/src/theory/strings/regexp_operation.cpp index 53344dd6c..a665a02c1 100644 --- a/src/theory/strings/regexp_operation.cpp +++ b/src/theory/strings/regexp_operation.cpp @@ -39,6 +39,10 @@ RegExpOpr::RegExpOpr() d_sigma_star = NodeManager::currentNM()->mkNode( kind::REGEXP_STAR, d_sigma ); } +RegExpOpr::~RegExpOpr(){ + +} + int RegExpOpr::gcd ( int a, int b ) { int c; while ( a != 0 ) { diff --git a/src/theory/strings/regexp_operation.h b/src/theory/strings/regexp_operation.h index c537553f2..075391370 100644 --- a/src/theory/strings/regexp_operation.h +++ b/src/theory/strings/regexp_operation.h @@ -99,6 +99,7 @@ private: public: RegExpOpr(); + ~RegExpOpr(); bool checkConstRegExp( Node r ); void simplify(Node t, std::vector< Node > &new_nodes, bool polarity); diff --git a/src/theory/strings/theory_strings.cpp b/src/theory/strings/theory_strings.cpp index b3e1925ae..57344236e 100644 --- a/src/theory/strings/theory_strings.cpp +++ b/src/theory/strings/theory_strings.cpp @@ -28,6 +28,7 @@ #include "theory/strings/type_enumerator.h" #include "theory/theory_model.h" #include "theory/valuation.h" +#include "theory/quantifiers/term_database.h" using namespace std; using namespace CVC4::context; @@ -74,9 +75,11 @@ TheoryStrings::TheoryStrings(context::Context* c, context::UserContext* u, d_preproc(u), d_preproc_cache(u), d_extf_infer_cache(c), + d_ee_disequalities(c), d_congruent(c), d_proxy_var(u), d_proxy_var_to_length(u), + d_functionsTerms(c), d_neg_ctn_eqlen(c), d_neg_ctn_ulen(c), d_neg_ctn_cached(u), @@ -124,7 +127,9 @@ TheoryStrings::TheoryStrings(context::Context* c, context::UserContext* u, } TheoryStrings::~TheoryStrings() { - + for( std::map< Node, EqcInfo* >::iterator it = d_eqc_info.begin(); it != d_eqc_info.end(); ++it ){ + delete it->second; + } } Node TheoryStrings::getRepresentative( Node t ) { @@ -467,18 +472,30 @@ void TheoryStrings::preRegisterTerm(TNode n) { break; } default: { - if( n.getType().isString() ) { + TypeNode tn = n.getType(); + if( tn.isString() ) { registerTerm( n, 0 ); // FMF if( n.getKind() == kind::VARIABLE && options::stringFMF() ){ d_input_vars.insert(n); } - } else if (n.getType().isBoolean()) { + d_equalityEngine.addTerm(n); + } else if (tn.isBoolean()) { // Get triggered for both equal and dis-equal d_equalityEngine.addTriggerPredicate(n); } else { // Function applications/predicates d_equalityEngine.addTerm(n); + if( options::stringExp() ){ + //collect extended functions here: some may not be asserted to strings (such as those with return type Int), + // but we need to record them so they are treated properly + std::map< Node, bool > visited; + collectExtendedFuncTerms( n, visited ); + } + } + //concat terms do not contribute to theory combination? TODO: verify + if( n.hasOperator() && kindToTheoryId( n.getKind() )==THEORY_STRINGS && n.getKind()!=kind::STRING_CONCAT ){ + d_functionsTerms.push_back( n ); } } } @@ -486,6 +503,7 @@ void TheoryStrings::preRegisterTerm(TNode n) { } Node TheoryStrings::expandDefinition(LogicRequest &logicRequest, Node node) { + Trace("strings-exp-def") << "TheoryStrings::expandDefinition : " << node << std::endl; return node; } @@ -740,53 +758,23 @@ void TheoryStrings::eqNotifyNewClass(TNode t){ /** called when two equivalance classes will merge */ void TheoryStrings::eqNotifyPreMerge(TNode t1, TNode t2){ - EqcInfo * e2 = getOrMakeEqcInfo(t2, false); - if( e2 ){ - EqcInfo * e1 = getOrMakeEqcInfo( t1 ); - //add information from e2 to e1 - if( !e2->d_const_term.get().isNull() ){ - e1->d_const_term.set( e2->d_const_term ); - } - if( !e2->d_length_term.get().isNull() ){ - e1->d_length_term.set( e2->d_length_term ); - } - if( e2->d_cardinality_lem_k.get()>e1->d_cardinality_lem_k.get() ) { - e1->d_cardinality_lem_k.set( e2->d_cardinality_lem_k ); - } - if( !e2->d_normalized_length.get().isNull() ){ - e1->d_normalized_length.set( e2->d_normalized_length ); - } + EqcInfo * e2 = getOrMakeEqcInfo(t2, false); + if( e2 ){ + EqcInfo * e1 = getOrMakeEqcInfo( t1 ); + //add information from e2 to e1 + if( !e2->d_const_term.get().isNull() ){ + e1->d_const_term.set( e2->d_const_term ); } - /* - if( hasTerm( d_zero ) ){ - Node leqc; - if( areEqual(d_zero, t1) ){ - leqc = t2; - }else if( areEqual(d_zero, t2) ){ - leqc = t1; - } - if( !leqc.isNull() ){ - //scan equivalence class to see if we apply - eq::EqClassIterator eqc_i = eq::EqClassIterator( leqc, &d_equalityEngine ); - while( !eqc_i.isFinished() ){ - Node n = (*eqc_i); - if( n.getKind()==kind::STRING_LENGTH ){ - if( !hasTerm( d_emptyString ) || !areEqual(n[0], d_emptyString ) ){ - //apply the rule length(n[0])==0 => n[0] == "" - Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, n[0], d_emptyString ); - d_pending.push_back( eq ); - Node eq_exp = NodeManager::currentNM()->mkNode( kind::EQUAL, n, d_zero ); - d_pending_exp[eq] = eq_exp; - Trace("strings-infer") << "Strings : Infer Empty : " << eq << " from " << eq_exp << std::endl; - d_infer.push_back(eq); - d_infer_exp.push_back(eq_exp); - } - } - ++eqc_i; - } - } + if( !e2->d_length_term.get().isNull() ){ + e1->d_length_term.set( e2->d_length_term ); + } + if( e2->d_cardinality_lem_k.get()>e1->d_cardinality_lem_k.get() ) { + e1->d_cardinality_lem_k.set( e2->d_cardinality_lem_k ); } - */ + if( !e2->d_normalized_length.get().isNull() ){ + e1->d_normalized_length.set( e2->d_normalized_length ); + } + } } /** called when two equivalance classes have merged */ @@ -796,11 +784,106 @@ void TheoryStrings::eqNotifyPostMerge(TNode t1, TNode t2) { /** called when two equivalance classes are disequal */ void TheoryStrings::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { + if( t1.getType().isString() ){ + //store disequalities between strings, may need to check if their lengths are equal/disequal + d_ee_disequalities.push_back( t1.eqNode( t2 ) ); + } +} +void TheoryStrings::addCarePairs( quantifiers::TermArgTrie * t1, quantifiers::TermArgTrie * t2, unsigned arity, unsigned depth ) { + if( depth==arity ){ + if( t2!=NULL ){ + Node f1 = t1->getNodeData(); + Node f2 = t2->getNodeData(); + if( !d_equalityEngine.areEqual( f1, f2 ) ){ + Trace("strings-cg-debug") << "TheoryStrings::computeCareGraph(): checking function " << f1 << " and " << f2 << std::endl; + vector< pair<TNode, TNode> > currentPairs; + for (unsigned k = 0; k < f1.getNumChildren(); ++ k) { + TNode x = f1[k]; + TNode y = f2[k]; + Assert( d_equalityEngine.hasTerm(x) ); + Assert( d_equalityEngine.hasTerm(y) ); + Assert( !d_equalityEngine.areDisequal( x, y, false ) ); + if( !d_equalityEngine.areEqual( x, y ) ){ + if( d_equalityEngine.isTriggerTerm(x, THEORY_STRINGS) && d_equalityEngine.isTriggerTerm(y, THEORY_STRINGS) ){ + TNode x_shared = d_equalityEngine.getTriggerTermRepresentative(x, THEORY_STRINGS); + TNode y_shared = d_equalityEngine.getTriggerTermRepresentative(y, THEORY_STRINGS); + EqualityStatus eqStatus = d_valuation.getEqualityStatus(x_shared, y_shared); + if( eqStatus==EQUALITY_FALSE_AND_PROPAGATED || eqStatus==EQUALITY_FALSE || eqStatus==EQUALITY_FALSE_IN_MODEL ){ + //an argument is disequal, we are done + return; + }else{ + currentPairs.push_back(make_pair(x_shared, y_shared)); + } + } + } + } + for (unsigned c = 0; c < currentPairs.size(); ++ c) { + Trace("strings-cg-pair") << "TheoryStrings::computeCareGraph(): pair : " << currentPairs[c].first << " " << currentPairs[c].second << std::endl; + Trace("ajr-temp") << currentPairs[c].first << ", " << currentPairs[c].second << std::endl; + addCarePair(currentPairs[c].first, currentPairs[c].second); + } + } + } + }else{ + if( t2==NULL ){ + if( depth<(arity-1) ){ + //add care pairs internal to each child + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + addCarePairs( &it->second, NULL, arity, depth+1 ); + } + } + //add care pairs based on each pair of non-disequal arguments + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + std::map< TNode, quantifiers::TermArgTrie >::iterator it2 = it; + ++it2; + for( ; it2 != t1->d_data.end(); ++it2 ){ + if( !d_equalityEngine.areDisequal(it->first, it2->first, false) ){ + addCarePairs( &it->second, &it2->second, arity, depth+1 ); + } + } + } + }else{ + //add care pairs based on product of indices, non-disequal arguments + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it2 = t2->d_data.begin(); it2 != t2->d_data.end(); ++it2 ){ + if( !d_equalityEngine.areDisequal(it->first, it2->first, false) ){ + addCarePairs( &it->second, &it2->second, arity, depth+1 ); + } + } + } + } + } } void TheoryStrings::computeCareGraph(){ - Theory::computeCareGraph(); + //computing the care graph here is probably still necessary, due to operators that take non-string arguments TODO: verify + Trace("strings-cg") << "TheoryStrings::computeCareGraph(): Build term indices..." << std::endl; + std::map< Node, quantifiers::TermArgTrie > index; + std::map< Node, unsigned > arity; + unsigned functionTerms = d_functionsTerms.size(); + for (unsigned i = 0; i < functionTerms; ++ i) { + TNode f1 = d_functionsTerms[i]; + Trace("strings-cg") << "...build for " << f1 << std::endl; + Node op = f1.getOperator(); + std::vector< TNode > reps; + bool has_trigger_arg = false; + for( unsigned j=0; j<f1.getNumChildren(); j++ ){ + reps.push_back( d_equalityEngine.getRepresentative( f1[j] ) ); + if( d_equalityEngine.isTriggerTerm( f1[j], THEORY_STRINGS ) ){ + has_trigger_arg = true; + } + } + if( has_trigger_arg ){ + index[op].addTerm( f1, reps ); + arity[op] = reps.size(); + } + } + //for each index + for( std::map< Node, quantifiers::TermArgTrie >::iterator itii = index.begin(); itii != index.end(); ++itii ){ + Trace("strings-cg") << "TheoryStrings::computeCareGraph(): Process index " << itii->first << "..." << std::endl; + addCarePairs( &itii->second, NULL, arity[ itii->first ], 0 ); + } } void TheoryStrings::assertPendingFact(Node atom, bool polarity, Node exp) { @@ -1136,8 +1219,12 @@ void TheoryStrings::checkExtendedFuncsEval( int effort ) { }else{ if( !areEqual( n, nrc ) ){ if( n.getType().isBoolean() ){ - d_extf_exp[n].push_back( nrc==d_true ? n.negate() : n ); - conc = d_false; + if( areEqual( n, nrc==d_true ? d_false : d_true ) ){ + d_extf_exp[n].push_back( nrc==d_true ? n.negate() : n ); + conc = d_false; + }else{ + conc = nrc==d_true ? n : n.negate(); + } }else{ conc = n.eqNode( nrc ); } @@ -1145,7 +1232,7 @@ void TheoryStrings::checkExtendedFuncsEval( int effort ) { } if( !conc.isNull() ){ Trace("strings-extf") << " resolve extf : " << nr << " -> " << nrc << std::endl; - sendInference( d_extf_exp[n], conc, effort==0 ? "EXTF" : "EXTF-N", n.getType().isInteger() || d_extf_exp[n].empty() ); + sendInference( d_extf_exp[n], conc, effort==0 ? "EXTF" : "EXTF-N", true ); if( d_conflict ){ Trace("strings-extf-debug") << " conflict, return." << std::endl; return; @@ -1745,9 +1832,9 @@ bool TheoryStrings::normalizeEquivalenceClass( Node eqc, std::vector< Node > & n } nf.insert( nf.end(), normal_forms[nf_index].begin(), normal_forms[nf_index].end() ); nf_exp.insert( nf_exp.end(), normal_forms_exp[nf_index].begin(), normal_forms_exp[nf_index].end() ); - //if( eqc!=normal_form_src[nf_index] ){ - // nf_exp.push_back( eqc.eqNode( normal_form_src[nf_index] ) ); - //} + if( eqc!=normal_form_src[nf_index] ){ + nf_exp.push_back( eqc.eqNode( normal_form_src[nf_index] ) ); + } Trace("strings-solve-debug2") << "take normal form ... done" << std::endl; d_normal_forms_base[eqc] = normal_form_src[nf_index]; //*/ @@ -2639,23 +2726,17 @@ int TheoryStrings::processSimpleDeq( std::vector< Node >& nfi, std::vector< Node void TheoryStrings::addNormalFormPair( Node n1, Node n2 ){ if( !isNormalFormPair( n1, n2 ) ){ - //Assert( !isNormalFormPair( n1, n2 ) ); - NodeList* lst; - NodeListMap::iterator nf_i = d_nf_pairs.find( n1 ); - if( nf_i == d_nf_pairs.end() ){ - if( d_nf_pairs.find( n2 )!=d_nf_pairs.end() ){ - addNormalFormPair( n2, n1 ); - return; - } - lst = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false, - ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) ); - d_nf_pairs.insertDataFromContextMemory( n1, lst ); - Trace("strings-nf") << "Create cache for " << n1 << std::endl; - } else { - lst = (*nf_i).second; - } - Trace("strings-nf") << "Add normal form pair : " << n1 << " " << n2 << std::endl; - lst->push_back( n2 ); + int index = 0; + NodeIntMap::const_iterator it = d_nf_pairs.find( n1 ); + if( it!=d_nf_pairs.end() ){ + index = (*it).second; + } + d_nf_pairs[n1] = index + 1; + if( index<(int)d_nf_pairs_data[n1].size() ){ + d_nf_pairs_data[n1][index] = n2; + }else{ + d_nf_pairs_data[n1].push_back( n2 ); + } Assert( isNormalFormPair( n1, n2 ) ); } else { Trace("strings-nf-debug") << "Already a normal form pair " << n1 << " " << n2 << std::endl; @@ -2668,15 +2749,14 @@ bool TheoryStrings::isNormalFormPair( Node n1, Node n2 ) { } bool TheoryStrings::isNormalFormPair2( Node n1, Node n2 ) { - //Trace("strings-debug") << "is normal form pair. " << n1 << " " << n2 << std::endl; - NodeList* lst; - NodeListMap::iterator nf_i = d_nf_pairs.find( n1 ); - if( nf_i != d_nf_pairs.end() ) { - lst = (*nf_i).second; - for( NodeList::const_iterator i = lst->begin(); i != lst->end(); ++i ) { - Node n = *i; - if( n==n2 ) { - return true; + //Trace("strings-debug") << "is normal form pair. " << n1 << " " << n2 << std::endl; + NodeIntMap::const_iterator it = d_nf_pairs.find( n1 ); + if( it!=d_nf_pairs.end() ){ + Assert( d_nf_pairs_data.find( n1 )!=d_nf_pairs_data.end() ); + for( int i=0; i<(*it).second; i++ ){ + Assert( i<(int)d_nf_pairs_data[n1].size() ); + if( d_nf_pairs_data[n1][i]==n2 ){ + return true; } } } @@ -2752,7 +2832,7 @@ void TheoryStrings::sendInference( std::vector< Node >& exp, std::vector< Node > eq = eq.isNull() ? d_false : Rewriter::rewrite( eq ); if( eq!=d_true ){ if( Trace.isOn("strings-infer-debug") ){ - Trace("strings-infer-debug") << "infer : " << eq << " from: " << std::endl; + Trace("strings-infer-debug") << "By " << c << ", infer : " << eq << " from: " << std::endl; for( unsigned i=0; i<exp.size(); i++ ){ Trace("strings-infer-debug") << " " << exp[i] << std::endl; } @@ -2761,8 +2841,8 @@ void TheoryStrings::sendInference( std::vector< Node >& exp, std::vector< Node > } //Trace("strings-infer-debug") << "as lemma : " << asLemma << std::endl; } - bool doSendLemma = ( asLemma || eq==d_false || eq.getKind()==kind::OR || options::stringInferAsLemmas() ); - if( doSendLemma ){ + //check if we should send a lemma or an inference + if( asLemma || eq==d_false || eq.getKind()==kind::OR || !exp_n.empty() || options::stringInferAsLemmas() ){ Node eq_exp; if( options::stringRExplainLemmas() ){ eq_exp = mkExplain( exp, exp_n ); @@ -2780,7 +2860,6 @@ void TheoryStrings::sendInference( std::vector< Node >& exp, std::vector< Node > } sendLemma( eq_exp, eq, c ); }else{ - Assert( exp_n.empty() ); sendInfer( mkAnd( exp ), eq, c ); } } @@ -3064,30 +3143,56 @@ void TheoryStrings::getConcatVec( Node n, std::vector< Node >& c ) { void TheoryStrings::checkDeqNF() { std::vector< std::vector< Node > > cols; std::vector< Node > lts; - separateByLength( d_strings_eqc, cols, lts ); - for( unsigned i=0; i<cols.size(); i++ ){ - if( cols[i].size()>1 && d_lemma_cache.empty() ){ - Trace("strings-solve") << "- Verify disequalities are processed for " << cols[i][0]; - printConcat( d_normal_forms[cols[i][0]], "strings-solve" ); - Trace("strings-solve") << "... #eql = " << cols[i].size() << std::endl; - //must ensure that normal forms are disequal - for( unsigned j=0; j<cols[i].size(); j++ ){ - for( unsigned k=(j+1); k<cols[i].size(); k++ ){ - if( areDisequal( cols[i][j], cols[i][k] ) ){ - Assert( !d_conflict ); - //if( !areDisequal( cols[i][j], cols[i][k] ) ){ - // sendSplit( cols[i][j], cols[i][k], "D-NORM", true ); - // return; - //}else{ - Trace("strings-solve") << "- Compare " << cols[i][j] << " "; - printConcat( d_normal_forms[cols[i][j]], "strings-solve" ); - Trace("strings-solve") << " against " << cols[i][k] << " "; - printConcat( d_normal_forms[cols[i][k]], "strings-solve" ); - Trace("strings-solve") << "..." << std::endl; - if( processDeq( cols[i][j], cols[i][k] ) ){ - return; + std::map< Node, std::map< Node, bool > > processed; + + //for each pair of disequal strings, must determine whether their lengths are equal or disequal + bool addedLSplit = false; + for( NodeList::const_iterator id = d_ee_disequalities.begin(); id != d_ee_disequalities.end(); ++id ) { + Node eq = *id; + Node n[2]; + for( unsigned i=0; i<2; i++ ){ + n[i] = d_equalityEngine.getRepresentative( eq[i] ); + } + if( processed[n[0]].find( n[1] )==processed[n[0]].end() ){ + processed[n[0]][n[1]] = true; + Node lt[2]; + for( unsigned i=0; i<2; i++ ){ + EqcInfo* ei = getOrMakeEqcInfo( n[i], false ); + lt[i] = ei ? ei->d_length_term : Node::null(); + if( lt[i].isNull() ){ + lt[i] = eq[i]; + } + lt[i] = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, lt[i] ); + } + if( !areEqual( lt[0], lt[1] ) && !areDisequal( lt[0], lt[1] ) ){ + addedLSplit = true; + sendSplit( lt[0], lt[1], "DEQ-LENGTH-SP" ); + } + } + } + + if( !addedLSplit ){ + separateByLength( d_strings_eqc, cols, lts ); + for( unsigned i=0; i<cols.size(); i++ ){ + if( cols[i].size()>1 && d_lemma_cache.empty() ){ + Trace("strings-solve") << "- Verify disequalities are processed for " << cols[i][0] << ", normal form : "; + printConcat( d_normal_forms[cols[i][0]], "strings-solve" ); + Trace("strings-solve") << "... #eql = " << cols[i].size() << std::endl; + //must ensure that normal forms are disequal + for( unsigned j=0; j<cols[i].size(); j++ ){ + for( unsigned k=(j+1); k<cols[i].size(); k++ ){ + //for strings that are disequal, but have the same length + if( areDisequal( cols[i][j], cols[i][k] ) ){ + Assert( !d_conflict ); + Trace("strings-solve") << "- Compare " << cols[i][j] << " "; + printConcat( d_normal_forms[cols[i][j]], "strings-solve" ); + Trace("strings-solve") << " against " << cols[i][k] << " "; + printConcat( d_normal_forms[cols[i][k]], "strings-solve" ); + Trace("strings-solve") << "..." << std::endl; + if( processDeq( cols[i][j], cols[i][k] ) ){ + return; + } } - //} } } } @@ -3149,6 +3254,9 @@ void TheoryStrings::checkCardinality() { //int cardinality = options::stringCharCardinality(); //Trace("strings-solve-debug2") << "get cardinality: " << cardinality << endl; + //AJR: this will create a partition of eqc, where each collection has length that are pairwise propagated to be equal. + // we do not require disequalities between the lengths of each collection, since we split on disequalities between lengths of string terms that are disequal (DEQ-LENGTH-SP). + // TODO: revisit this? std::vector< std::vector< Node > > cols; std::vector< Node > lts; separateByLength( d_strings_eqc, cols, lts ); @@ -3159,54 +3267,51 @@ void TheoryStrings::checkCardinality() { if( cols[i].size() > 1 ) { // size > c^k unsigned card_need = 1; - double curr = (double)cols[i].size()-1; + double curr = (double)cols[i].size(); while( curr>d_card_size ){ curr = curr/(double)d_card_size; card_need++; } + Trace("strings-card") << "Need length " << card_need << " for this number of strings (where alphabet size is " << d_card_size << ")." << std::endl; Node cmp = NodeManager::currentNM()->mkNode( kind::GEQ, lr, NodeManager::currentNM()->mkConst( Rational( card_need ) ) ); cmp = Rewriter::rewrite( cmp ); if( cmp!=d_true ){ unsigned int int_k = (unsigned int)card_need; - bool allDisequal = true; for( std::vector< Node >::iterator itr1 = cols[i].begin(); itr1 != cols[i].end(); ++itr1) { for( std::vector< Node >::iterator itr2 = itr1 + 1; itr2 != cols[i].end(); ++itr2) { if(!areDisequal( *itr1, *itr2 )) { - allDisequal = false; // add split lemma sendSplit( *itr1, *itr2, "CARD-SP" ); return; } } } - if( allDisequal ){ - EqcInfo* ei = getOrMakeEqcInfo( lr, true ); - Trace("strings-card") << "Previous cardinality used for " << lr << " is " << ((int)ei->d_cardinality_lem_k.get()-1) << std::endl; - if( int_k+1 > ei->d_cardinality_lem_k.get() ){ - Node k_node = NodeManager::currentNM()->mkConst( ::CVC4::Rational( int_k ) ); - //add cardinality lemma - Node dist = NodeManager::currentNM()->mkNode( kind::DISTINCT, cols[i] ); - std::vector< Node > vec_node; - vec_node.push_back( dist ); - for( std::vector< Node >::iterator itr1 = cols[i].begin(); - itr1 != cols[i].end(); ++itr1) { - Node len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, *itr1 ); - if( len!=lr ) { - Node len_eq_lr = len.eqNode(lr); - vec_node.push_back( len_eq_lr ); - } - } - Node len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, cols[i][0] ); - Node cons = NodeManager::currentNM()->mkNode( kind::GEQ, len, k_node ); - cons = Rewriter::rewrite( cons ); - ei->d_cardinality_lem_k.set( int_k+1 ); - if( cons!=d_true ){ - sendInference( d_empty_vec, vec_node, cons, "CARDINALITY", true ); - return; + EqcInfo* ei = getOrMakeEqcInfo( lr, true ); + Trace("strings-card") << "Previous cardinality used for " << lr << " is " << ((int)ei->d_cardinality_lem_k.get()-1) << std::endl; + if( int_k+1 > ei->d_cardinality_lem_k.get() ){ + Node k_node = NodeManager::currentNM()->mkConst( ::CVC4::Rational( int_k ) ); + //add cardinality lemma + Node dist = NodeManager::currentNM()->mkNode( kind::DISTINCT, cols[i] ); + std::vector< Node > vec_node; + vec_node.push_back( dist ); + for( std::vector< Node >::iterator itr1 = cols[i].begin(); + itr1 != cols[i].end(); ++itr1) { + Node len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, *itr1 ); + if( len!=lr ) { + Node len_eq_lr = len.eqNode(lr); + vec_node.push_back( len_eq_lr ); } } + Node len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, cols[i][0] ); + Node cons = NodeManager::currentNM()->mkNode( kind::GEQ, len, k_node ); + cons = Rewriter::rewrite( cons ); + ei->d_cardinality_lem_k.set( int_k+1 ); + if( cons!=d_true ){ + sendInference( d_empty_vec, vec_node, cons, "CARDINALITY", true ); + return; + } } } } @@ -3275,11 +3380,9 @@ void TheoryStrings::separateByLength(std::vector< Node >& n, Assert( d_equalityEngine.getRepresentative(eqc)==eqc ); EqcInfo* ei = getOrMakeEqcInfo( eqc, false ); Node lt = ei ? ei->d_length_term : Node::null(); - Trace("ajr-temp") << "Length term for " << eqc << " is " << lt << std::endl; if( !lt.isNull() ){ lt = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, lt ); Node r = d_equalityEngine.getRepresentative( lt ); - Trace("ajr-temp") << "Length term rep for " << eqc << " is " << lt << std::endl; if( eqc_to_leqc.find( r )==eqc_to_leqc.end() ){ eqc_to_leqc[r] = leqc_counter; leqc_to_eqc[leqc_counter] = r; @@ -3318,6 +3421,26 @@ void TheoryStrings::updateMpl( Node n, int b ) { } */ + +unsigned TheoryStrings::getNumMemberships( Node n, bool isPos ) { + if( isPos ){ + NodeIntMap::const_iterator it = d_pos_memberships.find( n ); + if( it!=d_pos_memberships.end() ){ + return (*it).second; + } + }else{ + NodeIntMap::const_iterator it = d_neg_memberships.find( n ); + if( it!=d_neg_memberships.end() ){ + return (*it).second; + } + } + return 0; +} + +Node TheoryStrings::getMembership( Node n, bool isPos, unsigned i ) { + return isPos ? d_pos_memberships_data[n][i] : d_neg_memberships_data[n][i]; +} + //// Regular Expressions Node TheoryStrings::mkRegExpAntec(Node atom, Node ant) { if(d_regexp_ant.find(atom) == d_regexp_ant.end()) { @@ -3395,10 +3518,8 @@ bool TheoryStrings::normalizePosMemberships(std::map< Node, std::vector< Node > Trace("regexp-check") << "Normalizing Positive Memberships ... " << std::endl; - for(NodeListMap::const_iterator itr_xr = d_pos_memberships.begin(); - itr_xr != d_pos_memberships.end(); ++itr_xr ) { + for( NodeIntMap::const_iterator itr_xr = d_pos_memberships.begin(); itr_xr != d_pos_memberships.end(); ++itr_xr ){ Node x = (*itr_xr).first; - NodeList* lst = (*itr_xr).second; Node nf_x = x; std::vector< Node > nf_x_exp; if(d_normal_forms.find( x ) != d_normal_forms.end()) { @@ -3412,10 +3533,11 @@ bool TheoryStrings::normalizePosMemberships(std::map< Node, std::vector< Node > std::vector< Node > vec_x; std::vector< Node > vec_r; - for(NodeList::const_iterator itr_lst = lst->begin(); - itr_lst != lst->end(); ++itr_lst) { - Node r = *itr_lst; - Node nf_r = normalizeRegexp((*lst)[0]); + unsigned n_pmem = (*itr_xr).second; + Assert( getNumMemberships( x, true )==n_pmem ); + for( unsigned k=0; k<n_pmem; k++ ){ + Node r = getMembership( x, true, k ); + Node nf_r = normalizeRegexp( r ); //AJR: fixed (was normalizing mem #0 always) Node memb = NodeManager::currentNM()->mkNode(kind::STRING_IN_REGEXP, nf_x, nf_r); if(d_processed_memberships.find(memb) == d_processed_memberships.end()) { if(d_regexp_opr.checkConstRegExp(nf_r)) { @@ -3645,46 +3767,43 @@ void TheoryStrings::checkMemberships() { Trace("regexp-debug") << "Checking Memberships ... " << std::endl; //if(options::stringEIT()) { //TODO: Opt for normal forms - for(NodeListMap::const_iterator itr_xr = d_pos_memberships.begin(); - itr_xr != d_pos_memberships.end(); ++itr_xr ) { + for( NodeIntMap::const_iterator itr_xr = d_pos_memberships.begin(); itr_xr != d_pos_memberships.end(); ++itr_xr ){ bool spflag = false; Node x = (*itr_xr).first; - NodeList* lst = (*itr_xr).second; Trace("regexp-debug") << "Checking Memberships for " << x << std::endl; if(d_inter_index.find(x) == d_inter_index.end()) { d_inter_index[x] = 0; } int cur_inter_idx = d_inter_index[x]; - if(cur_inter_idx != (int)lst->size()) { - if(lst->size() == 1) { - d_inter_cache[x] = (*lst)[0]; + unsigned n_pmem = (*itr_xr).second; + Assert( getNumMemberships( x, true )==n_pmem ); + if( cur_inter_idx != (int)n_pmem ) { + if( n_pmem == 1) { + d_inter_cache[x] = getMembership( x, true, 0 ); d_inter_index[x] = 1; Trace("regexp-debug") << "... only one choice " << std::endl; - } else if(lst->size() > 1) { + } else if(n_pmem > 1) { Node r; if(d_inter_cache.find(x) != d_inter_cache.end()) { r = d_inter_cache[x]; } if(r.isNull()) { - r = (*lst)[0]; + r = getMembership( x, true, 0 ); cur_inter_idx = 1; } - NodeList::const_iterator itr_lst = lst->begin(); - for(int i=0; i<cur_inter_idx; i++) { - ++itr_lst; - } - Trace("regexp-debug") << "... staring from : " << cur_inter_idx << ", we have " << lst->size() << std::endl; - for(;itr_lst != lst->end(); ++itr_lst) { - Node r2 = *itr_lst; + + unsigned k_start = cur_inter_idx; + Trace("regexp-debug") << "... staring from : " << cur_inter_idx << ", we have " << n_pmem << std::endl; + for(unsigned k = k_start; k<n_pmem; k++) { + Node r2 = getMembership( x, true, k ); r = d_regexp_opr.intersect(r, r2, spflag); if(spflag) { break; } else if(r == d_emptyRegexp) { std::vector< Node > vec_nodes; - ++itr_lst; - for(NodeList::const_iterator itr2 = lst->begin(); - itr2 != itr_lst; ++itr2) { - Node n = NodeManager::currentNM()->mkNode(kind::STRING_IN_REGEXP, x, *itr2); + for( unsigned kk=0; kk<=k; kk++ ){ + Node rr = getMembership( x, true, kk ); + Node n = NodeManager::currentNM()->mkNode(kind::STRING_IN_REGEXP, x, rr); vec_nodes.push_back( n ); } Node conc; @@ -3699,7 +3818,7 @@ void TheoryStrings::checkMemberships() { //updates if(!d_conflict && !spflag) { d_inter_cache[x] = r; - d_inter_index[x] = (int)lst->size(); + d_inter_index[x] = (int)n_pmem; } } } @@ -4322,44 +4441,52 @@ void TheoryStrings::addMembership(Node assertion) { Node x = atom[0]; Node r = atom[1]; if(polarity) { - NodeList* lst; - NodeListMap::iterator itr_xr = d_pos_memberships.find( x ); - if( itr_xr == d_pos_memberships.end() ){ - lst = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false, - ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) ); - d_pos_memberships.insertDataFromContextMemory( x, lst ); - } else { - lst = (*itr_xr).second; - } - //check - for( NodeList::const_iterator itr = lst->begin(); itr != lst->end(); ++itr ) { - if( r == *itr ) { - return; + int index = 0; + NodeIntMap::const_iterator it = d_pos_memberships.find( x ); + if( it!=d_nf_pairs.end() ){ + index = (*it).second; + for( int k=0; k<index; k++ ){ + if( k<(int)d_pos_memberships_data[x].size() ){ + if( d_pos_memberships_data[x][k]==r ){ + return; + } + }else{ + break; + } } } - lst->push_back( r ); + d_pos_memberships[x] = index + 1; + if( index<(int)d_pos_memberships_data[x].size() ){ + d_pos_memberships_data[x][index] = r; + }else{ + d_pos_memberships_data[x].push_back( r ); + } } else if(!options::stringIgnNegMembership()) { /*if(options::stringEIT() && d_regexp_opr.checkConstRegExp(r)) { int rt; Node r2 = d_regexp_opr.complement(r, rt); Node a = NodeManager::currentNM()->mkNode(kind::STRING_IN_REGEXP, x, r2); }*/ - NodeList* lst; - NodeListMap::iterator itr_xr = d_neg_memberships.find( x ); - if( itr_xr == d_neg_memberships.end() ){ - lst = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false, - ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) ); - d_neg_memberships.insertDataFromContextMemory( x, lst ); - } else { - lst = (*itr_xr).second; - } - //check - for( NodeList::const_iterator itr = lst->begin(); itr != lst->end(); ++itr ) { - if( r == *itr ) { - return; + int index = 0; + NodeIntMap::const_iterator it = d_neg_memberships.find( x ); + if( it!=d_nf_pairs.end() ){ + index = (*it).second; + for( int k=0; k<index; k++ ){ + if( k<(int)d_neg_memberships_data[x].size() ){ + if( d_neg_memberships_data[x][k]==r ){ + return; + } + }else{ + break; + } } } - lst->push_back( r ); + d_neg_memberships[x] = index + 1; + if( index<(int)d_neg_memberships_data[x].size() ){ + d_neg_memberships_data[x][index] = r; + }else{ + d_neg_memberships_data[x].push_back( r ); + } } // old if(polarity || !options::stringIgnNegMembership()) { diff --git a/src/theory/strings/theory_strings.h b/src/theory/strings/theory_strings.h index b9da524de..2deb09654 100644 --- a/src/theory/strings/theory_strings.h +++ b/src/theory/strings/theory_strings.h @@ -33,6 +33,11 @@ namespace CVC4 { namespace theory { + +namespace quantifiers{ + class TermArgTrie; +} + namespace strings { /** @@ -45,7 +50,6 @@ typedef expr::Attribute< StringsProxyVarAttributeId, bool > StringsProxyVarAttri class TheoryStrings : public Theory { typedef context::CDChunkList<Node> NodeList; - typedef context::CDHashMap<Node, NodeList*, NodeHashFunction> NodeListMap; typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap; typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap; typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; @@ -160,7 +164,8 @@ private: std::map< Node, std::vector< Node > > d_normal_forms_exp; std::map< Node, std::map< Node, std::map< bool, int > > > d_normal_forms_exp_depend; //map of pairs of terms that have the same normal form - NodeListMap d_nf_pairs; + NodeIntMap d_nf_pairs; + std::map< Node, std::vector< Node > > d_nf_pairs_data; void addNormalFormPair( Node n1, Node n2 ); bool isNormalFormPair( Node n1, Node n2 ); bool isNormalFormPair2( Node n1, Node n2 ); @@ -176,6 +181,8 @@ private: // extended functions inferences cache NodeSet d_extf_infer_cache; std::vector< Node > d_empty_vec; + // + NodeList d_ee_disequalities; private: NodeSet d_congruent; std::map< Node, Node > d_eqc_to_const; @@ -236,6 +243,8 @@ private: //maintain which concat terms have the length lemma instantiated NodeNodeMap d_proxy_var; NodeNodeMap d_proxy_var_to_length; + /** All the function terms that the theory has seen */ + context::CDList<TNode> d_functionsTerms; private: //initial check void checkInit(); @@ -304,6 +313,8 @@ private: //cardinality check void checkCardinality(); +private: + void addCarePairs( quantifiers::TermArgTrie * t1, quantifiers::TermArgTrie * t2, unsigned arity, unsigned depth ); public: /** preregister term */ void preRegisterTerm(TNode n); @@ -410,8 +421,12 @@ private: NodeSet d_regexp_ucached; NodeSet d_regexp_ccached; // stored assertions - NodeListMap d_pos_memberships; - NodeListMap d_neg_memberships; + NodeIntMap d_pos_memberships; + std::map< Node, std::vector< Node > > d_pos_memberships_data; + NodeIntMap d_neg_memberships; + std::map< Node, std::vector< Node > > d_neg_memberships_data; + unsigned getNumMemberships( Node n, bool isPos ); + Node getMembership( Node n, bool isPos, unsigned i ); // semi normal forms for symbolic expression std::map< Node, Node > d_nf_regexps; std::map< Node, std::vector< Node > > d_nf_regexps_exp; diff --git a/src/theory/strings/theory_strings_preprocess.cpp b/src/theory/strings/theory_strings_preprocess.cpp index a4f42bddd..ba811644a 100644 --- a/src/theory/strings/theory_strings_preprocess.cpp +++ b/src/theory/strings/theory_strings_preprocess.cpp @@ -33,6 +33,10 @@ StringsPreprocess::StringsPreprocess( context::UserContext* u ) : d_cache( u ){ d_zero = NodeManager::currentNM()->mkConst( ::CVC4::Rational(0) ); } +StringsPreprocess::~StringsPreprocess(){ + +} + /* int StringsPreprocess::checkFixLenVar( Node t ) { int ret = 2; diff --git a/src/theory/strings/theory_strings_preprocess.h b/src/theory/strings/theory_strings_preprocess.h index 5bc9667ea..abc7b5a91 100644 --- a/src/theory/strings/theory_strings_preprocess.h +++ b/src/theory/strings/theory_strings_preprocess.h @@ -40,6 +40,7 @@ private: Node simplify( Node t, std::vector< Node > &new_nodes ); public: StringsPreprocess( context::UserContext* u ); + ~StringsPreprocess(); Node decompose( Node t, std::vector< Node > &new_nodes ); void simplify(std::vector< Node > &vec_node); diff --git a/src/theory/term_registration_visitor.cpp b/src/theory/term_registration_visitor.cpp index 830e7f809..e758002fa 100644 --- a/src/theory/term_registration_visitor.cpp +++ b/src/theory/term_registration_visitor.cpp @@ -37,8 +37,12 @@ bool PreRegisterVisitor::alreadyVisited(TNode current, TNode parent) { if( ( parent.getKind() == kind::FORALL || parent.getKind() == kind::EXISTS || - parent.getKind() == kind::REWRITE_RULE /*|| - parent.getKind() == kind::CARDINALITY_CONSTRAINT*/ ) && + parent.getKind() == kind::REWRITE_RULE || + parent.getKind() == kind::SEP_STAR || + parent.getKind() == kind::SEP_WAND || + ( parent.getKind() == kind::SEP_LABEL && current.getType().isBoolean() ) + // parent.getKind() == kind::CARDINALITY_CONSTRAINT + ) && current != parent ) { Debug("register::internal") << "quantifier:true" << std::endl; return true; @@ -64,14 +68,8 @@ bool PreRegisterVisitor::alreadyVisited(TNode current, TNode parent) { TypeNode type = current.getType(); typeTheoryId = Theory::theoryOf(type); if (typeTheoryId != currentTheoryId) { - if (options::finiteModelFind() && type.isSort()) { - // We're looking for finite models + if (type.isInterpretedFinite()) { useType = true; - } else { - Cardinality card = type.getCardinality(); - if (card.isFinite()) { - useType = true; - } } } } @@ -130,14 +128,8 @@ void PreRegisterVisitor::visit(TNode current, TNode parent) { TypeNode type = current.getType(); typeTheoryId = Theory::theoryOf(type); if (typeTheoryId != currentTheoryId) { - if (options::finiteModelFind() && type.isSort()) { - // We're looking for finite models + if (type.isInterpretedFinite()) { useType = true; - } else { - Cardinality card = type.getCardinality(); - if (card.isFinite()) { - useType = true; - } } } } @@ -189,8 +181,12 @@ bool SharedTermsVisitor::alreadyVisited(TNode current, TNode parent) const { if( ( parent.getKind() == kind::FORALL || parent.getKind() == kind::EXISTS || - parent.getKind() == kind::REWRITE_RULE /*|| - parent.getKind() == kind::CARDINALITY_CONSTRAINT*/ ) && + parent.getKind() == kind::REWRITE_RULE || + parent.getKind() == kind::SEP_STAR || + parent.getKind() == kind::SEP_WAND || + ( parent.getKind() == kind::SEP_LABEL && current.getType().isBoolean() ) + // parent.getKind() == kind::CARDINALITY_CONSTRAINT + ) && current != parent ) { Debug("register::internal") << "quantifier:true" << std::endl; return true; @@ -222,14 +218,8 @@ bool SharedTermsVisitor::alreadyVisited(TNode current, TNode parent) const { TypeNode type = current.getType(); typeTheoryId = Theory::theoryOf(type); if (typeTheoryId != currentTheoryId) { - if (options::finiteModelFind() && type.isSort()) { - // We're looking for finite models + if (type.isInterpretedFinite()) { useType = true; - } else { - Cardinality card = type.getCardinality(); - if (card.isFinite()) { - useType = true; - } } } } @@ -244,14 +234,8 @@ bool SharedTermsVisitor::alreadyVisited(TNode current, TNode parent) const { TypeNode type = current.getType(); typeTheoryId = Theory::theoryOf(type); if (typeTheoryId != currentTheoryId) { - if (options::finiteModelFind() && type.isSort()) { - // We're looking for finite models + if (type.isInterpretedFinite()) { useType = true; - } else { - Cardinality card = type.getCardinality(); - if (card.isFinite()) { - useType = true; - } } } } @@ -297,14 +281,8 @@ void SharedTermsVisitor::visit(TNode current, TNode parent) { TypeNode type = current.getType(); typeTheoryId = Theory::theoryOf(type); if (typeTheoryId != currentTheoryId) { - if (options::finiteModelFind() && type.isSort()) { - // We're looking for finite models + if (type.isInterpretedFinite()) { useType = true; - } else { - Cardinality card = type.getCardinality(); - if (card.isFinite()) { - useType = true; - } } } } diff --git a/src/theory/theory.h b/src/theory/theory.h index 382d4cf65..e8518b1f6 100644 --- a/src/theory/theory.h +++ b/src/theory/theory.h @@ -781,6 +781,14 @@ public: assertions_iterator facts_end() const { return d_facts.end(); } + /** + * Whether facts have been asserted to this theory. + * + * @return true iff facts have been asserted to this theory. + */ + bool hasFacts() { + return !d_facts.empty(); + } typedef context::CDList<TNode>::const_iterator shared_terms_iterator; diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index f2231ff7a..881acdddd 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -26,6 +26,8 @@ #include "options/bv_options.h" #include "options/options.h" #include "options/quantifiers_options.h" +#include "proof/cnf_proof.h" +#include "proof/lemma_proof.h" #include "proof/proof_manager.h" #include "proof/theory_proof.h" #include "smt/ite_removal.h" @@ -53,6 +55,104 @@ using namespace CVC4::theory; namespace CVC4 { +inline void flattenAnd(Node n, std::vector<TNode>& out){ + Assert(n.getKind() == kind::AND); + for(Node::iterator i=n.begin(), i_end=n.end(); i != i_end; ++i){ + Node curr = *i; + if(curr.getKind() == kind::AND){ + flattenAnd(curr, out); + }else{ + out.push_back(curr); + } + } +} + +inline Node flattenAnd(Node n){ + std::vector<TNode> out; + flattenAnd(n, out); + return NodeManager::currentNM()->mkNode(kind::AND, out); +} + +theory::LemmaStatus TheoryEngine::EngineOutputChannel::lemma(TNode lemma, + ProofRule rule, + bool removable, + bool preprocess, + bool sendAtoms) + throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException) { + Debug("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::lemma(" << lemma << ")" << ", preprocess = " << preprocess << std::endl; + ++ d_statistics.lemmas; + d_engine->d_outputChannelUsed = true; + + LemmaProofRecipe* proofRecipe = NULL; + PROOF({ + // Theory lemmas have one step that proves the empty clause + proofRecipe = new LemmaProofRecipe; + + Node emptyNode; + LemmaProofRecipe::ProofStep proofStep(d_theory, emptyNode); + + Node rewritten; + if (lemma.getKind() == kind::OR) { + for (unsigned i = 0; i < lemma.getNumChildren(); ++i) { + rewritten = theory::Rewriter::rewrite(lemma[i]); + if (rewritten != lemma[i]) { + proofRecipe->addRewriteRule(lemma[i].negate(), rewritten.negate()); + } + proofStep.addAssertion(lemma[i]); + proofRecipe->addBaseAssertion(rewritten); + } + } else { + rewritten = theory::Rewriter::rewrite(lemma); + if (rewritten != lemma) { + proofRecipe->addRewriteRule(lemma.negate(), rewritten.negate()); + } + proofStep.addAssertion(lemma); + proofRecipe->addBaseAssertion(rewritten); + } + proofRecipe->addStep(proofStep); + }); + + theory::LemmaStatus result = d_engine->lemma(lemma, + rule, + false, + removable, + preprocess, + sendAtoms ? d_theory : theory::THEORY_LAST, + proofRecipe); + PROOF(delete proofRecipe;); + return result; +} + +theory::LemmaStatus TheoryEngine::EngineOutputChannel::splitLemma(TNode lemma, bool removable) + throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException) { + Debug("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::lemma(" << lemma << ")" << std::endl; + ++ d_statistics.lemmas; + d_engine->d_outputChannelUsed = true; + + + LemmaProofRecipe* proofRecipe = NULL; + Debug("pf::explain") << "TheoryEngine::EngineOutputChannel::splitLemma( " << lemma << " )" << std::endl; + theory::LemmaStatus result = d_engine->lemma(lemma, RULE_SPLIT, false, removable, false, d_theory, proofRecipe); + return result; +} + +bool TheoryEngine::EngineOutputChannel::propagate(TNode literal) + throw(AssertionException, UnsafeInterruptException) { + Debug("theory::propagate") << "EngineOutputChannel<" << d_theory << ">::propagate(" << literal << ")" << std::endl; + ++ d_statistics.propagations; + d_engine->d_outputChannelUsed = true; + return d_engine->propagate(literal, d_theory); +} + +void TheoryEngine::EngineOutputChannel::conflict(TNode conflictNode, Proof* pf) + throw(AssertionException, UnsafeInterruptException) { + Trace("theory::conflict") << "EngineOutputChannel<" << d_theory << ">::conflict(" << conflictNode << ")" << std::endl; + Assert (pf == NULL); // Theory shouldn't be producing proofs yet + ++ d_statistics.conflicts; + d_engine->d_outputChannelUsed = true; + d_engine->conflict(conflictNode, d_theory); +} + void TheoryEngine::finishInit() { // initialize the quantifiers engine d_quantEngine = new QuantifiersEngine(d_context, d_userContext, this); @@ -161,7 +261,9 @@ TheoryEngine::TheoryEngine(context::Context* context, d_true = NodeManager::currentNM()->mkConst<bool>(true); d_false = NodeManager::currentNM()->mkConst<bool>(false); - PROOF (ProofManager::currentPM()->initTheoryProofEngine(); ); +#ifdef CVC4_PROOF + ProofManager::currentPM()->initTheoryProofEngine(); +#endif d_iteUtilities = new ITEUtilities(d_iteRemover.getContainsVisitor()); @@ -416,18 +518,29 @@ void TheoryEngine::check(Theory::Effort effort) { // Must consult quantifiers theory for last call to ensure sat, or otherwise add a lemma if( effort == Theory::EFFORT_FULL && ! d_inConflict && ! needCheck() ) { - //d_theoryTable[THEORY_STRINGS]->check(Theory::EFFORT_LAST_CALL); - if(d_logicInfo.isQuantified()) { - // quantifiers engine must pass effort last call check - d_quantEngine->check(Theory::EFFORT_LAST_CALL); - // if returning incomplete or SAT, we have ensured that the model in the quantifiers engine has been built - } else if(options::produceModels()) { - // must build model at this point - d_curr_model_builder->buildModel(d_curr_model, true); + //calls to theories requiring the model go here + //FIXME: this should not be theory-specific + if(d_logicInfo.isTheoryEnabled(THEORY_SEP)) { + Assert( d_theoryTable[THEORY_SEP]!=NULL ); + if( d_theoryTable[THEORY_SEP]->hasFacts() ){ + // must build model at this point + d_curr_model_builder->buildModel(getModel(), false); + d_theoryTable[THEORY_SEP]->check(Theory::EFFORT_LAST_CALL); + } } - Trace("theory::assertions-model") << endl; - if (Trace.isOn("theory::assertions-model")) { - printAssertions("theory::assertions-model"); + if( ! d_inConflict && ! needCheck() ){ + if(d_logicInfo.isQuantified()) { + // quantifiers engine must pass effort last call check + d_quantEngine->check(Theory::EFFORT_LAST_CALL); + // if returning incomplete or SAT, we have ensured that the model in the quantifiers engine has been built + } else if(options::produceModels()) { + // must build model at this point + d_curr_model_builder->buildModel(getModel(), true); + } + Trace("theory::assertions-model") << endl; + if (Trace.isOn("theory::assertions-model")) { + printAssertions("theory::assertions-model"); + } } } @@ -497,7 +610,10 @@ void TheoryEngine::combineTheories() { // We need to split on it Debug("combineTheories") << "TheoryEngine::combineTheories(): requesting a split " << endl; - lemma(equality.orNode(equality.notNode()), RULE_INVALID, false, false, false, carePair.theory, carePair.theory); + + LemmaProofRecipe* proofRecipe = NULL; + lemma(equality.orNode(equality.notNode()), RULE_INVALID, false, false, false, carePair.theory, proofRecipe); + // This code is supposed to force preference to follow what the theory models already have // but it doesn't seem to make a big difference - need to explore more -Clark // if (true) { @@ -963,7 +1079,7 @@ bool TheoryEngine::markPropagation(TNode assertion, TNode originalAssertion, the void TheoryEngine::assertToTheory(TNode assertion, TNode originalAssertion, theory::TheoryId toTheoryId, theory::TheoryId fromTheoryId) { - Trace("theory::assertToTheory") << "TheoryEngine::assertToTheory(" << assertion << ", " << toTheoryId << ", " << fromTheoryId << ")" << endl; + Trace("theory::assertToTheory") << "TheoryEngine::assertToTheory(" << assertion << ", " << originalAssertion << "," << toTheoryId << ", " << fromTheoryId << ")" << endl; Assert(toTheoryId != fromTheoryId); if(toTheoryId != THEORY_SAT_SOLVER && @@ -1176,6 +1292,23 @@ bool TheoryEngine::propagate(TNode literal, theory::TheoryId theory) { assertToTheory(literal, literal, /* to */ THEORY_BUILTIN, /* from */ theory); } } else { + // We could be propagating a unit-clause lemma. In this case, we need to provide a + // recipe. + // TODO: Consider putting this someplace else? This is the only refence to the proof + // manager in this class. + + PROOF({ + LemmaProofRecipe proofRecipe; + proofRecipe.addBaseAssertion(literal); + + Node emptyNode; + LemmaProofRecipe::ProofStep proofStep(theory, emptyNode); + proofStep.addAssertion(literal); + proofRecipe.addStep(proofStep); + + ProofManager::getCnfProof()->setProofRecipe(&proofRecipe); + }); + // Just send off to the SAT solver Assert(d_propEngine->isSatLiteral(literal)); assertToTheory(literal, literal, /* to */ THEORY_SAT_SOLVER, /* from */ theory); @@ -1270,7 +1403,7 @@ static Node mkExplanation(const std::vector<NodeTheoryPair>& explanation) { return conjunction; } -NodeTheoryPair TheoryEngine::getExplanationAndExplainer(TNode node) { +Node TheoryEngine::getExplanationAndRecipe(TNode node, LemmaProofRecipe* proofRecipe) { Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << "): current propagation index = " << d_propagationMapTimestamp << endl; bool polarity = node.getKind() != kind::NOT; @@ -1278,32 +1411,90 @@ NodeTheoryPair TheoryEngine::getExplanationAndExplainer(TNode node) { // If we're not in shared mode, explanations are simple if (!d_logicInfo.isSharingEnabled()) { + Debug("theory::explain") << "TheoryEngine::getExplanation: sharing is NOT enabled. " + << " Responsible theory is: " + << theoryOf(atom)->getId() << std::endl; + Node explanation = theoryOf(atom)->explain(node); Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << endl; - return NodeTheoryPair(explanation, theoryOf(atom)->getId()); + PROOF({ + if(proofRecipe) { + Node emptyNode; + LemmaProofRecipe::ProofStep proofStep(theoryOf(atom)->getId(), emptyNode); + proofStep.addAssertion(node); + proofRecipe->addBaseAssertion(node); + + if (explanation.getKind() == kind::AND) { + // If the explanation is a conjunction, the recipe for the corresponding lemma is + // the negation of its conjuncts. + Node flat = flattenAnd(explanation); + for (unsigned i = 0; i < flat.getNumChildren(); ++i) { + if (flat[i].isConst() && flat[i].getConst<bool>()) { + ++ i; + continue; + } + if (flat[i].getKind() == kind::NOT && + flat[i][0].isConst() && !flat[i][0].getConst<bool>()) { + ++ i; + continue; + } + Debug("theory::explain") << "TheoryEngine::getExplanationAndRecipe: adding recipe assertion: " + << flat[i].negate() << std::endl; + proofStep.addAssertion(flat[i].negate()); + proofRecipe->addBaseAssertion(flat[i].negate()); + } + } else { + // The recipe for proving it is by negating it. "True" is not an acceptable reason. + if (!((explanation.isConst() && explanation.getConst<bool>()) || + (explanation.getKind() == kind::NOT && + explanation[0].isConst() && !explanation[0].getConst<bool>()))) { + proofStep.addAssertion(explanation.negate()); + proofRecipe->addBaseAssertion(explanation.negate()); + } + } + + proofRecipe->addStep(proofStep); + } + }); + + return explanation; } + Debug("theory::explain") << "TheoryEngine::getExplanation: sharing IS enabled" << std::endl; + // Initial thing to explain NodeTheoryPair toExplain(node, THEORY_SAT_SOLVER, d_propagationMapTimestamp); Assert(d_propagationMap.find(toExplain) != d_propagationMap.end()); NodeTheoryPair nodeExplainerPair = d_propagationMap[toExplain]; + Debug("theory::explain") << "TheoryEngine::getExplanation: explainer for node " + << nodeExplainerPair.node + << " is theory: " << nodeExplainerPair.theory << std::endl; TheoryId explainer = nodeExplainerPair.theory; // Create the workplace for explanations std::vector<NodeTheoryPair> explanationVector; explanationVector.push_back(d_propagationMap[toExplain]); // Process the explanation - getExplanation(explanationVector); + if (proofRecipe) { + Node emptyNode; + LemmaProofRecipe::ProofStep proofStep(explainer, emptyNode); + proofStep.addAssertion(node); + proofRecipe->addStep(proofStep); + proofRecipe->addBaseAssertion(node); + } + + getExplanation(explanationVector, proofRecipe); Node explanation = mkExplanation(explanationVector); Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << endl; - return NodeTheoryPair(explanation, explainer); + return explanation; } Node TheoryEngine::getExplanation(TNode node) { - return getExplanationAndExplainer(node).node; + LemmaProofRecipe *dontCareRecipe = NULL; + return getExplanationAndRecipe(node, dontCareRecipe); } struct AtomsCollect { @@ -1406,7 +1597,7 @@ theory::LemmaStatus TheoryEngine::lemma(TNode node, bool removable, bool preprocess, theory::TheoryId atomsTo, - theory::TheoryId ownerTheory) { + LemmaProofRecipe* proofRecipe) { // For resource-limiting (also does a time check). // spendResource(); @@ -1456,10 +1647,10 @@ theory::LemmaStatus TheoryEngine::lemma(TNode node, } // assert to prop engine - d_propEngine->assertLemma(additionalLemmas[0], negated, removable, rule, ownerTheory, node); + d_propEngine->assertLemma(additionalLemmas[0], negated, removable, rule, proofRecipe, node); for (unsigned i = 1; i < additionalLemmas.size(); ++ i) { additionalLemmas[i] = theory::Rewriter::rewrite(additionalLemmas[i]); - d_propEngine->assertLemma(additionalLemmas[i], false, removable, rule, ownerTheory, node); + d_propEngine->assertLemma(additionalLemmas[i], false, removable, rule, proofRecipe, node); } // WARNING: Below this point don't assume additionalLemmas[0] to be not negated. @@ -1493,22 +1684,69 @@ void TheoryEngine::conflict(TNode conflict, TheoryId theoryId) { << CheckSatCommand(conflict.toExpr()); } + LemmaProofRecipe* proofRecipe = NULL; + PROOF({ + proofRecipe = new LemmaProofRecipe; + Node emptyNode; + LemmaProofRecipe::ProofStep proofStep(theoryId, emptyNode); + + if (conflict.getKind() == kind::AND) { + for (unsigned i = 0; i < conflict.getNumChildren(); ++i) { + proofStep.addAssertion(conflict[i].negate()); + } + } else { + proofStep.addAssertion(conflict.negate()); + } + + proofRecipe->addStep(proofStep); + }); + // In the multiple-theories case, we need to reconstruct the conflict if (d_logicInfo.isSharingEnabled()) { // Create the workplace for explanations std::vector<NodeTheoryPair> explanationVector; explanationVector.push_back(NodeTheoryPair(conflict, theoryId, d_propagationMapTimestamp)); + // Process the explanation - getExplanation(explanationVector); + getExplanation(explanationVector, proofRecipe); Node fullConflict = mkExplanation(explanationVector); Debug("theory::conflict") << "TheoryEngine::conflict(" << conflict << ", " << theoryId << "): full = " << fullConflict << endl; Assert(properConflict(fullConflict)); - lemma(fullConflict, RULE_CONFLICT, true, true, false, THEORY_LAST, theoryId); + lemma(fullConflict, RULE_CONFLICT, true, true, false, THEORY_LAST, proofRecipe); + } else { // When only one theory, the conflict should need no processing Assert(properConflict(conflict)); - lemma(conflict, RULE_CONFLICT, true, true, false, THEORY_LAST, theoryId); + PROOF({ + if (conflict.getKind() == kind::AND) { + // If the conflict is a conjunction, the corresponding lemma is derived by negating + // its conjuncts. + for (unsigned i = 0; i < conflict.getNumChildren(); ++i) { + if (conflict[i].isConst() && conflict[i].getConst<bool>()) { + ++ i; + continue; + } + if (conflict[i].getKind() == kind::NOT && + conflict[i][0].isConst() && !conflict[i][0].getConst<bool>()) { + ++ i; + continue; + } + proofRecipe->getStep(0)->addAssertion(conflict[i].negate()); + proofRecipe->addBaseAssertion(conflict[i].negate()); + } + } else { + proofRecipe->getStep(0)->addAssertion(conflict.negate()); + proofRecipe->addBaseAssertion(conflict.negate()); + } + }); + + lemma(conflict, RULE_CONFLICT, true, true, false, THEORY_LAST, proofRecipe); } + + PROOF({ + delete proofRecipe; + proofRecipe = NULL; + }); } void TheoryEngine::staticInitializeBVOptions(const std::vector<Node>& assertions) { @@ -1558,15 +1796,9 @@ void TheoryEngine::mkAckermanizationAsssertions(std::vector<Node>& assertions) { Node TheoryEngine::ppSimpITE(TNode assertion) { - if(options::incrementalSolving()){ - // disabling the d_iteUtilities->simpITE(assertion) pass for incremental solving. - // This is paranoia. We do not actually know of a bug coming from this. - // TODO re-enable + if (!d_iteRemover.containsTermITE(assertion)) { return assertion; - } else if(!d_iteRemover.containsTermITE(assertion)){ - return assertion; - }else{ - + } else { Node result = d_iteUtilities->simpITE(assertion); Node res_rewritten = Rewriter::rewrite(result); @@ -1576,10 +1808,9 @@ Node TheoryEngine::ppSimpITE(TNode assertion) Chat() << "ending simplifyWithCare()" << " post simplifyWithCare()" << postSimpWithCare.getId() << endl; result = Rewriter::rewrite(postSimpWithCare); - }else{ + } else { result = res_rewritten; } - return result; } } @@ -1667,19 +1898,21 @@ bool TheoryEngine::donePPSimpITE(std::vector<Node>& assertions){ return result; } -void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector) -{ +void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector, LemmaProofRecipe* proofRecipe) { Assert(explanationVector.size() > 0); unsigned i = 0; // Index of the current literal we are processing unsigned j = 0; // Index of the last literal we are keeping - while (i < explanationVector.size()) { + std::set<Node> inputAssertions; + PROOF(inputAssertions = proofRecipe->getStep(0)->getAssertions();); + while (i < explanationVector.size()) { // Get the current literal to explain NodeTheoryPair toExplain = explanationVector[i]; - Debug("theory::explain") << "TheoryEngine::explain(): processing [" << toExplain.timestamp << "] " << toExplain.node << " sent from " << toExplain.theory << endl; + Debug("theory::explain") << "[i=" << i << "] TheoryEngine::explain(): processing [" << toExplain.timestamp << "] " << toExplain.node << " sent from " << toExplain.theory << endl; + // If a true constant or a negation of a false constant we can ignore it if (toExplain.node.isConst() && toExplain.node.getConst<bool>()) { @@ -1693,6 +1926,7 @@ void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector // If from the SAT solver, keep it if (toExplain.theory == THEORY_SAT_SOLVER) { + Debug("theory::explain") << "\tLiteral came from THEORY_SAT_SOLVER. Kepping it." << endl; explanationVector[j++] = explanationVector[i++]; continue; } @@ -1711,10 +1945,26 @@ void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector // See if it was sent to the theory by another theory PropagationMap::const_iterator find = d_propagationMap.find(toExplain); if (find != d_propagationMap.end()) { + Debug("theory::explain") << "\tTerm was propagated by another theory (theory = " + << theoryOf((*find).second.theory)->getId() << ")" << std::endl; // There is some propagation, check if its a timely one if ((*find).second.timestamp < toExplain.timestamp) { + Debug("theory::explain") << "\tRelevant timetsamp, pushing " + << (*find).second.node << "to index = " << explanationVector.size() << std::endl; explanationVector.push_back((*find).second); - ++ i; + ++i; + + PROOF({ + if (toExplain.node != (*find).second.node) { + Debug("pf::explain") << "TheoryEngine::getExplanation: Rewrite alert! toAssert = " << toExplain.node + << ", toExplain = " << (*find).second.node << std::endl; + + if (proofRecipe) { + proofRecipe->addRewriteRule(toExplain.node, (*find).second.node); + } + } + }) + continue; } } @@ -1723,21 +1973,61 @@ void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector Node explanation; if (toExplain.theory == THEORY_BUILTIN) { explanation = d_sharedTerms.explain(toExplain.node); + Debug("theory::explain") << "\tTerm was propagated by THEORY_BUILTIN. Explanation: " << explanation << std::endl; } else { explanation = theoryOf(toExplain.theory)->explain(toExplain.node); + Debug("theory::explain") << "\tTerm was propagated by owner theory: " + << theoryOf(toExplain.theory)->getId() + << ". Explanation: " << explanation << std::endl; } + Debug("theory::explain") << "TheoryEngine::explain(): got explanation " << explanation << " got from " << toExplain.theory << endl; Assert(explanation != toExplain.node, "wasn't sent to you, so why are you explaining it trivially"); // Mark the explanation NodeTheoryPair newExplain(explanation, toExplain.theory, toExplain.timestamp); explanationVector.push_back(newExplain); + ++ i; + + PROOF({ + if (proofRecipe) { + // If we're expanding the target node of the explanation (this is the first expansion...), + // we don't want to add it as a separate proof step. It is already part of the assertions. + if (inputAssertions.find(toExplain.node) == inputAssertions.end()) { + LemmaProofRecipe::ProofStep proofStep(toExplain.theory, toExplain.node); + if (explanation.getKind() == kind::AND) { + Node flat = flattenAnd(explanation); + for (unsigned k = 0; k < flat.getNumChildren(); ++ k) { + // If a true constant or a negation of a false constant we can ignore it + if (! ((flat[k].isConst() && flat[k].getConst<bool>()) || + (flat[k].getKind() == kind::NOT && flat[k][0].isConst() && !flat[k][0].getConst<bool>()))) { + proofStep.addAssertion(flat[k].negate()); + } + } + } else { + if (! ((explanation.isConst() && explanation.getConst<bool>()) || + (explanation.getKind() == kind::NOT && explanation[0].isConst() && !explanation[0].getConst<bool>()))) { + proofStep.addAssertion(explanation.negate()); + } + } + proofRecipe->addStep(proofStep); + } + } + }); } // Keep only the relevant literals explanationVector.resize(j); -} + PROOF({ + if (proofRecipe) { + // The remaining literals are the base of the proof + for (unsigned k = 0; k < explanationVector.size(); ++k) { + proofRecipe->addBaseAssertion(explanationVector[k].node.negate()); + } + } + }); +} void TheoryEngine::ppUnconstrainedSimp(vector<Node>& assertions) { diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index db94edd7c..53c4aac77 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -50,6 +50,7 @@ namespace CVC4 { class ResourceManager; +class LemmaProofRecipe; /** * A pair of a theory and a node. This is used to mark the flow of @@ -270,46 +271,18 @@ class TheoryEngine { } } - void conflict(TNode conflictNode, Proof* pf = NULL) throw(AssertionException, UnsafeInterruptException) { - Trace("theory::conflict") << "EngineOutputChannel<" << d_theory << ">::conflict(" << conflictNode << ")" << std::endl; - Assert(pf == NULL); // theory shouldn't be producing proofs yet - ++ d_statistics.conflicts; - d_engine->d_outputChannelUsed = true; - d_engine->conflict(conflictNode, d_theory); - } + void conflict(TNode conflictNode, Proof* pf = NULL) throw(AssertionException, UnsafeInterruptException); - bool propagate(TNode literal) throw(AssertionException, UnsafeInterruptException) { - Trace("theory::propagate") << "EngineOutputChannel<" << d_theory << ">::propagate(" << literal << ")" << std::endl; - ++ d_statistics.propagations; - d_engine->d_outputChannelUsed = true; - return d_engine->propagate(literal, d_theory); - } + bool propagate(TNode literal) throw(AssertionException, UnsafeInterruptException); theory::LemmaStatus lemma(TNode lemma, ProofRule rule, bool removable = false, bool preprocess = false, bool sendAtoms = false) - throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException) { - Trace("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::lemma(" << lemma << ")" << std::endl; - ++ d_statistics.lemmas; - d_engine->d_outputChannelUsed = true; - return d_engine->lemma(lemma, rule, false, removable, preprocess, sendAtoms ? d_theory : theory::THEORY_LAST, d_theory); - } + throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException); - /*theory::LemmaStatus preservedLemma(TNode lemma, bool removable = false, bool preprocess = false) throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException, LogicException) { - Trace("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::preservedLemma(" << lemma << ")" << std::endl; - ++ d_statistics.lemmas; - d_engine->d_outputChannelUsed = true; - return d_engine->lemma(lemma, false, removable, preprocess, d_theory); - }*/ - - theory::LemmaStatus splitLemma(TNode lemma, bool removable = false) throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException) { - Trace("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::splitLemma(" << lemma << ")" << std::endl; - ++ d_statistics.lemmas; - d_engine->d_outputChannelUsed = true; - return d_engine->lemma(lemma, RULE_SPLIT, false, removable, false, d_theory, d_theory); - } + theory::LemmaStatus splitLemma(TNode lemma, bool removable = false) throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException); void demandRestart() throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException) { NodeManager* curr = NodeManager::currentNM(); @@ -456,7 +429,7 @@ class TheoryEngine { bool removable, bool preprocess, theory::TheoryId atomsTo, - theory::TheoryId ownerTheory); + LemmaProofRecipe* proofRecipe); /** Enusre that the given atoms are send to the given theory */ void ensureLemmaAtoms(const std::vector<TNode>& atoms, theory::TheoryId theory); @@ -606,9 +579,10 @@ private: * asking relevant theories to explain the propagations. Initially * the explanation vector should contain only the element (node, theory) * where the node is the one to be explained, and the theory is the - * theory that sent the literal. + * theory that sent the literal. The lemmaProofRecipe will contain a list + * of the explanation steps required to produce the original node. */ - void getExplanation(std::vector<NodeTheoryPair>& explanationVector); + void getExplanation(std::vector<NodeTheoryPair>& explanationVector, LemmaProofRecipe* lemmaProofRecipe); public: @@ -730,7 +704,7 @@ public: * Returns an explanation of the node propagated to the SAT solver and the theory * that propagated it. */ - NodeTheoryPair getExplanationAndExplainer(TNode node); + Node getExplanationAndRecipe(TNode node, LemmaProofRecipe* proofRecipe); /** * collect model info diff --git a/src/theory/theory_model.cpp b/src/theory/theory_model.cpp index fa7e497e2..f43a2aa7f 100644 --- a/src/theory/theory_model.cpp +++ b/src/theory/theory_model.cpp @@ -561,7 +561,7 @@ void TheoryEngineModelBuilder::buildModel(Model* m, bool fullModel) TheoryModel* tm = (TheoryModel*)m; // buildModel with fullModel = true should only be called once in any context - Assert(!tm->d_modelBuilt); + Assert(!tm->isBuilt()); tm->d_modelBuilt = fullModel; // Reset model @@ -832,6 +832,7 @@ void TheoryEngineModelBuilder::buildModel(Model* m, bool fullModel) Assert(!t.isBoolean() || (*i2).getKind() == kind::APPLY_UF); Node n; if (t.getCardinality().isInfinite()) { + // if (!t.isInterpretedFinite()) { bool success; do{ Trace("model-builder-debug") << "Enumerate term of type " << t << std::endl; diff --git a/src/theory/theory_model.h b/src/theory/theory_model.h index 6e4f77336..833b124eb 100644 --- a/src/theory/theory_model.h +++ b/src/theory/theory_model.h @@ -36,6 +36,7 @@ class TheoryModel : public Model protected: /** substitution map for this model */ SubstitutionMap d_substitutions; + context::CDO<bool> d_modelBuilt; public: TheoryModel(context::Context* c, std::string name, bool enableFuncModels); virtual ~TheoryModel() throw(); @@ -51,7 +52,6 @@ public: /** true/false nodes */ Node d_true; Node d_false; - context::CDO<bool> d_modelBuilt; mutable std::hash_map<Node, Node, NodeHashFunction> d_modelCache; protected: @@ -62,6 +62,8 @@ protected: */ Node getModelValue(TNode n, bool hasBoundVars = false, bool useDontCares = false) const; public: + /** is built */ + bool isBuilt() { return d_modelBuilt.get(); } /** * Get value function. This should be called only after a ModelBuilder has called buildModel(...) * on this model. diff --git a/src/theory/type_enumerator.h b/src/theory/type_enumerator.h index d1318aaa8..bcd7e695f 100644 --- a/src/theory/type_enumerator.h +++ b/src/theory/type_enumerator.h @@ -115,7 +115,7 @@ public: // On Mac clang, there appears to be a code generation bug in an exception // block here. For now, there doesn't appear a good workaround; just disable // assertions on that setup. -#if defined(CVC4_ASSERTIONS) && !(defined(__APPLE__) && defined(__clang__)) +#if defined(CVC4_ASSERTIONS) && !(defined(__clang__)) if(d_te->isFinished()) { try { **d_te; diff --git a/src/theory/uf/equality_engine.cpp b/src/theory/uf/equality_engine.cpp index 9b429765e..25b12f75f 100644 --- a/src/theory/uf/equality_engine.cpp +++ b/src/theory/uf/equality_engine.cpp @@ -964,12 +964,9 @@ void EqualityEngine::explainEquality(TNode t1, TNode t2, bool polarity, std::vec std::vector<EqProof *> orderedChildren; bool nullCongruenceFound = false; for (unsigned i = 0; i < eqpc->d_children.size(); ++i) { - if (eqpc->d_children[i]->d_id==eq::MERGED_THROUGH_CONGRUENCE && eqpc->d_children[i]->d_node.isNull()) { - - // For now, assume there can only be one null congruence child - Assert(!nullCongruenceFound); + if (eqpc->d_children[i]->d_id==eq::MERGED_THROUGH_CONGRUENCE && + eqpc->d_children[i]->d_node.isNull()) { nullCongruenceFound = true; - Debug("pf::ee") << "Have congruence with empty d_node. Splitting..." << std::endl; orderedChildren.insert(orderedChildren.begin(), eqpc->d_children[i]->d_children[0]); orderedChildren.push_back(eqpc->d_children[i]->d_children[1]); @@ -1192,6 +1189,9 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st getExplanation(childId, getEqualityNode(childId).getFind(), equalities, eqpcc); if( eqpc ) { eqpc->d_children.push_back( eqpcc ); + + Debug("pf::ee") << "MERGED_THROUGH_CONSTANTS. Dumping the child proof" << std::endl; + eqpc->debug_print("pf::ee", 1); } } @@ -1605,6 +1605,7 @@ void EqualityEngine::propagate() { } void EqualityEngine::debugPrintGraph() const { + Debug("equality::graph") << std::endl << "Dumping graph" << std::endl; for (EqualityNodeId nodeId = 0; nodeId < d_nodes.size(); ++ nodeId) { Debug("equality::graph") << d_nodes[nodeId] << " " << nodeId << "(" << getEqualityNode(nodeId).getFind() << "):"; @@ -1618,6 +1619,7 @@ void EqualityEngine::debugPrintGraph() const { Debug("equality::graph") << std::endl; } + Debug("equality::graph") << std::endl; } bool EqualityEngine::areEqual(TNode t1, TNode t2) const { @@ -2209,9 +2211,15 @@ bool EqClassIterator::isFinished() const { return d_current == null_id; } -void EqProof::debug_print( const char * c, unsigned tb ) const{ - for( unsigned i=0; i<tb; i++ ) { Debug( c ) << " "; } - Debug( c ) << d_id << "("; +void EqProof::debug_print(const char* c, unsigned tb, PrettyPrinter* prettyPrinter) const { + for(unsigned i=0; i<tb; i++) { Debug( c ) << " "; } + + if (prettyPrinter) + Debug( c ) << prettyPrinter->printTag(d_id); + else + Debug( c ) << d_id; + + Debug( c ) << "("; if( !d_children.empty() || !d_node.isNull() ){ if( !d_node.isNull() ){ Debug( c ) << std::endl; @@ -2221,7 +2229,7 @@ void EqProof::debug_print( const char * c, unsigned tb ) const{ for( unsigned i=0; i<d_children.size(); i++ ){ if( i>0 || !d_node.isNull() ) Debug( c ) << ","; Debug( c ) << std::endl; - d_children[i]->debug_print( c, tb+1 ); + d_children[i]->debug_print( c, tb+1, prettyPrinter ); } } Debug( c ) << ")" << std::endl; diff --git a/src/theory/uf/equality_engine.h b/src/theory/uf/equality_engine.h index f30f1e8a0..843e7ce7f 100644 --- a/src/theory/uf/equality_engine.h +++ b/src/theory/uf/equality_engine.h @@ -902,11 +902,17 @@ public: class EqProof { public: + class PrettyPrinter { + public: + virtual ~PrettyPrinter() {} + virtual std::string printTag(unsigned tag) = 0; + }; + EqProof() : d_id(MERGED_THROUGH_REFLEXIVITY){} unsigned d_id; Node d_node; std::vector< EqProof * > d_children; - void debug_print( const char * c, unsigned tb = 0 ) const; + void debug_print(const char * c, unsigned tb = 0, PrettyPrinter* prettyPrinter = NULL) const; };/* class EqProof */ } // Namespace eq diff --git a/src/theory/uf/theory_uf.cpp b/src/theory/uf/theory_uf.cpp index 0c7bed773..ae935798e 100644 --- a/src/theory/uf/theory_uf.cpp +++ b/src/theory/uf/theory_uf.cpp @@ -26,6 +26,8 @@ #include "theory/theory_model.h" #include "theory/type_enumerator.h" #include "theory/uf/theory_uf_strong_solver.h" +#include "theory/quantifiers/term_database.h" +#include "options/theory_options.h" using namespace std; @@ -431,100 +433,107 @@ void TheoryUF::addSharedTerm(TNode t) { d_equalityEngine.addTriggerTerm(t, THEORY_UF); } -void TheoryUF::computeCareGraph() { - - if (d_sharedTerms.size() > 0) { - - vector< pair<TNode, TNode> > currentPairs; - - // Go through the function terms and see if there are any to split on - unsigned functionTerms = d_functionsTerms.size(); - for (unsigned i = 0; i < functionTerms; ++ i) { - - TNode f1 = d_functionsTerms[i]; - Node op = f1.getOperator(); - - for (unsigned j = i + 1; j < functionTerms; ++ j) { - - TNode f2 = d_functionsTerms[j]; - - // If the operators are not the same, we can skip this pair - if (f2.getOperator() != op) { - continue; - } - +//TODO: move quantifiers::TermArgTrie to src/theory/ +void TheoryUF::addCarePairs( quantifiers::TermArgTrie * t1, quantifiers::TermArgTrie * t2, unsigned arity, unsigned depth ){ + if( depth==arity ){ + if( t2!=NULL ){ + Node f1 = t1->getNodeData(); + Node f2 = t2->getNodeData(); + if( !d_equalityEngine.areEqual( f1, f2 ) ){ Debug("uf::sharing") << "TheoryUf::computeCareGraph(): checking function " << f1 << " and " << f2 << std::endl; - - // If the terms are already known to be equal, we are also in good shape - if (d_equalityEngine.areEqual(f1, f2)) { - Debug("uf::sharing") << "TheoryUf::computeCareGraph(): equal, skipping" << std::endl; - continue; - } - - // We have two functions f(x1, ..., xn) and f(y1, ..., yn) no known to be equal - // We split on the argument pairs that are are not known, unless there is some - // argument pair that is already dis-equal. - bool somePairIsDisequal = false; - currentPairs.clear(); + vector< pair<TNode, TNode> > currentPairs; for (unsigned k = 0; k < f1.getNumChildren(); ++ k) { - TNode x = f1[k]; TNode y = f2[k]; - - Debug("uf::sharing") << "TheoryUf::computeCareGraph(): checking arguments " << x << " and " << y << std::endl; - - if (d_equalityEngine.areDisequal(x, y, false)) { - // Mark that there is a dis-equal pair and break - somePairIsDisequal = true; - Debug("uf::sharing") << "TheoryUf::computeCareGraph(): disequal, disregarding all" << std::endl; - break; + Assert( d_equalityEngine.hasTerm(x) ); + Assert( d_equalityEngine.hasTerm(y) ); + Assert( !d_equalityEngine.areDisequal( x, y, false ) ); + if( !d_equalityEngine.areEqual( x, y ) ){ + if( d_equalityEngine.isTriggerTerm(x, THEORY_UF) && d_equalityEngine.isTriggerTerm(y, THEORY_UF) ){ + TNode x_shared = d_equalityEngine.getTriggerTermRepresentative(x, THEORY_UF); + TNode y_shared = d_equalityEngine.getTriggerTermRepresentative(y, THEORY_UF); + EqualityStatus eqStatus = d_valuation.getEqualityStatus(x_shared, y_shared); + if( eqStatus==EQUALITY_FALSE_AND_PROPAGATED || eqStatus==EQUALITY_FALSE || eqStatus==EQUALITY_FALSE_IN_MODEL ){ + //an argument is disequal, we are done + return; + }else{ + currentPairs.push_back(make_pair(x_shared, y_shared)); + } + } } - - if (d_equalityEngine.areEqual(x, y)) { - // We don't need this one - Debug("uf::sharing") << "TheoryUf::computeCareGraph(): equal" << std::endl; - continue; - } - - if (!d_equalityEngine.isTriggerTerm(x, THEORY_UF) || !d_equalityEngine.isTriggerTerm(y, THEORY_UF)) { - // Not connected to shared terms, we don't care - continue; + } + for (unsigned c = 0; c < currentPairs.size(); ++ c) { + Debug("uf::sharing") << "TheoryUf::computeCareGraph(): adding to care-graph" << std::endl; + addCarePair(currentPairs[c].first, currentPairs[c].second); + } + } + } + }else{ + if( t2==NULL ){ + if( depth<(arity-1) ){ + //add care pairs internal to each child + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + addCarePairs( &it->second, NULL, arity, depth+1 ); + } + } + //add care pairs based on each pair of non-disequal arguments + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + std::map< TNode, quantifiers::TermArgTrie >::iterator it2 = it; + ++it2; + for( ; it2 != t1->d_data.end(); ++it2 ){ + if( !d_equalityEngine.areDisequal(it->first, it2->first, false) ){ + addCarePairs( &it->second, &it2->second, arity, depth+1 ); } - - // Get representative trigger terms - TNode x_shared = d_equalityEngine.getTriggerTermRepresentative(x, THEORY_UF); - TNode y_shared = d_equalityEngine.getTriggerTermRepresentative(y, THEORY_UF); - - EqualityStatus eqStatusDomain = d_valuation.getEqualityStatus(x_shared, y_shared); - switch (eqStatusDomain) { - case EQUALITY_FALSE_AND_PROPAGATED: - case EQUALITY_FALSE: - case EQUALITY_FALSE_IN_MODEL: - somePairIsDisequal = true; - continue; - break; - default: - break; - // nothing + } + } + }else{ + //add care pairs based on product of indices, non-disequal arguments + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it2 = t2->d_data.begin(); it2 != t2->d_data.end(); ++it2 ){ + if( !d_equalityEngine.areDisequal(it->first, it2->first, false) ){ + addCarePairs( &it->second, &it2->second, arity, depth+1 ); } - - // Otherwise, we need to figure it out - Debug("uf::sharing") << "TheoryUf::computeCareGraph(): adding to care-graph" << std::endl; - currentPairs.push_back(make_pair(x_shared, y_shared)); } + } + } + } +} - if (!somePairIsDisequal) { - for (unsigned i = 0; i < currentPairs.size(); ++ i) { - addCarePair(currentPairs[i].first, currentPairs[i].second); - } +void TheoryUF::computeCareGraph() { + + if (d_sharedTerms.size() > 0) { + //use term indexing + Debug("uf::sharing") << "TheoryUf::computeCareGraph(): Build term indices..." << std::endl; + std::map< Node, quantifiers::TermArgTrie > index; + std::map< Node, unsigned > arity; + unsigned functionTerms = d_functionsTerms.size(); + for (unsigned i = 0; i < functionTerms; ++ i) { + TNode f1 = d_functionsTerms[i]; + Node op = f1.getOperator(); + std::vector< TNode > reps; + bool has_trigger_arg = false; + for( unsigned j=0; j<f1.getNumChildren(); j++ ){ + reps.push_back( d_equalityEngine.getRepresentative( f1[j] ) ); + if( d_equalityEngine.isTriggerTerm( f1[j], THEORY_UF ) ){ + has_trigger_arg = true; } } + if( has_trigger_arg ){ + index[op].addTerm( f1, reps ); + arity[op] = reps.size(); + } + } + //for each index + for( std::map< Node, quantifiers::TermArgTrie >::iterator itii = index.begin(); itii != index.end(); ++itii ){ + Debug("uf::sharing") << "TheoryUf::computeCareGraph(): Process index " << itii->first << "..." << std::endl; + addCarePairs( &itii->second, NULL, arity[ itii->first ], 0 ); } } }/* TheoryUF::computeCareGraph() */ void TheoryUF::conflict(TNode a, TNode b) { eq::EqProof* pf = d_proofsEnabled ? new eq::EqProof() : NULL; + if (a.getKind() == kind::CONST_BOOLEAN) { d_conflictNode = explain(a.iffNode(b),pf); } else { @@ -542,9 +551,9 @@ void TheoryUF::eqNotifyNewClass(TNode t) { } void TheoryUF::eqNotifyPreMerge(TNode t1, TNode t2) { - if (getLogicInfo().isQuantified()) { + //if (getLogicInfo().isQuantified()) { //getQuantifiersEngine()->getEfficientEMatcher()->merge( t1, t2 ); - } + //} } void TheoryUF::eqNotifyPostMerge(TNode t1, TNode t2) { diff --git a/src/theory/uf/theory_uf.h b/src/theory/uf/theory_uf.h index 42a804c09..3a83decec 100644 --- a/src/theory/uf/theory_uf.h +++ b/src/theory/uf/theory_uf.h @@ -32,6 +32,11 @@ namespace CVC4 { namespace theory { + +namespace quantifiers{ + class TermArgTrie; +} + namespace uf { class UfTermDb; @@ -204,6 +209,8 @@ public: StrongSolverTheoryUF* getStrongSolver() { return d_thss; } +private: + void addCarePairs( quantifiers::TermArgTrie * t1, quantifiers::TermArgTrie * t2, unsigned arity, unsigned depth ); };/* class TheoryUF */ }/* CVC4::theory::uf namespace */ diff --git a/src/theory/uf/theory_uf_strong_solver.cpp b/src/theory/uf/theory_uf_strong_solver.cpp index ed28cc2fc..cda94e1c4 100644 --- a/src/theory/uf/theory_uf_strong_solver.cpp +++ b/src/theory/uf/theory_uf_strong_solver.cpp @@ -1670,6 +1670,12 @@ StrongSolverTheoryUF::StrongSolverTheoryUF(context::Context* c, } } +StrongSolverTheoryUF::~StrongSolverTheoryUF() { + for( std::map< TypeNode, SortModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){ + delete it->second; + } +} + SortInference* StrongSolverTheoryUF::getSortInference() { return d_th->getQuantifiersEngine()->getTheoryEngine()->getSortInference(); } diff --git a/src/theory/uf/theory_uf_strong_solver.h b/src/theory/uf/theory_uf_strong_solver.h index 11f0664f3..4e4dbef83 100644 --- a/src/theory/uf/theory_uf_strong_solver.h +++ b/src/theory/uf/theory_uf_strong_solver.h @@ -414,7 +414,7 @@ private: SubsortSymmetryBreaker* d_sym_break; public: StrongSolverTheoryUF(context::Context* c, context::UserContext* u, OutputChannel& out, TheoryUF* th); - ~StrongSolverTheoryUF() {} + ~StrongSolverTheoryUF(); /** get theory */ TheoryUF* getTheory() { return d_th; } /** disequality propagator */ diff --git a/src/theory/valuation.cpp b/src/theory/valuation.cpp index 165937c13..7e13668cd 100644 --- a/src/theory/valuation.cpp +++ b/src/theory/valuation.cpp @@ -87,6 +87,10 @@ Node Valuation::getModelValue(TNode var) { return d_engine->getModelValue(var); } +TheoryModel* Valuation::getModel() { + return d_engine->getModel(); +} + Node Valuation::ensureLiteral(TNode n) { return d_engine->ensureLiteral(n); } diff --git a/src/theory/valuation.h b/src/theory/valuation.h index 4ecdecad0..54af14fdd 100644 --- a/src/theory/valuation.h +++ b/src/theory/valuation.h @@ -32,6 +32,7 @@ namespace theory { class EntailmentCheckParameters; class EntailmentCheckSideEffects; +class TheoryModel; /** * The status of an equality in the current context. @@ -106,6 +107,11 @@ public: Node getModelValue(TNode var); /** + * Returns pointer to model. + */ + TheoryModel* getModel(); + + /** * Ensure that the given node will have a designated SAT literal * that is definitionally equal to it. The result of this function * is a Node that can be queried via getSatValue(). |