diff options
author | PaulMeng <baolmeng@gmail.com> | 2016-08-24 11:24:57 -0400 |
---|---|---|
committer | PaulMeng <baolmeng@gmail.com> | 2016-08-24 11:24:57 -0400 |
commit | dc3f45d6e41bdd4e52e39b0c6fecae3148a2944c (patch) | |
tree | 7516d3d5d43f0dadb943bb186615e0903cbd9f7f /src | |
parent | 74bf9ff63f5566fbe1b5db9124f9dc1fde65cb82 (diff) | |
parent | 6b355496aaf27d46d6a33402814753589b755842 (diff) |
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'src')
83 files changed, 3602 insertions, 1967 deletions
diff --git a/src/expr/expr_manager_template.cpp b/src/expr/expr_manager_template.cpp index 53e16751e..2dc3aebe5 100644 --- a/src/expr/expr_manager_template.cpp +++ b/src/expr/expr_manager_template.cpp @@ -75,7 +75,7 @@ ExprManager::ExprManager() : for (unsigned i = 0; i < kind::LAST_KIND; ++ i) { d_exprStatistics[i] = NULL; } - for (unsigned i = 0; i < LAST_TYPE; ++ i) { + for (unsigned i = 0; i <= LAST_TYPE; ++ i) { d_exprStatisticsVars[i] = NULL; } #endif @@ -84,7 +84,7 @@ ExprManager::ExprManager() : ExprManager::ExprManager(const Options& options) : d_nodeManager(new NodeManager(this, options)) { #ifdef CVC4_STATISTICS_ON - for (unsigned i = 0; i < LAST_TYPE; ++ i) { + for (unsigned i = 0; i <= LAST_TYPE; ++ i) { d_exprStatisticsVars[i] = NULL; } for (unsigned i = 0; i < kind::LAST_KIND; ++ i) { @@ -106,7 +106,7 @@ ExprManager::~ExprManager() throw() { d_exprStatistics[i] = NULL; } } - for (unsigned i = 0; i < LAST_TYPE; ++ i) { + for (unsigned i = 0; i <= LAST_TYPE; ++ i) { if (d_exprStatisticsVars[i] != NULL) { d_nodeManager->getStatisticsRegistry()->unregisterStat(d_exprStatisticsVars[i]); delete d_exprStatisticsVars[i]; diff --git a/src/expr/expr_manager_template.h b/src/expr/expr_manager_template.h index 31c911736..f30b720de 100644 --- a/src/expr/expr_manager_template.h +++ b/src/expr/expr_manager_template.h @@ -57,7 +57,7 @@ private: NodeManager* d_nodeManager; /** Counts of expressions and variables created of a given kind */ - IntStat* d_exprStatisticsVars[LAST_TYPE]; + IntStat* d_exprStatisticsVars[LAST_TYPE + 1]; IntStat* d_exprStatistics[kind::LAST_KIND]; /** diff --git a/src/options/options_handler.cpp b/src/options/options_handler.cpp index 6a5f6cd39..8cd651da5 100644 --- a/src/options/options_handler.cpp +++ b/src/options/options_handler.cpp @@ -369,6 +369,19 @@ min-s-all \n\ + Consider only minimal subterms that meet criteria for single triggers, all otherwise. \n\ \n\ "; +const std::string OptionsHandler::s_triggerActiveSelModeHelp = "\ +Trigger active selection modes currently supported by the --trigger-sel option:\n\ +\n\ +all \n\ ++ Make all triggers active. \n\ +\n\ +min \n\ ++ Activate triggers with minimal ground terms.\n\ +\n\ +max \n\ ++ Activate triggers with maximal ground terms. \n\ +\n\ +"; const std::string OptionsHandler::s_prenexQuantModeHelp = "\ Prenex quantifiers modes currently supported by the --prenex-quant option:\n\ \n\ @@ -630,9 +643,7 @@ theory::quantifiers::UserPatMode OptionsHandler::stringToUserPatMode(std::string } theory::quantifiers::TriggerSelMode OptionsHandler::stringToTriggerSelMode(std::string option, std::string optarg) throw(OptionException) { - if(optarg == "default") { - return theory::quantifiers::TRIGGER_SEL_DEFAULT; - } else if(optarg == "min") { + if(optarg == "default" || optarg == "min") { return theory::quantifiers::TRIGGER_SEL_MIN; } else if(optarg == "max") { return theory::quantifiers::TRIGGER_SEL_MAX; @@ -651,6 +662,21 @@ theory::quantifiers::TriggerSelMode OptionsHandler::stringToTriggerSelMode(std:: } } +theory::quantifiers::TriggerActiveSelMode OptionsHandler::stringToTriggerActiveSelMode(std::string option, std::string optarg) throw(OptionException) { + if(optarg == "default" || optarg == "all") { + return theory::quantifiers::TRIGGER_ACTIVE_SEL_ALL; + } else if(optarg == "min") { + return theory::quantifiers::TRIGGER_ACTIVE_SEL_MIN; + } else if(optarg == "max") { + return theory::quantifiers::TRIGGER_ACTIVE_SEL_MAX; + } else if(optarg == "help") { + puts(s_triggerActiveSelModeHelp.c_str()); + exit(1); + } else { + throw OptionException(std::string("unknown option for --trigger-active-sel: `") + + optarg + "'. Try --trigger-active-sel help."); + } +} theory::quantifiers::PrenexQuantMode OptionsHandler::stringToPrenexQuantMode(std::string option, std::string optarg) throw(OptionException) { if(optarg == "default" ) { diff --git a/src/options/options_handler.h b/src/options/options_handler.h index 5db2887c0..e327b9c8e 100644 --- a/src/options/options_handler.h +++ b/src/options/options_handler.h @@ -94,6 +94,7 @@ public: theory::quantifiers::QcfMode stringToQcfMode(std::string option, std::string optarg) throw(OptionException); theory::quantifiers::UserPatMode stringToUserPatMode(std::string option, std::string optarg) throw(OptionException); theory::quantifiers::TriggerSelMode stringToTriggerSelMode(std::string option, std::string optarg) throw(OptionException); + theory::quantifiers::TriggerActiveSelMode stringToTriggerActiveSelMode(std::string option, std::string optarg) throw(OptionException); theory::quantifiers::PrenexQuantMode stringToPrenexQuantMode(std::string option, std::string optarg) throw(OptionException); theory::quantifiers::CegqiFairMode stringToCegqiFairMode(std::string option, std::string optarg) throw(OptionException); theory::quantifiers::TermDbMode stringToTermDbMode(std::string option, std::string optarg) throw(OptionException); @@ -221,6 +222,7 @@ public: static const std::string s_termDbModeHelp; static const std::string s_theoryOfModeHelp; static const std::string s_triggerSelModeHelp; + static const std::string s_triggerActiveSelModeHelp; static const std::string s_ufssModeHelp; static const std::string s_userPatModeHelp; static const std::string s_errorSelectionRulesHelp; diff --git a/src/options/proof_options b/src/options/proof_options index a99d858bc..322ef5a9c 100644 --- a/src/options/proof_options +++ b/src/options/proof_options @@ -8,4 +8,10 @@ module PROOF "options/proof_options.h" Proof option lfscLetification --lfsc-letification bool :default true turns on global letification in LFSC proofs +option aggressiveCoreMin --aggressive-core-min bool :default false + turns on aggressive unsat core minimization (experimental) + +option fewerPreprocessingHoles --fewer-preprocessing-holes bool :default false :read-write + try to eliminate preprocessing holes in proofs + endmodule diff --git a/src/options/quantifiers_modes.h b/src/options/quantifiers_modes.h index 65445be17..fe76f8798 100644 --- a/src/options/quantifiers_modes.h +++ b/src/options/quantifiers_modes.h @@ -101,8 +101,6 @@ enum UserPatMode { }; enum TriggerSelMode { - /** default for trigger selection */ - TRIGGER_SEL_DEFAULT, /** only consider minimal terms for triggers */ TRIGGER_SEL_MIN, /** only consider maximal terms for triggers */ @@ -115,6 +113,15 @@ enum TriggerSelMode { TRIGGER_SEL_ALL, }; +enum TriggerActiveSelMode { + /** always use all triggers */ + TRIGGER_ACTIVE_SEL_ALL, + /** only use triggers with minimal # of ground terms */ + TRIGGER_ACTIVE_SEL_MIN, + /** only use triggers with maximal # of ground terms */ + TRIGGER_ACTIVE_SEL_MAX, +}; + enum CVC4_PUBLIC PrenexQuantMode { /** default : prenex quantifiers without user patterns */ PRENEX_NO_USER_PAT, diff --git a/src/options/quantifiers_options b/src/options/quantifiers_options index 4d228bbad..7536d385d 100644 --- a/src/options/quantifiers_options +++ b/src/options/quantifiers_options @@ -26,6 +26,8 @@ option prenexQuant --prenex-quant=MODE CVC4::theory::quantifiers::PrenexQuantMod # forall y. P( c, y ) option varElimQuant --var-elim-quant bool :default true enable simple variable elimination for quantified formulas +option varIneqElimQuant --var-ineq-elim-quant bool :default true + enable variable elimination based on infinite projection of unbound arithmetic variables option dtVarExpandQuant --dt-var-exp-quant bool :default true expand datatype variables bound to one constructor in quantifiers #ite lift mode for quantified formulas @@ -50,8 +52,6 @@ option aggressiveMiniscopeQuant --ag-miniscope-quant bool :default false perform aggressive miniscoping for quantifiers option elimTautQuant --elim-taut-quant bool :default true eliminate tautological disjuncts of quantified formulas -option purifyQuant --purify-quant bool :default false - purify quantified formulas option elimExtArithQuant --elim-ext-arith-quant bool :default true eliminate extended arithmetic symbols in quantified formulas option condRewriteQuant --cond-rewrite-quant bool :default true @@ -89,8 +89,10 @@ option multiTriggerWhenSingle --multi-trigger-when-single bool :default false select multi triggers when single triggers exist option multiTriggerPriority --multi-trigger-priority bool :default false only try multi triggers if single triggers give no instantiations -option triggerSelMode --trigger-sel CVC4::theory::quantifiers::TriggerSelMode :default CVC4::theory::quantifiers::TRIGGER_SEL_DEFAULT :read-write :include "options/quantifiers_modes.h" :handler stringToTriggerSelMode +option triggerSelMode --trigger-sel CVC4::theory::quantifiers::TriggerSelMode :default CVC4::theory::quantifiers::TRIGGER_SEL_MIN :read-write :include "options/quantifiers_modes.h" :handler stringToTriggerSelMode selection mode for triggers +option triggerActiveSelMode --trigger-active-sel CVC4::theory::quantifiers::TriggerActiveSelMode :default CVC4::theory::quantifiers::TRIGGER_ACTIVE_SEL_ALL :read-write :include "options/quantifiers_modes.h" :handler stringToTriggerActiveSelMode + selection mode to activate triggers option userPatternsQuant --user-pat=MODE CVC4::theory::quantifiers::UserPatMode :default CVC4::theory::quantifiers::USER_PAT_MODE_TRUST :read-write :include "options/quantifiers_modes.h" :handler stringToUserPatMode policy for handling user-provided patterns for quantifier instantiation option incrementTriggers --increment-triggers bool :default true @@ -160,8 +162,10 @@ option fmfFmcSimple --fmf-fmc-simple bool :default true simple models in full model check for finite model finding option fmfBoundInt fmf-bound-int --fmf-bound-int bool :default false :read-write finite model finding on bounded integer quantification -option fmfBoundIntLazy --fmf-bound-int-lazy bool :default false :read-write - enforce bounds for bounded integer quantification lazily via use of proxy variables +option fmfBound fmf-bound --fmf-bound bool :default false :read-write + finite model finding on bounded quantification +option fmfBoundLazy --fmf-bound-lazy bool :default false :read-write + enforce bounds for bounded quantification lazily via use of proxy variables ### conflict-based instantiation options @@ -239,6 +243,10 @@ option cegqiSingleInvPartial --cegqi-si-partial bool :default false combined techniques for synthesis conjectures that are partially single invocation option cegqiSingleInvReconstruct --cegqi-si-reconstruct bool :default true reconstruct solutions for single invocation conjectures in original grammar +option cegqiSolMinCore --cegqi-si-sol-min-core bool :default false + minimize solutions for single invocation conjectures based on unsat core +option cegqiSolMinInst --cegqi-si-sol-min-inst bool :default true + minimize individual instantiations for single invocation conjectures based on unsat core option cegqiSingleInvReconstructConst --cegqi-si-reconstruct-const bool :default true include constants when reconstruct solutions for single invocation conjectures in original grammar option cegqiSingleInvAbort --cegqi-si-abort bool :default false @@ -325,4 +333,9 @@ option quantAntiSkolem --quant-anti-skolem bool :read-write :default false option quantEqualityEngine --quant-ee bool :default false maintain congrunce closure over universal equalities +### proof options + +option trackInstLemmas --track-inst-lemmas bool :default true + when proof is enabled, track instantiation lemmas (for proofs, unsat cores, qe and synthesis minimization) + endmodule diff --git a/src/options/strings_options b/src/options/strings_options index 5c991a1bb..6dd7030a1 100644 --- a/src/options/strings_options +++ b/src/options/strings_options @@ -38,9 +38,7 @@ option stringIgnNegMembership strings-inm --strings-inm bool :default false # the cardinality of the characters used by the theory of strings, default 128 (for standard ASCII) or 256 (for extended ASCII) option stringLazyPreproc strings-lazy-pp --strings-lazy-pp bool :default true - perform string preprocessing lazily upon assertion -option stringLazyPreproc2 strings-lazy-pp2 --strings-lazy-pp2 bool :default true - perform string preprocessing lazily upon failure to reduce + perform string preprocessing lazily option stringLenGeqZ strings-len-geqz --strings-len-geqz bool :default false strings length greater than zero lemmas @@ -65,6 +63,14 @@ option stringRExplainLemmas strings-rexplain-lemmas --strings-rexplain-lemmas bo regression explanations for string lemmas option stringMinPrefixExplain strings-min-prefix-explain --strings-min-prefix-explain bool :default true minimize explanations for prefix of normal forms in strings +option stringGuessModel strings-guess-model --strings-guess-model bool :default false + use model guessing to avoid string extended function reductions +option stringUfReduct strings-uf-reduct --strings-uf-reduct bool :default false + use uninterpreted functions when applying extended function reductions +option stringBinaryCsp strings-binary-csp --strings-binary-csp bool :default false + use binary search when splitting strings +option stringLenPropCsp strings-lprop-csp --strings-lprop-csp bool :default false + do length propagation based on constant splits endmodule diff --git a/src/printer/cvc/cvc_printer.cpp b/src/printer/cvc/cvc_printer.cpp index 8bd222ca7..84c8eecf5 100644 --- a/src/printer/cvc/cvc_printer.cpp +++ b/src/printer/cvc/cvc_printer.cpp @@ -726,7 +726,7 @@ void CvcPrinter::toStream(std::ostream& out, TNode n, int depth, bool types, boo case kind::BITVECTOR_SIGN_EXTEND: out << "SX("; toStream(out, n[0], depth, types, false); - out << ", " << n.getOperator().getConst<BitVectorSignExtend>() << ')'; + out << ", " << BitVectorType(n.getType().toType()).getSize() << ')'; return; break; case kind::BITVECTOR_ROTATE_LEFT: diff --git a/src/printer/smt2/smt2_printer.cpp b/src/printer/smt2/smt2_printer.cpp index 4006a9e08..c9dc814f2 100644 --- a/src/printer/smt2/smt2_printer.cpp +++ b/src/printer/smt2/smt2_printer.cpp @@ -1069,6 +1069,23 @@ void Smt2Printer::toStream(std::ostream& out, const UnsatCore& core, const std:: void Smt2Printer::toStream(std::ostream& out, const Model& m) const throw() { + //print the model comments + std::stringstream c; + m.getComments( c ); + std::string ln; + while( std::getline( c, ln ) ){ + out << "; " << ln << std::endl; + } + //print the heap model, if it exists + Expr h, neq; + if( m.getHeapModel( h, neq ) ){ + // description of the heap+what nil is equal to fully describes model + out << "(heap" << endl; + out << h << endl; + out << neq << endl; + out << ")" << std::endl; + } + //print the model out << "(model" << endl; this->Printer::toStream(out, m); out << ")" << endl; diff --git a/src/proof/arith_proof.cpp b/src/proof/arith_proof.cpp index 4864cbf46..4e813d646 100644 --- a/src/proof/arith_proof.cpp +++ b/src/proof/arith_proof.cpp @@ -804,9 +804,9 @@ void LFSCArithProof::printOwnedSort(Type type, std::ostream& os) { if (type.isInteger() && d_realMode) { // If in "real mode", don't use type Int for, e.g., equality. - os << "Real "; + os << "Real"; } else { - os << type << " "; + os << type; } } @@ -830,7 +830,7 @@ void LFSCArithProof::printDeferredDeclarations(std::ostream& os, std::ostream& p // Nothing to do here at this point. } -void LFSCArithProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren) { +void LFSCArithProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) { // Nothing to do here at this point. } diff --git a/src/proof/arith_proof.h b/src/proof/arith_proof.h index d980654c4..126e89ed2 100644 --- a/src/proof/arith_proof.h +++ b/src/proof/arith_proof.h @@ -74,7 +74,7 @@ public: virtual void printSortDeclarations(std::ostream& os, std::ostream& paren); virtual void printTermDeclarations(std::ostream& os, std::ostream& paren); virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); - virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren); + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap); }; diff --git a/src/proof/array_proof.cpp b/src/proof/array_proof.cpp index 484bc70c8..32a7c247d 100644 --- a/src/proof/array_proof.cpp +++ b/src/proof/array_proof.cpp @@ -305,7 +305,8 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, out << " "; ProofManager::getTheoryProofEngine()->printConstantDisequalityProof(out, n1[0].toExpr(), - n1[1].toExpr()); + n1[1].toExpr(), + map); } out << "))" << std::endl; @@ -585,7 +586,8 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, ProofManager::getTheoryProofEngine()->printConstantDisequalityProof(out, n[0].toExpr(), - n[1].toExpr()); + n[1].toExpr(), + map); return pf->d_node; } @@ -637,6 +639,7 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, // we look at the node after the equality sequence. If it needs a, we go for a=a; and if it needs // b, we go for b=b. If there is no following node, we look at the goal of the transitivity proof, // and use it to determine which option we need. + if(n2.getKind() == kind::EQUAL || n2.getKind() == kind::IFF) { if (((n1[0] == n2[0]) && (n1[1] == n2[1])) || ((n1[0] == n2[1]) && (n1[1] == n2[0]))) { // We are in a sequence of identical equalities @@ -900,7 +903,6 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, // inner index != outer index // t3 is the outer index - Assert(pf->d_children.size() == 1); std::stringstream ss; Node subproof = toStreamRecLFSC(ss, tp, pf->d_children[0], tb + 1, map); @@ -915,22 +917,76 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, tp->printTerm(t4.toExpr(), out, map); out << " "; + Debug("pf::array") << "pf->d_children[0]->d_node is: " << pf->d_children[0]->d_node << ". t3 is: " << t3 << std::endl << "subproof is: " << subproof << std::endl; Debug("pf::array") << "Subproof is: " << ss.str() << std::endl; - if (subproof[0][1] == t3) { - Debug("pf::array") << "Dont need symmetry!" << std::endl; - out << ss.str(); + // The subproof needs to show that t2 != t3. This can either be a direct disequality, + // or, if (wlog) t2 is constant, it can show that t3 is equal to another constant. + if (subproof.getKind() == kind::NOT) { + // The subproof is for t2 != t3 (or t3 != t2) + if (subproof[0][1] == t3) { + Debug("pf::array") << "Dont need symmetry!" << std::endl; + out << ss.str(); + } else { + Debug("pf::array") << "Need symmetry!" << std::endl; + out << "(negsymm _ _ _ " << ss.str() << ")"; + } } else { - Debug("pf::array") << "Need symmetry!" << std::endl; - out << "(negsymm _ _ _ " << ss.str() << ")"; + // Either t2 or t3 is a constant. + Assert(subproof.getKind() == kind::EQUAL); + Assert(subproof[0].isConst() || subproof[1].isConst()); + Assert(t2.isConst() || t3.isConst()); + Assert(!(t2.isConst() && t3.isConst())); + + bool t2IsConst = t2.isConst(); + if (subproof[0].isConst()) { + if (t2IsConst) { + // (t3 == subproof[1]) == subproof[0] != t2 + // goal is t2 != t3 + // subproof already shows constant = t3 + Assert(t3 == subproof[1]); + out << "(negtrans _ _ _ _ "; + tp->printConstantDisequalityProof(out, t2.toExpr(), subproof[0].toExpr(), map); + out << " "; + out << ss.str(); + out << ")"; + } else { + Assert(t2 == subproof[1]); + out << "(negsymm _ _ _ "; + out << "(negtrans _ _ _ _ "; + tp->printConstantDisequalityProof(out, t3.toExpr(), subproof[0].toExpr(), map); + out << " "; + out << ss.str(); + out << "))"; + } + } else { + if (t2IsConst) { + // (t3 == subproof[0]) == subproof[1] != t2 + // goal is t2 != t3 + // subproof already shows constant = t3 + Assert(t3 == subproof[0]); + out << "(negtrans _ _ _ _ "; + tp->printConstantDisequalityProof(out, t2.toExpr(), subproof[1].toExpr(), map); + out << " "; + out << "(symm _ _ _ " << ss.str() << ")"; + out << ")"; + } else { + Assert(t2 == subproof[0]); + out << "(negsymm _ _ _ "; + out << "(negtrans _ _ _ _ "; + tp->printConstantDisequalityProof(out, t3.toExpr(), subproof[1].toExpr(), map); + out << " "; + out << "(symm _ _ _ " << ss.str() << ")"; + out << "))"; + } + } } out << ")"; - return ret; } else { Debug("pf::array") << "In the case of NEGATIVE ROW" << std::endl; @@ -1215,7 +1271,7 @@ void LFSCArrayProof::printOwnedSort(Type type, std::ostream& os) { printSort(array_type.getConstituentType(), os); os << ")"; } else { - os << type <<" "; + os << type; } } @@ -1268,6 +1324,7 @@ void LFSCArrayProof::printTermDeclarations(std::ostream& os, std::ostream& paren printSort(array_type.getConstituentType(), os); os << "))\n"; + paren << ")"; } else { Assert(term.isVariable()); if (ProofManager::getSkolemizationManager()->isSkolem(*it)) { @@ -1277,10 +1334,9 @@ void LFSCArrayProof::printTermDeclarations(std::ostream& os, std::ostream& paren os << "(% " << ProofManager::sanitize(term) << " "; os << "(term "; os << term.getType() << ")\n"; + paren << ")"; } } - - paren << ")"; } Debug("pf::array") << "Declaring terms done!" << std::endl; @@ -1326,7 +1382,7 @@ void LFSCArrayProof::printDeferredDeclarations(std::ostream& os, std::ostream& p } } -void LFSCArrayProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren) { +void LFSCArrayProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) { // Nothing to do here at this point. } diff --git a/src/proof/array_proof.h b/src/proof/array_proof.h index 69e62dbf3..61f168a16 100644 --- a/src/proof/array_proof.h +++ b/src/proof/array_proof.h @@ -124,7 +124,7 @@ public: virtual void printSortDeclarations(std::ostream& os, std::ostream& paren); virtual void printTermDeclarations(std::ostream& os, std::ostream& paren); virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); - virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren); + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap); }; diff --git a/src/proof/bitvector_proof.cpp b/src/proof/bitvector_proof.cpp index f19e45920..8c6af5c34 100644 --- a/src/proof/bitvector_proof.cpp +++ b/src/proof/bitvector_proof.cpp @@ -16,6 +16,7 @@ **/ #include "options/bv_options.h" +#include "options/proof_options.h" #include "proof/array_proof.h" #include "proof/bitvector_proof.h" #include "proof/clause_id.h" @@ -41,6 +42,7 @@ BitVectorProof::BitVectorProof(theory::bv::TheoryBV* bv, TheoryProofEngine* proo , d_resolutionProof(NULL) , d_cnfProof(NULL) , d_bitblaster(NULL) + , d_useConstantLetification(false) {} void BitVectorProof::initSatProof(CVC4::BVMinisat::Solver* solver) { @@ -117,6 +119,14 @@ void BitVectorProof::registerAtomBB(Expr atom, Expr atom_bb) { void BitVectorProof::registerTerm(Expr term) { Debug("pf::bv") << "BitVectorProof::registerTerm( " << term << " )" << std::endl; + if (options::lfscLetification() && term.isConst()) { + if (d_constantLetMap.find(term) == d_constantLetMap.end()) { + std::ostringstream name; + name << "letBvc" << d_constantLetMap.size(); + d_constantLetMap[term] = name.str(); + } + } + d_usedBB.insert(term); if (Theory::isLeafOf(term, theory::THEORY_BV) && @@ -384,16 +394,21 @@ void LFSCBitVectorProof::printBitOf(Expr term, std::ostream& os, const ProofLetM void LFSCBitVectorProof::printConstant(Expr term, std::ostream& os) { Assert (term.isConst()); - os <<"(a_bv " << utils::getSize(term)<<" "; - std::ostringstream paren; - int size = utils::getSize(term); - for (int i = size - 1; i >= 0; --i) { - os << "(bvc "; - os << (utils::getBit(term, i) ? "b1" : "b0") <<" "; - paren << ")"; + os << "(a_bv " << utils::getSize(term) << " "; + + if (d_useConstantLetification) { + os << d_constantLetMap[term] << ")"; + } else { + std::ostringstream paren; + int size = utils::getSize(term); + for (int i = size - 1; i >= 0; --i) { + os << "(bvc "; + os << (utils::getBit(term, i) ? "b1" : "b0") <<" "; + paren << ")"; + } + os << " bvn)"; + os << paren.str(); } - os << " bvn)"; - os << paren.str(); } void LFSCBitVectorProof::printOperatorNary(Expr term, std::ostream& os, const ProofLetMap& map) { @@ -464,7 +479,7 @@ void LFSCBitVectorProof::printOwnedSort(Type type, std::ostream& os) { Debug("pf::bv") << std::endl << "(pf::bv) LFSCBitVectorProof::printOwnedSort( " << type << " )" << std::endl; Assert (type.isBitVector()); unsigned width = utils::getSize(type); - os << "(BitVec "<<width<<")"; + os << "(BitVec " << width << ")"; } void LFSCBitVectorProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map) { @@ -653,10 +668,29 @@ void LFSCBitVectorProof::printTermDeclarations(std::ostream& os, std::ostream& p } void LFSCBitVectorProof::printDeferredDeclarations(std::ostream& os, std::ostream& paren) { - // Nothing to do here at this point. + if (options::lfscLetification()) { + os << std::endl << ";; BV const letification\n" << std::endl; + std::map<Expr,std::string>::const_iterator it; + for (it = d_constantLetMap.begin(); it != d_constantLetMap.end(); ++it) { + os << "\n(@ " << it->second << " "; + std::ostringstream localParen; + int size = utils::getSize(it->first); + for (int i = size - 1; i >= 0; --i) { + os << "(bvc "; + os << (utils::getBit(it->first, i) ? "b1" : "b0") << " "; + localParen << ")"; + } + os << "bvn"; + os << localParen.str(); + paren << ")"; + } + os << std::endl; + + d_useConstantLetification = true; + } } -void LFSCBitVectorProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren) { +void LFSCBitVectorProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) { // Print "trust" statements to bind complex bv variables to their associated terms ExprToString::const_iterator it = d_assignedAliases.begin(); @@ -673,8 +707,7 @@ void LFSCBitVectorProof::printAliasingDeclarations(std::ostream& os, std::ostrea os << "(trust_f "; os << "(= (BitVec " << utils::getSize(it->first) << ") "; os << "(a_var_bv " << utils::getSize(it->first) << " " << it->second << ") "; - ProofLetMap emptyMap; - d_proofEngine->printBoundTerm(it->first, os, emptyMap); + d_proofEngine->printBoundTerm(it->first, os, globalLetMap); os << ")) "; os << "(\\ "<< d_aliasToBindDeclaration[it->second] << "\n"; paren << "))"; @@ -701,15 +734,20 @@ void LFSCBitVectorProof::printTermBitblasting(Expr term, std::ostream& os) { os << "(bv_bbl_const "<< utils::getSize(term) <<" _ "; std::ostringstream paren; int size = utils::getSize(term); - for (int i = size - 1; i>= 0; --i) { - os << "(bvc "; - os << (utils::getBit(term, i) ? "b1" : "b0") <<" "; - paren << ")"; + if (d_useConstantLetification) { + os << d_constantLetMap[term] << ")"; + } else { + for (int i = size - 1; i>= 0; --i) { + os << "(bvc "; + os << (utils::getBit(term, i) ? "b1" : "b0") <<" "; + paren << ")"; + } + os << " bvn)"; + os << paren.str(); } - os << " bvn)"; - os << paren.str(); return; } + case kind::BITVECTOR_AND : case kind::BITVECTOR_OR : case kind::BITVECTOR_XOR : @@ -1016,7 +1054,7 @@ bool LFSCBitVectorProof::hasAlias(Expr expr) { return d_assignedAliases.find(expr) != d_assignedAliases.end(); } -void LFSCBitVectorProof::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2) { +void LFSCBitVectorProof::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap) { Assert (c1.isConst()); Assert (c2.isConst()); Assert (utils::getSize(c1) == utils::getSize(c2)); diff --git a/src/proof/bitvector_proof.h b/src/proof/bitvector_proof.h index a52292ec9..6b952e35c 100644 --- a/src/proof/bitvector_proof.h +++ b/src/proof/bitvector_proof.h @@ -81,6 +81,10 @@ protected: bool d_isAssumptionConflict; theory::bv::TBitblaster<Node>* d_bitblaster; std::string getBBTermName(Expr expr); + + std::map<Expr,std::string> d_constantLetMap; + bool d_useConstantLetification; + public: BitVectorProof(theory::bv::TheoryBV* bv, TheoryProofEngine* proofEngine); @@ -148,12 +152,12 @@ public: virtual void printSortDeclarations(std::ostream& os, std::ostream& paren); virtual void printTermDeclarations(std::ostream& os, std::ostream& paren); virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); - virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren); + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap); virtual void printBitblasting(std::ostream& os, std::ostream& paren); virtual void printResolutionProof(std::ostream& os, std::ostream& paren, ProofLetMap& letMap); void calculateAtomsInBitblastingProof(); const std::set<Node>* getAtomsInBitblastingProof(); - void printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2); + void printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap); void printRewriteProof(std::ostream& os, const Node &n1, const Node &n2); }; diff --git a/src/proof/lemma_proof.cpp b/src/proof/lemma_proof.cpp index a12a516cf..3a962f987 100644 --- a/src/proof/lemma_proof.cpp +++ b/src/proof/lemma_proof.cpp @@ -65,6 +65,10 @@ void LemmaProofRecipe::dump(const char *tag) const { Debug(tag) << std::endl << "[Simple lemma]" << std::endl << std::endl; } + if (d_originalLemma != Node()) { + Debug(tag) << std::endl << "Original lemma: " << d_originalLemma << std::endl << std::endl; + } + unsigned count = 1; Debug(tag) << "Base assertions:" << std::endl; for (std::set<Node>::iterator baseIt = d_baseAssertions.begin(); @@ -190,4 +194,13 @@ unsigned LemmaProofRecipe::getNumSteps() const { return d_proofSteps.size(); } +void LemmaProofRecipe::setOriginalLemma(Node lemma) { + d_originalLemma = lemma; +} + +Node LemmaProofRecipe::getOriginalLemma() const { + return d_originalLemma; +} + + } /* namespace CVC4 */ diff --git a/src/proof/lemma_proof.h b/src/proof/lemma_proof.h index e96ff5337..9c838cee7 100644 --- a/src/proof/lemma_proof.h +++ b/src/proof/lemma_proof.h @@ -51,6 +51,10 @@ public: bool wasRewritten(Node assertion) const; Node getExplanation(Node assertion) const; + //* Original lemma */ + void setOriginalLemma(Node lemma); + Node getOriginalLemma() const; + //* Proof Steps */ void addStep(ProofStep& proofStep); const ProofStep* getStep(unsigned index) const; @@ -72,6 +76,9 @@ private: //* A map from assertions to their rewritten explanations (toAssert --> toExplain) */ std::map<Node, Node> d_assertionToExplanation; + + //* The original lemma, as asserted by the owner theory solver */ + Node d_originalLemma; }; } /* CVC4 namespace */ diff --git a/src/proof/proof_manager.cpp b/src/proof/proof_manager.cpp index d155630e5..1c9bb0ff0 100644 --- a/src/proof/proof_manager.cpp +++ b/src/proof/proof_manager.cpp @@ -41,6 +41,15 @@ namespace CVC4 { +std::string nodeSetToString(const std::set<Node>& nodes) { + std::ostringstream os; + std::set<Node>::const_iterator it; + for (it = nodes.begin(); it != nodes.end(); ++it) { + os << *it << " "; + } + return os.str(); +} + std::string append(const std::string& str, uint64_t num) { std::ostringstream os; os << str << num; @@ -202,6 +211,10 @@ std::string ProofManager::getLitName(prop::SatLiteral lit, std::string ProofManager::getPreprocessedAssertionName(Node node, const std::string& prefix) { + if (currentPM()->d_assertionFilters.find(node) != currentPM()->d_assertionFilters.end()) { + return currentPM()->d_assertionFilters[node]; + } + node = node.getKind() == kind::BITVECTOR_EAGER_ATOM ? node[0] : node; return append(prefix+".PA", node.getId()); } @@ -209,6 +222,9 @@ std::string ProofManager::getAssertionName(Node node, const std::string& prefix) { return append(prefix+".A", node.getId()); } +std::string ProofManager::getInputFormulaName(const Expr& expr) { + return currentPM()->d_inputFormulaToName[expr]; +} std::string ProofManager::getAtomName(TNode atom, const std::string& prefix) { @@ -243,7 +259,7 @@ std::string ProofManager::sanitize(TNode node) { return name; } -void ProofManager::traceDeps(TNode n) { +void ProofManager::traceDeps(TNode n, ExprSet* coreAssertions) { Debug("cores") << "trace deps " << n << std::endl; if ((n.isConst() && n == NodeManager::currentNM()->mkConst<bool>(true)) || (n.getKind() == kind::NOT && n[0] == NodeManager::currentNM()->mkConst<bool>(false))) { @@ -252,7 +268,7 @@ void ProofManager::traceDeps(TNode n) { if(d_inputCoreFormulas.find(n.toExpr()) != d_inputCoreFormulas.end()) { // originating formula was in core set Debug("cores") << " -- IN INPUT CORE LIST!" << std::endl; - d_outputCoreFormulas.insert(n.toExpr()); + coreAssertions->insert(n.toExpr()); } else { Debug("cores") << " -- NOT IN INPUT CORE LIST!" << std::endl; if(d_deps.find(n) == d_deps.end()) { @@ -263,7 +279,7 @@ void ProofManager::traceDeps(TNode n) { for(std::vector<Node>::const_iterator i = deps.begin(); i != deps.end(); ++i) { Debug("cores") << " + tracing deps: " << n << " -deps-on- " << *i << std::endl; if( !(*i).isNull() ){ - traceDeps(*i); + traceDeps(*i, coreAssertions); } } } @@ -271,7 +287,7 @@ void ProofManager::traceDeps(TNode n) { void ProofManager::traceUnsatCore() { Assert (options::unsatCores()); - d_satProof->constructProof(); + constructSatProof(); IdToSatClause used_lemmas; IdToSatClause used_inputs; d_satProof->collectClausesUsed(used_inputs, @@ -287,14 +303,157 @@ void ProofManager::traceUnsatCore() { rule == RULE_GIVEN) { // trace dependences back to actual assertions // (this adds them to the unsat core) - traceDeps(node); + traceDeps(node, &d_outputCoreFormulas); + } + } +} + +bool ProofManager::unsatCoreAvailable() const { + return d_satProof->derivedEmptyClause(); +} + +void ProofManager::constructSatProof() { + if (!d_satProof->proofConstructed()) { + d_satProof->constructProof(); + } +} + +void ProofManager::getLemmasInUnsatCore(theory::TheoryId theory, std::vector<Node> &lemmas) { + Assert(PROOF_ON(), "Cannot compute unsat core when proofs are off"); + Assert(unsatCoreAvailable(), "Cannot get unsat core at this time. Mabye the input is SAT?" ); + + constructSatProof(); + + IdToSatClause used_lemmas; + IdToSatClause used_inputs; + d_satProof->collectClausesUsed(used_inputs, used_lemmas); + + IdToSatClause::const_iterator it; + std::set<Node> seen; + + Debug("pf::lemmasUnsatCore") << "Dumping all lemmas in unsat core" << std::endl; + for (it = used_lemmas.begin(); it != used_lemmas.end(); ++it) { + std::set<Node> lemma = satClauseToNodeSet(it->second); + Debug("pf::lemmasUnsatCore") << nodeSetToString(lemma); + + // TODO: we should be able to drop the "haveProofRecipe" check. + // however, there are some rewrite issues with the arith solver, resulting + // in non-registered recipes. For now we assume no one is requesting arith lemmas. + LemmaProofRecipe recipe; + if (!getCnfProof()->haveProofRecipe(lemma)) { + Debug("pf::lemmasUnsatCore") << "\t[no recipe]" << std::endl; + continue; + } + + recipe = getCnfProof()->getProofRecipe(lemma); + Debug("pf::lemmasUnsatCore") << "\t[owner = " << recipe.getTheory() + << ", original = " << recipe.getOriginalLemma() << "]" << std::endl; + if (recipe.simpleLemma() && recipe.getTheory() == theory && seen.find(recipe.getOriginalLemma()) == seen.end()) { + lemmas.push_back(recipe.getOriginalLemma()); + seen.insert(recipe.getOriginalLemma()); + } + } +} + +std::set<Node> ProofManager::satClauseToNodeSet(prop::SatClause* clause) { + std::set<Node> result; + for (unsigned i = 0; i < clause->size(); ++i) { + prop::SatLiteral lit = (*clause)[i]; + Node node = getCnfProof()->getAtom(lit.getSatVariable()); + Expr atom = node.toExpr(); + if (atom != utils::mkTrue()) + result.insert(lit.isNegated() ? node.notNode() : node); + } + + return result; +} + +Node ProofManager::getWeakestImplicantInUnsatCore(Node lemma) { + Assert(PROOF_ON(), "Cannot compute unsat core when proofs are off"); + Assert(unsatCoreAvailable(), "Cannot get unsat core at this time. Mabye the input is SAT?" ); + + // If we're doing aggressive minimization, work on all lemmas, not just conjunctions. + if (!options::aggressiveCoreMin() && (lemma.getKind() != kind::AND)) + return lemma; + + constructSatProof(); + + NodeBuilder<> builder(kind::AND); + + IdToSatClause used_lemmas; + IdToSatClause used_inputs; + d_satProof->collectClausesUsed(used_inputs, used_lemmas); + + IdToSatClause::const_iterator it; + std::set<Node>::iterator lemmaIt; + + if (!options::aggressiveCoreMin()) { + for (it = used_lemmas.begin(); it != used_lemmas.end(); ++it) { + std::set<Node> currentLemma = satClauseToNodeSet(it->second); + + // TODO: we should be able to drop the "haveProofRecipe" check. + // however, there are some rewrite issues with the arith solver, resulting + // in non-registered recipes. For now we assume no one is requesting arith lemmas. + LemmaProofRecipe recipe; + if (!getCnfProof()->haveProofRecipe(currentLemma)) + continue; + + recipe = getCnfProof()->getProofRecipe(currentLemma); + if (recipe.getOriginalLemma() == lemma) { + for (lemmaIt = currentLemma.begin(); lemmaIt != currentLemma.end(); ++lemmaIt) { + builder << *lemmaIt; + + // Check that each conjunct appears in the original lemma. + bool found = false; + for (unsigned i = 0; i < lemma.getNumChildren(); ++i) { + if (lemma[i] == *lemmaIt) + found = true; + } + + if (!found) + return lemma; + } + } + } + } else { + // Aggressive mode + for (it = used_lemmas.begin(); it != used_lemmas.end(); ++it) { + std::set<Node> currentLemma = satClauseToNodeSet(it->second); + + // TODO: we should be able to drop the "haveProofRecipe" check. + // however, there are some rewrite issues with the arith solver, resulting + // in non-registered recipes. For now we assume no one is requesting arith lemmas. + LemmaProofRecipe recipe; + if (!getCnfProof()->haveProofRecipe(currentLemma)) + continue; + + recipe = getCnfProof()->getProofRecipe(currentLemma); + if (recipe.getOriginalLemma() == lemma) { + NodeBuilder<> disjunction(kind::OR); + for (lemmaIt = currentLemma.begin(); lemmaIt != currentLemma.end(); ++lemmaIt) { + disjunction << *lemmaIt; + } + + Node conjunct = (disjunction.getNumChildren() == 1) ? disjunction[0] : disjunction; + builder << conjunct; + } } } + + AlwaysAssert(builder.getNumChildren() != 0); + + if (builder.getNumChildren() == 1) + return builder[0]; + + return builder; } void ProofManager::addAssertion(Expr formula) { Debug("proof:pm") << "assert: " << formula << std::endl; d_inputFormulas.insert(formula); + std::ostringstream name; + name << "A" << d_inputFormulaToName.size(); + d_inputFormulaToName[formula] = name.str(); } void ProofManager::addCoreAssertion(Expr formula) { @@ -319,6 +478,10 @@ void ProofManager::addUnsatCore(Expr formula) { d_outputCoreFormulas.insert(formula); } +void ProofManager::addAssertionFilter(const Node& node, const std::string& rewritten) { + d_assertionFilters[node] = rewritten; +} + void ProofManager::setLogic(const LogicInfo& logic) { d_logic = logic; } @@ -343,6 +506,7 @@ void LFSCProof::toStream(std::ostream& out) { Assert(options::bitblastMode() != theory::bv::BITBLAST_MODE_EAGER); + Assert(!d_satProof->proofConstructed()); d_satProof->constructProof(); // collecting leaf clauses in resolution proof @@ -446,6 +610,7 @@ void LFSCProof::toStream(std::ostream& out) { smt::SmtScope scope(d_smtEngine); std::ostringstream paren; out << "(check\n"; + paren << ")"; out << " ;; Declarations\n"; // declare the theory atoms @@ -468,23 +633,22 @@ void LFSCProof::toStream(std::ostream& out) { Debug("pf::pm") << std::endl << "LFSCProof::toStream: print assertions DONE" << std::endl; out << "(: (holds cln)\n\n"; + paren << ")"; // Have the theory proofs print deferred declarations, e.g. for skolem variables. out << " ;; Printing deferred declarations \n\n"; d_theoryProof->printDeferredDeclarations(out, paren); + out << "\n ;; Printing the global let map"; d_theoryProof->finalizeBvConflicts(used_lemmas, out); ProofManager::getBitVectorProof()->calculateAtomsInBitblastingProof(); - - out << "\n ;; Printing the global let map \n"; - ProofLetMap globalLetMap; if (options::lfscLetification()) { ProofManager::currentPM()->printGlobalLetMap(atoms, globalLetMap, out, paren); } out << " ;; Printing aliasing declarations \n\n"; - d_theoryProof->printAliasingDeclarations(out, paren); + d_theoryProof->printAliasingDeclarations(out, paren, globalLetMap); out << " ;; Rewrites for Lemmas \n"; d_theoryProof->printLemmaRewrites(rewrites, out, paren); @@ -511,20 +675,15 @@ void LFSCProof::toStream(std::ostream& out) { Debug("pf::pm") << "Proof manager: printing theory lemmas DONE!" << std::endl; if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER && ProofManager::getBitVectorProof()) { - // print actual resolution proof - // d_satProof->printResolutions(out, paren); ProofManager::getBitVectorProof()->getSatProof()->printResolutionEmptyClause(out, paren); - paren <<")))\n;;"; - out << paren.str(); - out << "\n"; } else { // print actual resolution proof d_satProof->printResolutions(out, paren); d_satProof->printResolutionEmptyClause(out, paren); - paren <<")))\n;;"; - out << paren.str(); - out << "\n"; } + + out << paren.str(); + out << "\n;;\n"; } void LFSCProof::printPreprocessedAssertions(const NodeSet& assertions, @@ -537,21 +696,118 @@ void LFSCProof::printPreprocessedAssertions(const NodeSet& assertions, Debug("pf::pm") << "LFSCProof::printPreprocessedAssertions starting" << std::endl; - for (; it != end; ++it) { - os << "(th_let_pf _ "; + if (options::fewerPreprocessingHoles()) { + // Check for assertions that did not get rewritten, and update the printing filter. + checkUnrewrittenAssertion(assertions); + + // For the remaining assertions, bind them to input assertions. + for (; it != end; ++it) { + // Rewrite preprocessing step if it cannot be eliminated + if (!ProofManager::currentPM()->have_input_assertion((*it).toExpr())) { + os << "(th_let_pf _ (trust_f (iff "; + + Expr inputAssertion; + + if (((*it).isConst() && *it == NodeManager::currentNM()->mkConst<bool>(true)) || + ((*it).getKind() == kind::NOT && (*it)[0] == NodeManager::currentNM()->mkConst<bool>(false))) { + inputAssertion = NodeManager::currentNM()->mkConst<bool>(true).toExpr(); + } else { + // Figure out which input assertion led to this assertion + ExprSet inputAssertions; + ProofManager::currentPM()->traceDeps(*it, &inputAssertions); + + Debug("pf::pm") << "Original assertions for " << *it << " are: " << std::endl; + + ProofManager::assertions_iterator assertionIt; + for (assertionIt = inputAssertions.begin(); assertionIt != inputAssertions.end(); ++assertionIt) { + Debug("pf::pm") << "\t" << *assertionIt << std::endl; + } + + if (inputAssertions.size() == 0) { + Debug("pf::pm") << "LFSCProof::printPreprocessedAssertions: Count NOT find the assertion that caused this PA. Picking an arbitrary one..." << std::endl; + // For now just use the first assertion... + inputAssertion = *(ProofManager::currentPM()->begin_assertions()); + } else { + if (inputAssertions.size() != 1) { + Debug("pf::pm") << "LFSCProof::printPreprocessedAssertions: Attention: more than one original assertion was found. Picking just one." << std::endl; + } + inputAssertion = *inputAssertions.begin(); + } + } + + if (!ProofManager::currentPM()->have_input_assertion(inputAssertion)) { + // The thing returned by traceDeps does not appear in the input assertions... + Debug("pf::pm") << "LFSCProof::printPreprocessedAssertions: Count NOT find the assertion that caused this PA. Picking an arbitrary one..." << std::endl; + // For now just use the first assertion... + inputAssertion = *(ProofManager::currentPM()->begin_assertions()); + } + + Debug("pf::pm") << "Original assertion for " << *it + << " is: " + << inputAssertion + << ", AKA " + << ProofManager::currentPM()->getInputFormulaName(inputAssertion) + << std::endl; + + ProofManager::currentPM()->getTheoryProofEngine()->printTheoryTerm(inputAssertion, os, globalLetMap); + os << " "; + ProofManager::currentPM()->getTheoryProofEngine()->printTheoryTerm((*it).toExpr(), os, globalLetMap); + + os << "))"; + os << "(\\ "<< ProofManager::getPreprocessedAssertionName(*it, "") << "\n"; + paren << "))"; + + std::ostringstream rewritten; + + rewritten << "(or_elim_1 _ _ "; + rewritten << "(not_not_intro _ "; + rewritten << ProofManager::currentPM()->getInputFormulaName(inputAssertion); + rewritten << ") (iff_elim_1 _ _ "; + rewritten << ProofManager::getPreprocessedAssertionName(*it, ""); + rewritten << "))"; + + ProofManager::currentPM()->addAssertionFilter(*it, rewritten.str()); + } + } + } else { + for (; it != end; ++it) { + os << "(th_let_pf _ "; - //TODO - os << "(trust_f "; - ProofManager::currentPM()->getTheoryProofEngine()->printTheoryTerm((*it).toExpr(), os, globalLetMap); - os << ") "; + //TODO + os << "(trust_f "; + ProofManager::currentPM()->getTheoryProofEngine()->printTheoryTerm((*it).toExpr(), os, globalLetMap); + os << ") "; - os << "(\\ "<< ProofManager::getPreprocessedAssertionName(*it, "") << "\n"; - paren << "))"; + os << "(\\ "<< ProofManager::getPreprocessedAssertionName(*it, "") << "\n"; + paren << "))"; + } } os << "\n"; } +void LFSCProof::checkUnrewrittenAssertion(const NodeSet& rewrites) { + Debug("pf::pm") << "LFSCProof::checkUnrewrittenAssertion starting" << std::endl; + + NodeSet::const_iterator rewrite; + for (rewrite = rewrites.begin(); rewrite != rewrites.end(); ++rewrite) { + Debug("pf::pm") << "LFSCProof::checkUnrewrittenAssertion: handling " << *rewrite << std::endl; + if (ProofManager::currentPM()->have_input_assertion((*rewrite).toExpr())) { + Assert(ProofManager::currentPM()->have_input_assertion((*rewrite).toExpr())); + Debug("pf::pm") << "LFSCProof::checkUnrewrittenAssertion: this assertion was NOT rewritten!" << std::endl + << "\tAdding filter: " + << ProofManager::getPreprocessedAssertionName(*rewrite, "") + << " --> " + << ProofManager::currentPM()->getInputFormulaName((*rewrite).toExpr()) + << std::endl; + ProofManager::currentPM()->addAssertionFilter(*rewrite, + ProofManager::currentPM()->getInputFormulaName((*rewrite).toExpr())); + } else { + Debug("pf::pm") << "LFSCProof::checkUnrewrittenAssertion: this assertion WAS rewritten! " << *rewrite << std::endl; + } + } +} + //---from Morgan--- bool ProofManager::hasOp(TNode n) const { diff --git a/src/proof/proof_manager.h b/src/proof/proof_manager.h index 14793f380..fa7224d72 100644 --- a/src/proof/proof_manager.h +++ b/src/proof/proof_manager.h @@ -21,13 +21,11 @@ #include <iosfwd> #include <map> -#include "proof/proof.h" -#include "proof/proof_utils.h" -#include "proof/skolemization_manager.h" -#include "util/proof.h" #include "expr/node.h" #include "proof/clause_id.h" #include "proof/proof.h" +#include "proof/proof_utils.h" +#include "proof/skolemization_manager.h" #include "theory/logic_info.h" #include "theory/substitutions.h" #include "util/proof.h" @@ -147,6 +145,7 @@ class ProofManager { // information that will need to be shared across proofs ExprSet d_inputFormulas; + std::map<Expr, std::string> d_inputFormulaToName; ExprSet d_inputCoreFormulas; ExprSet d_outputCoreFormulas; @@ -158,12 +157,11 @@ class ProofManager { ProofFormat d_format; // used for now only in debug builds NodeToNodes d_deps; - // trace dependences back to unsat core - void traceDeps(TNode n); std::set<Type> d_printedTypes; std::map<std::string, std::string> d_rewriteFilters; + std::map<Node, std::string> d_assertionFilters; std::vector<RewriteLogEntry> d_rewriteLog; @@ -204,6 +202,9 @@ public: } assertions_iterator end_assertions() const { return d_inputFormulas.end(); } size_t num_assertions() const { return d_inputFormulas.size(); } + bool have_input_assertion(const Expr& assertion) { + return d_inputFormulas.find(assertion) != d_inputFormulas.end(); + } //---from Morgan--- Node mkOp(TNode n); @@ -222,6 +223,7 @@ public: static std::string getLearntClauseName(ClauseId id, const std::string& prefix = ""); static std::string getPreprocessedAssertionName(Node node, const std::string& prefix = ""); static std::string getAssertionName(Node node, const std::string& prefix = ""); + static std::string getInputFormulaName(const Expr& expr); static std::string getVarName(prop::SatVariable var, const std::string& prefix = ""); static std::string getAtomName(prop::SatVariable var, const std::string& prefix = ""); @@ -241,11 +243,17 @@ public: void addDependence(TNode n, TNode dep); void addUnsatCore(Expr formula); + // trace dependences back to unsat core + void traceDeps(TNode n, ExprSet* coreAssertions); void traceUnsatCore(); assertions_iterator begin_unsat_core() const { return d_outputCoreFormulas.begin(); } assertions_iterator end_unsat_core() const { return d_outputCoreFormulas.end(); } size_t size_unsat_core() const { return d_outputCoreFormulas.size(); } + bool unsatCoreAvailable() const; + void getLemmasInUnsatCore(theory::TheoryId theory, std::vector<Node> &lemmas); + Node getWeakestImplicantInUnsatCore(Node lemma); + int nextId() { return d_nextId++; } void setLogic(const LogicInfo& logic); @@ -258,6 +266,8 @@ public: void addRewriteFilter(const std::string &original, const std::string &substitute); void clearRewriteFilters(); + void addAssertionFilter(const Node& node, const std::string& rewritten); + static void registerRewrite(unsigned ruleId, Node original, Node result); static void clearRewriteLog(); @@ -268,6 +278,10 @@ public: ProofLetMap& letMap, std::ostream& out, std::ostringstream& paren); + +private: + void constructSatProof(); + std::set<Node> satClauseToNodeSet(prop::SatClause* clause); };/* class ProofManager */ class LFSCProof : public Proof { @@ -281,6 +295,9 @@ class LFSCProof : public Proof { std::ostream& os, std::ostream& paren, ProofLetMap& globalLetMap); + + void checkUnrewrittenAssertion(const NodeSet& assertions); + public: LFSCProof(SmtEngine* smtEngine, LFSCCoreSatProof* sat, diff --git a/src/proof/proof_utils.h b/src/proof/proof_utils.h index 546d419fc..b172217d8 100644 --- a/src/proof/proof_utils.h +++ b/src/proof/proof_utils.h @@ -33,7 +33,8 @@ typedef std::pair<Node, Node> NodePair; typedef std::set<NodePair> NodePairSet; -struct ProofLetCount { +class ProofLetCount { +public: static unsigned counter; static void resetCounter() { counter = 0; } static unsigned newId() { return ++counter; } diff --git a/src/proof/sat_proof.h b/src/proof/sat_proof.h index bda8094be..52e059e95 100644 --- a/src/proof/sat_proof.h +++ b/src/proof/sat_proof.h @@ -198,7 +198,9 @@ class TSatProof { */ void constructProof(ClauseId id); void constructProof() { constructProof(d_emptyClauseId); } + bool proofConstructed() const; void collectClauses(ClauseId id); + bool derivedEmptyClause() const; prop::SatClause* buildClause(ClauseId id); virtual void printResolution(ClauseId id, std::ostream& out, @@ -354,7 +356,7 @@ class TSatProof { IdToSatClause d_seenInputs; IdToSatClause d_seenLemmas; - private: + private: __gnu_cxx::hash_map<ClauseId, int> d_glueMap; struct Statistics { IntStat d_numLearnedClauses; @@ -369,6 +371,7 @@ class TSatProof { ~Statistics(); }; + bool d_satProofConstructed; Statistics d_statistics; }; /* class TSatProof */ diff --git a/src/proof/sat_proof_implementation.h b/src/proof/sat_proof_implementation.h index 1e01e4dce..603559da1 100644 --- a/src/proof/sat_proof_implementation.h +++ b/src/proof/sat_proof_implementation.h @@ -224,6 +224,7 @@ TSatProof<Solver>::TSatProof(Solver* solver, const std::string& name, d_seenLearnt(), d_seenInputs(), d_seenLemmas(), + d_satProofConstructed(false), d_statistics(name) { d_proxy = new ProofProxy<Solver>(this); } @@ -957,10 +958,16 @@ void TSatProof<Solver>::markDeleted(typename Solver::TCRef clause) { template <class Solver> void TSatProof<Solver>::constructProof(ClauseId conflict) { + d_satProofConstructed = true; collectClauses(conflict); } template <class Solver> +bool TSatProof<Solver>::proofConstructed() const { + return d_satProofConstructed; +} + +template <class Solver> std::string TSatProof<Solver>::clauseName(ClauseId id) { std::ostringstream os; if (isInputClause(id)) { @@ -998,6 +1005,11 @@ prop::SatClause* TSatProof<Solver>::buildClause(ClauseId id) { } template <class Solver> +bool TSatProof<Solver>::derivedEmptyClause() const { + return hasResolutionChain(d_emptyClauseId); +} + +template <class Solver> void TSatProof<Solver>::collectClauses(ClauseId id) { if (d_seenInputs.find(id) != d_seenInputs.end() || d_seenLemmas.find(id) != d_seenLemmas.end() || @@ -1080,33 +1092,34 @@ TSatProof<Solver>::Statistics::~Statistics() { template <class Solver> void LFSCSatProof<Solver>::printResolution(ClauseId id, std::ostream& out, std::ostream& paren) { - out << "(satlem_simplify _ _ _ "; + out << "(satlem_simplify _ _ _"; + paren << ")"; const ResChain<Solver>& res = this->getResolutionChain(id); const typename ResChain<Solver>::ResSteps& steps = res.getSteps(); for (int i = steps.size() - 1; i >= 0; i--) { - out << "("; - out << (steps[i].sign ? "R" : "Q") << " _ _ "; + out << " ("; + out << (steps[i].sign ? "R" : "Q") << " _ _"; } ClauseId start_id = res.getStart(); - out << this->clauseName(start_id) << " "; + out << " " << this->clauseName(start_id); for (unsigned i = 0; i < steps.size(); i++) { prop::SatVariable v = prop::MinisatSatSolver::toSatVariable(var(steps[i].lit)); - out << this->clauseName(steps[i].id) << " " + out << " " << this->clauseName(steps[i].id) << " " << ProofManager::getVarName(v, this->d_name) << ")"; } if (id == this->d_emptyClauseId) { - out <<"(\\ empty empty)"; + out <<" (\\ empty empty)"; return; } - out << "(\\ " << this->clauseName(id) << "\n"; // bind to lemma name - paren << "))"; // closing parethesis for lemma binding and satlem + out << " (\\ " << this->clauseName(id) << "\n"; // bind to lemma name + paren << ")"; } /// LFSCSatProof class diff --git a/src/proof/theory_proof.cpp b/src/proof/theory_proof.cpp index d12b561a6..1912dbada 100644 --- a/src/proof/theory_proof.cpp +++ b/src/proof/theory_proof.cpp @@ -126,26 +126,26 @@ void TheoryProofEngine::markTermForFutureRegistration(Expr term, theory::TheoryI d_exprToTheoryIds[term].insert(id); } -void TheoryProofEngine::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2) { +void TheoryProofEngine::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap) { Assert(c1.isConst()); Assert(c2.isConst()); Assert(theory::Theory::theoryOf(c1) == theory::Theory::theoryOf(c2)); - getTheoryProof(theory::Theory::theoryOf(c1))->printConstantDisequalityProof(os, c1, c2); + getTheoryProof(theory::Theory::theoryOf(c1))->printConstantDisequalityProof(os, c1, c2, globalLetMap); } void TheoryProofEngine::registerTerm(Expr term) { - Debug("pf::tp") << "TheoryProofEngine::registerTerm: registering term: " << term << std::endl; + Debug("pf::tp::register") << "TheoryProofEngine::registerTerm: registering term: " << term << std::endl; if (d_registrationCache.count(term)) { return; } - Debug("pf::tp") << "TheoryProofEngine::registerTerm: registering NEW term: " << term << std::endl; + Debug("pf::tp::register") << "TheoryProofEngine::registerTerm: registering NEW term: " << term << std::endl; theory::TheoryId theory_id = theory::Theory::theoryOf(term); - Debug("pf::tp") << "Term's theory( " << term << " ) = " << theory_id << std::endl; + Debug("pf::tp::register") << "Term's theory( " << term << " ) = " << theory_id << std::endl; // don't need to register boolean terms if (theory_id == theory::THEORY_BUILTIN || @@ -165,7 +165,7 @@ void TheoryProofEngine::registerTerm(Expr term) { // A special case: the array theory needs to know of every skolem, even if // it belongs to another theory (e.g., a BV skolem) if (ProofManager::getSkolemizationManager()->isSkolem(term) && theory_id != theory::THEORY_ARRAY) { - Debug("pf::tp") << "TheoryProofEngine::registerTerm: Special case: registering a non-array skolem: " << term << std::endl; + Debug("pf::tp::register") << "TheoryProofEngine::registerTerm: registering a non-array skolem: " << term << std::endl; getTheoryProof(theory::THEORY_ARRAY)->registerTerm(term); } @@ -321,15 +321,12 @@ void LFSCTheoryProofEngine::registerTermsFromAssertions() { void LFSCTheoryProofEngine::printAssertions(std::ostream& os, std::ostream& paren) { Debug("pf::tp") << "LFSCTheoryProofEngine::printAssertions called" << std::endl << std::endl; - unsigned counter = 0; ProofManager::assertions_iterator it = ProofManager::currentPM()->begin_assertions(); ProofManager::assertions_iterator end = ProofManager::currentPM()->end_assertions(); for (; it != end; ++it) { Debug("pf::tp") << "printAssertions: assertion is: " << *it << std::endl; - std::ostringstream name; - name << "A" << counter++; - os << "(% " << name.str() << " (th_holds "; + os << "(% " << ProofManager::currentPM()->getInputFormulaName(*it) << " (th_holds "; // Assertions appear before the global let map, so we use a dummpMap to avoid letification here. ProofLetMap dummyMap; @@ -406,13 +403,13 @@ void LFSCTheoryProofEngine::printDeferredDeclarations(std::ostream& os, std::ost } } -void LFSCTheoryProofEngine::printAliasingDeclarations(std::ostream& os, std::ostream& paren) { +void LFSCTheoryProofEngine::printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) { Debug("pf::tp") << "LFSCTheoryProofEngine::printAliasingDeclarations called" << std::endl; TheoryProofTable::const_iterator it = d_theoryProofTable.begin(); TheoryProofTable::const_iterator end = d_theoryProofTable.end(); for (; it != end; ++it) { - it->second->printAliasingDeclarations(os, paren); + it->second->printAliasingDeclarations(os, paren, globalLetMap); } } @@ -632,15 +629,27 @@ void LFSCTheoryProofEngine::printTheoryLemmas(const IdToSatClause& lemmas, clause_expr[k] = missingAssertion->toExpr(); std::ostringstream rewritten; - rewritten << "(or_elim_1 _ _ "; - rewritten << "(not_not_intro _ "; - rewritten << pm->getLitName(explanation); - rewritten << ") (iff_elim_1 _ _ "; - rewritten << d_assertionToRewrite[missingAssertion->negate()]; - rewritten << "))"; + + if (missingAssertion->getKind() == kind::NOT && (*missingAssertion)[0].toExpr() == utils::mkFalse()) { + rewritten << "(or_elim_2 _ _ "; + rewritten << "(not_not_intro _ "; + rewritten << pm->getLitName(explanation); + rewritten << ") (iff_elim_2 _ _ "; + rewritten << d_assertionToRewrite[missingAssertion->negate()]; + rewritten << "))"; + } + else { + rewritten << "(or_elim_1 _ _ "; + rewritten << "(not_not_intro _ "; + rewritten << pm->getLitName(explanation); + rewritten << ") (iff_elim_1 _ _ "; + rewritten << d_assertionToRewrite[missingAssertion->negate()]; + rewritten << "))"; + } Debug("pf::tp") << "Setting a rewrite filter for this proof: " << std::endl << pm->getLitName(*missingAssertion) << " --> " << rewritten.str() + << ", explanation = " << explanation << std::endl << std::endl; pm->addRewriteFilter(pm->getLitName(*missingAssertion), rewritten.str()); @@ -742,15 +751,27 @@ void LFSCTheoryProofEngine::printTheoryLemmas(const IdToSatClause& lemmas, currentClauseExpr[k] = missingAssertion->toExpr(); std::ostringstream rewritten; - rewritten << "(or_elim_1 _ _ "; - rewritten << "(not_not_intro _ "; - rewritten << pm->getLitName(explanation); - rewritten << ") (iff_elim_1 _ _ "; - rewritten << d_assertionToRewrite[missingAssertion->negate()]; - rewritten << "))"; + + if (missingAssertion->getKind() == kind::NOT && (*missingAssertion)[0].toExpr() == utils::mkFalse()) { + rewritten << "(or_elim_2 _ _ "; + rewritten << "(not_not_intro _ "; + rewritten << pm->getLitName(explanation); + rewritten << ") (iff_elim_2 _ _ "; + rewritten << d_assertionToRewrite[missingAssertion->negate()]; + rewritten << "))"; + } + else { + rewritten << "(or_elim_1 _ _ "; + rewritten << "(not_not_intro _ "; + rewritten << pm->getLitName(explanation); + rewritten << ") (iff_elim_1 _ _ "; + rewritten << d_assertionToRewrite[missingAssertion->negate()]; + rewritten << "))"; + } Debug("pf::tp") << "Setting a rewrite filter for this proof: " << std::endl << pm->getLitName(*missingAssertion) << " --> " << rewritten.str() + << "explanation = " << explanation << std::endl << std::endl; pm->addRewriteFilter(pm->getLitName(*missingAssertion), rewritten.str()); @@ -837,6 +858,7 @@ void LFSCTheoryProofEngine::printCoreTerm(Expr term, std::ostream& os, const Pro os << "("; os << "= "; printSort(term[0].getType(), os); + os << " "; printBoundTerm(term[0], os, map); os << " "; printBoundTerm(term[1], os, map); @@ -850,6 +872,7 @@ void LFSCTheoryProofEngine::printCoreTerm(Expr term, std::ostream& os, const Pro if (term.getNumChildren() == 2) { os << "(not (= "; printSort(term[0].getType(), os); + os << " "; printBoundTerm(term[0], os, map); os << " "; printBoundTerm(term[1], os, map); @@ -865,6 +888,7 @@ void LFSCTheoryProofEngine::printCoreTerm(Expr term, std::ostream& os, const Pro if ((i != 0) || (j != 1)) { os << "(not (= "; printSort(term[0].getType(), os); + os << " "; printBoundTerm(term[i], os, map); os << " "; printBoundTerm(term[j], os, map); @@ -872,6 +896,7 @@ void LFSCTheoryProofEngine::printCoreTerm(Expr term, std::ostream& os, const Pro } else { os << "(not (= "; printSort(term[0].getType(), os); + os << " "; printBoundTerm(term[0], os, map); os << " "; printBoundTerm(term[1], os, map); @@ -968,13 +993,6 @@ void TheoryProof::printTheoryLemmaProof(std::vector<Expr>& lemma, Assert(!oc.d_lemma.isNull()); Trace("pf::tp") << "; ++ but got lemma: " << oc.d_lemma << std::endl; - // Original, as in Liana's branch - // Trace("pf::tp") << "; asserting " << oc.d_lemma[1].negate() << std::endl; - // th->assertFact(oc.d_lemma[1].negate(), false); - // th->check(theory::Theory::EFFORT_FULL); - - // Altered version, to handle OR lemmas - if (oc.d_lemma.getKind() == kind::OR) { Debug("pf::tp") << "OR lemma. Negating each child separately" << std::endl; for (unsigned i = 0; i < oc.d_lemma.getNumChildren(); ++i) { @@ -1063,7 +1081,7 @@ void LFSCBooleanProof::printOwnedTerm(Expr term, std::ostream& os, const ProofLe } // The let map should already have the current expression. - ProofLetMap::const_iterator it = map.find(term); + ProofLetMap::const_iterator it = map.find(currentExpression.toExpr()); if (it != map.end()) { unsigned id = it->second.id; unsigned count = it->second.count; @@ -1144,25 +1162,25 @@ void LFSCBooleanProof::printDeferredDeclarations(std::ostream& os, std::ostream& // Nothing to do here at this point. } -void LFSCBooleanProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren) { +void LFSCBooleanProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) { // Nothing to do here at this point. } void LFSCBooleanProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, - std::ostream& paren) { + std::ostream& paren, + const ProofLetMap& map) { Unreachable("No boolean lemmas yet!"); } -void TheoryProof::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2) { +void TheoryProof::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap) { // By default, we just print a trust statement. Specific theories can implement // better proofs. - ProofLetMap emptyMap; os << "(trust_f (not (= _ "; - d_proofEngine->printBoundTerm(c1, os, emptyMap); + d_proofEngine->printBoundTerm(c1, os, globalLetMap); os << " "; - d_proofEngine->printBoundTerm(c2, os, emptyMap); + d_proofEngine->printBoundTerm(c2, os, globalLetMap); os << ")))"; } diff --git a/src/proof/theory_proof.h b/src/proof/theory_proof.h index 5907f9bd5..34248f7eb 100644 --- a/src/proof/theory_proof.h +++ b/src/proof/theory_proof.h @@ -109,7 +109,7 @@ public: * @param os * @param paren closing parenthesis */ - virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren) = 0; + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) = 0; /** * Print proofs of all the theory lemmas (must prove @@ -149,7 +149,7 @@ public: void markTermForFutureRegistration(Expr term, theory::TheoryId id); - void printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2); + void printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap); virtual void treatBoolsAsFormulas(bool value) {}; @@ -173,7 +173,7 @@ public: virtual void printAssertions(std::ostream& os, std::ostream& paren); virtual void printLemmaRewrites(NodePairSet& rewrites, std::ostream& os, std::ostream& paren); virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); - virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren); + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap); virtual void printTheoryLemmas(const IdToSatClause& lemmas, std::ostream& os, std::ostream& paren, @@ -205,7 +205,7 @@ public: {} virtual ~TheoryProof() {}; /** - * Print a term belonging some theory, not neccessarily this one. + * Print a term belonging some theory, not necessarily this one. * * @param term expresion representing term * @param os output stream @@ -216,7 +216,7 @@ public: /** * Print a term belonging to THIS theory. * - * @param term expresion representing term + * @param term expression representing term * @param os output stream */ virtual void printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map) = 0; @@ -274,7 +274,7 @@ public: * @param os * @param paren */ - virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren) = 0; + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) = 0; /** * Register a term of this theory that appears in the proof. * @@ -286,7 +286,7 @@ public: * * @param term */ - virtual void printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2); + virtual void printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap); /** * Print a proof for the equivalence of n1 and n2. * @@ -308,11 +308,11 @@ public: virtual void printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map) = 0; virtual void printOwnedSort(Type type, std::ostream& os) = 0; - virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren) = 0; + virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map) = 0; virtual void printSortDeclarations(std::ostream& os, std::ostream& paren) = 0; virtual void printTermDeclarations(std::ostream& os, std::ostream& paren) = 0; virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren) = 0; - virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren) = 0; + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) = 0; }; class LFSCBooleanProof : public BooleanProof { @@ -322,11 +322,11 @@ public: {} virtual void printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map); virtual void printOwnedSort(Type type, std::ostream& os); - virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren); + virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map); virtual void printSortDeclarations(std::ostream& os, std::ostream& paren); virtual void printTermDeclarations(std::ostream& os, std::ostream& paren); virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); - virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren); + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap); void treatBoolsAsFormulas(bool value) { d_treatBoolsAsFormulas = value; diff --git a/src/proof/uf_proof.cpp b/src/proof/uf_proof.cpp index 139ce5ed2..27f351102 100644 --- a/src/proof/uf_proof.cpp +++ b/src/proof/uf_proof.cpp @@ -264,7 +264,7 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E out << ss.str(); out << " "; - ProofManager::getTheoryProofEngine()->printConstantDisequalityProof(out, n1[0].toExpr(), n1[1].toExpr()); + ProofManager::getTheoryProofEngine()->printConstantDisequalityProof(out, n1[0].toExpr(), n1[1].toExpr(), map); out << "))" << std::endl; } @@ -733,7 +733,7 @@ void LFSCUFProof::printOwnedSort(Type type, std::ostream& os) { Debug("pf::uf") << std::endl << "(pf::uf) LFSCArrayProof::printOwnedSort: type is: " << type << std::endl; Assert (type.isSort()); - os << type <<" "; + os << type; } void LFSCUFProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map) { @@ -797,7 +797,7 @@ void LFSCUFProof::printDeferredDeclarations(std::ostream& os, std::ostream& pare // Nothing to do here at this point. } -void LFSCUFProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren) { +void LFSCUFProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) { // Nothing to do here at this point. } diff --git a/src/proof/uf_proof.h b/src/proof/uf_proof.h index 444b602dc..ff0f9a879 100644 --- a/src/proof/uf_proof.h +++ b/src/proof/uf_proof.h @@ -70,7 +70,7 @@ public: virtual void printSortDeclarations(std::ostream& os, std::ostream& paren); virtual void printTermDeclarations(std::ostream& os, std::ostream& paren); virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); - virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren); + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap); }; diff --git a/src/prop/cnf_stream.cpp b/src/prop/cnf_stream.cpp index eda736b8a..f2401041e 100644 --- a/src/prop/cnf_stream.cpp +++ b/src/prop/cnf_stream.cpp @@ -678,8 +678,7 @@ void TseitinCnfStream::convertAndAssert(TNode node, bool removable, bool negated, ProofRule proof_id, - TNode from, - LemmaProofRecipe* proofRecipe) { + TNode from) { Debug("cnf") << "convertAndAssert(" << node << ", removable = " << (removable ? "true" : "false") << ", negated = " << (negated ? "true" : "false") << ")" << endl; @@ -689,12 +688,6 @@ void TseitinCnfStream::convertAndAssert(TNode node, Node assertion = negated ? node.notNode() : (Node)node; Node from_assertion = negated? from.notNode() : (Node) from; - if (proofRecipe) { - Debug("pf::sat") << "TseitinCnfStream::convertAndAssert: setting proof recipe" << std::endl; - proofRecipe->dump("pf::sat"); - d_cnfProof->setProofRecipe(proofRecipe); - } - if (proof_id != RULE_INVALID) { d_cnfProof->pushCurrentAssertion(from.isNull() ? assertion : from_assertion); d_cnfProof->registerAssertion(from.isNull() ? assertion : from_assertion, proof_id); diff --git a/src/prop/cnf_stream.h b/src/prop/cnf_stream.h index 6cc10d878..661108dd0 100644 --- a/src/prop/cnf_stream.h +++ b/src/prop/cnf_stream.h @@ -37,8 +37,6 @@ namespace CVC4 { -class LemmaProofRecipe; - namespace prop { class PropEngine; @@ -210,9 +208,8 @@ public: * @param node node to convert and assert * @param removable whether the sat solver can choose to remove the clauses * @param negated whether we are asserting the node negated - * @param proofRecipe contains the proof recipe for proving this node */ - virtual void convertAndAssert(TNode node, bool removable, bool negated, ProofRule proof_id, TNode from = TNode::null(), LemmaProofRecipe* proofRecipe = NULL) = 0; + virtual void convertAndAssert(TNode node, bool removable, bool negated, ProofRule proof_id, TNode from = TNode::null()) = 0; /** * Get the node that is represented by the given SatLiteral. @@ -282,8 +279,7 @@ public: * @param negated true if negated */ void convertAndAssert(TNode node, bool removable, - bool negated, ProofRule rule, TNode from = TNode::null(), - LemmaProofRecipe* proofRecipe = NULL); + bool negated, ProofRule rule, TNode from = TNode::null()); /** * Constructs the stream to use the given sat solver. diff --git a/src/prop/prop_engine.cpp b/src/prop/prop_engine.cpp index eb607e901..6cdf17f30 100644 --- a/src/prop/prop_engine.cpp +++ b/src/prop/prop_engine.cpp @@ -132,13 +132,12 @@ void PropEngine::assertFormula(TNode node) { void PropEngine::assertLemma(TNode node, bool negated, bool removable, ProofRule rule, - LemmaProofRecipe* proofRecipe, TNode from) { //Assert(d_inCheckSat, "Sat solver should be in solve()!"); Debug("prop::lemmas") << "assertLemma(" << node << ")" << endl; // Assert as (possibly) removable - d_cnfStream->convertAndAssert(node, removable, negated, rule, from, proofRecipe); + d_cnfStream->convertAndAssert(node, removable, negated, rule, from); } void PropEngine::requirePhase(TNode n, bool phase) { diff --git a/src/prop/prop_engine.h b/src/prop/prop_engine.h index c02015931..f966def26 100644 --- a/src/prop/prop_engine.h +++ b/src/prop/prop_engine.h @@ -37,7 +37,6 @@ namespace CVC4 { class ResourceManager; class DecisionEngine; class TheoryEngine; -class LemmaProofRecipe; namespace theory { class TheoryRegistrar; @@ -135,7 +134,7 @@ public: * @param removable whether this lemma can be quietly removed based * on an activity heuristic (or not) */ - void assertLemma(TNode node, bool negated, bool removable, ProofRule rule, LemmaProofRecipe* proofRecipe, TNode from = TNode::null()); + void assertLemma(TNode node, bool negated, bool removable, ProofRule rule, TNode from = TNode::null()); /** * If ever n is decided upon, it must be in the given phase. This diff --git a/src/prop/theory_proxy.cpp b/src/prop/theory_proxy.cpp index 6e8f1fbbf..5f5eac733 100644 --- a/src/prop/theory_proxy.cpp +++ b/src/prop/theory_proxy.cpp @@ -181,8 +181,7 @@ void TheoryProxy::notifyRestart() { Debug("shared") << "=) " << asNode << std::endl; } - LemmaProofRecipe* noProofRecipe = NULL; - d_propEngine->assertLemma(d_theoryEngine->preprocess(asNode), false, true, RULE_INVALID, noProofRecipe); + d_propEngine->assertLemma(d_theoryEngine->preprocess(asNode), false, true, RULE_INVALID); } else { Debug("shared") << "=(" << asNode << std::endl; } diff --git a/src/smt/dump.cpp b/src/smt/dump.cpp index eee7b901a..dc1ef792d 100644 --- a/src/smt/dump.cpp +++ b/src/smt/dump.cpp @@ -32,7 +32,9 @@ std::ostream* DumpC::getStreamPointer() { return ::CVC4::DumpOutChannel.getStrea void DumpC::setDumpFromString(const std::string& optarg) { #ifdef CVC4_DUMPING - char* optargPtr = strdup(optarg.c_str()); + // Make a copy of optarg for strtok_r to use. + std::string optargCopy = optarg; + char* optargPtr = const_cast<char*>(optargCopy.c_str()); char* tokstr = optargPtr; char* toksave; while((optargPtr = strtok_r(tokstr, ",", &toksave)) != NULL) { @@ -130,7 +132,6 @@ void DumpC::setDumpFromString(const std::string& optarg) { } } } - free(optargPtr); #else /* CVC4_DUMPING */ throw OptionException("The dumping feature was disabled in this build of CVC4."); #endif /* CVC4_DUMPING */ diff --git a/src/smt/model.h b/src/smt/model.h index 768cb3e6a..fd31655f4 100644 --- a/src/smt/model.h +++ b/src/smt/model.h @@ -58,7 +58,6 @@ public: const SmtEngine* getSmtEngine() const { return &d_smt; } /** get the input name (file name, etc.) this model is associated to */ std::string getInputName() const { return d_inputName; } - public: /** Check whether this expr is a don't-care in the model */ virtual bool isDontCare(Expr expr) const { return false; } @@ -66,6 +65,10 @@ public: virtual Expr getValue(Expr expr) const = 0; /** get cardinality for sort */ virtual Cardinality getCardinality(Type t) const = 0; + /** print comments */ + virtual void getComments(std::ostream& out) const {} + /** get heap model (for separation logic) */ + virtual bool getHeapModel( Expr& h, Expr& ne ) const { return false; } };/* class Model */ class ModelBuilder { diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index 08495c936..32c44d224 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -57,6 +57,7 @@ #include "options/open_ostream.h" #include "options/option_exception.h" #include "options/printer_options.h" +#include "options/proof_options.h" #include "options/prop_options.h" #include "options/quantifiers_options.h" #include "options/set_language.h" @@ -92,9 +93,9 @@ #include "theory/quantifiers/fun_def_process.h" #include "theory/quantifiers/macros.h" #include "theory/quantifiers/quantifiers_rewriter.h" +#include "theory/sep/theory_sep.h" #include "theory/sort_inference.h" #include "theory/strings/theory_strings.h" -#include "theory/sep/theory_sep.h" #include "theory/substitutions.h" #include "theory/theory_engine.h" #include "theory/theory_model.h" @@ -1349,8 +1350,8 @@ void SmtEngine::setDefaults() { Trace("smt") << "turning on quantifier logic, for strings-exp" << std::endl; } - if(! options::fmfBoundInt.wasSetByUser()) { - options::fmfBoundInt.set( true ); + if(! options::fmfBound.wasSetByUser()) { + options::fmfBound.set( true ); Trace("smt") << "turning on fmf-bound-int, for strings-exp" << std::endl; } if(! options::fmfInstEngine.wasSetByUser()) { @@ -1752,12 +1753,13 @@ void SmtEngine::setDefaults() { options::cbqi.set(false); } - if(options::fmfBoundIntLazy.wasSetByUser() && options::fmfBoundIntLazy()) { - options::fmfBoundInt.set( true ); + if( ( options::fmfBoundLazy.wasSetByUser() && options::fmfBoundLazy() ) || + ( options::fmfBoundInt.wasSetByUser() && options::fmfBoundInt() ) ) { + options::fmfBound.set( true ); } //now have determined whether fmfBoundInt is on/off //apply fmfBoundInt options - if( options::fmfBoundInt() ){ + if( options::fmfBound() ){ //must have finite model finding on options::finiteModelFind.set( true ); if( ! options::mbqiMode.wasSetByUser() || @@ -3986,15 +3988,6 @@ void SmtEnginePrivate::processAssertions() { dumpAssertions("post-bv-to-bool", d_assertions); Trace("smt") << "POST bvToBool" << endl; } - if( d_smt.d_logic.isTheoryEnabled(THEORY_STRINGS) ) { - Trace("smt-proc") << "SmtEnginePrivate::processAssertions() : pre-strings-preprocess" << endl; - dumpAssertions("pre-strings-pp", d_assertions); - if( !options::stringLazyPreproc() ){ - ((theory::strings::TheoryStrings*)d_smt.d_theoryEngine->theoryOf(THEORY_STRINGS))->getPreprocess()->simplify( d_assertions.ref() ); - } - Trace("smt-proc") << "SmtEnginePrivate::processAssertions() : post-strings-preprocess" << endl; - dumpAssertions("post-strings-pp", d_assertions); - } if( d_smt.d_logic.isTheoryEnabled(THEORY_SEP) ) { //separation logic solver needs to register the entire input ((theory::sep::TheorySep*)d_smt.d_theoryEngine->theoryOf(THEORY_SEP))->processAssertions( d_assertions.ref() ); @@ -4292,8 +4285,10 @@ void SmtEnginePrivate::addFormula(TNode n, bool inUnsatCore, bool inInput) PROOF( if( inInput ){ // n is an input assertion - if (inUnsatCore || options::dumpUnsatCores() || options::checkUnsatCores()) + if (inUnsatCore || options::dumpUnsatCores() || options::checkUnsatCores() || options::fewerPreprocessingHoles()) { + ProofManager::currentPM()->addCoreAssertion(n.toExpr()); + } }else{ // n is the result of an unknown preprocessing step, add it to dependency map to null ProofManager::currentPM()->addDependence(n, Node::null()); diff --git a/src/theory/arrays/array_proof_reconstruction.cpp b/src/theory/arrays/array_proof_reconstruction.cpp index 6dfd14157..8dd7fe782 100644 --- a/src/theory/arrays/array_proof_reconstruction.cpp +++ b/src/theory/arrays/array_proof_reconstruction.cpp @@ -60,7 +60,7 @@ void ArrayProofReconstruction::notify(unsigned reasonType, Node reason, Node a, // or ((a[i]:=t)[k] == a[k]) because (i != k). if (proof) { - if (a.getNumChildren() == 2) { + if (a.getKind() == kind::SELECT) { // This is the case of ((a[i]:=t)[k] == a[k]) because (i != k). // The edge is ((a[i]:=t)[k], a[k]), or (a[k], (a[i]:=t)[k]). This flag should be @@ -156,7 +156,7 @@ void ArrayProofReconstruction::notify(unsigned reasonType, Node reason, Node a, proof->d_children.push_back(childProof); } else { - // This is the case of (i == k) because ((a[i]:=t)[k] != a[k]), + // This is the case of (i == k) because ((a[i]:=t)[k] != a[k]), Node indexOne = a; Node indexTwo = b; diff --git a/src/theory/bv/bv_subtheory_core.h b/src/theory/bv/bv_subtheory_core.h index 643093327..93a938cc0 100644 --- a/src/theory/bv/bv_subtheory_core.h +++ b/src/theory/bv/bv_subtheory_core.h @@ -117,6 +117,7 @@ public: bool hasTerm(TNode node) const { return d_equalityEngine.hasTerm(node); } void addTermToEqualityEngine(TNode node) { d_equalityEngine.addTerm(node); } void enableSlicer(); + eq::EqualityEngine * getEqualityEngine() { return &d_equalityEngine; } }; diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp index fec93e033..f0981044b 100644 --- a/src/theory/bv/theory_bv.cpp +++ b/src/theory/bv/theory_bv.cpp @@ -495,7 +495,36 @@ void TheoryBV::propagate(Effort e) { } } +eq::EqualityEngine * TheoryBV::getEqualityEngine() { + return NULL; +} +bool TheoryBV::getCurrentSubstitution( int effort, std::vector< Node >& vars, std::vector< Node >& subs, std::map< Node, std::vector< Node > >& exp ) { +#if 0 + CoreSolver* core = (CoreSolver*)d_subtheoryMap[SUB_CORE]; + if( core ){ + //get the constant equivalence classes + bool retVal = false; + for( unsigned i=0; i<vars.size(); i++ ){ + Node n = vars[i]; + if( core->getEqualityEngine()->hasTerm( n ) ){ + Node nr = core->getEqualityEngine()->getRepresenative( n ); + if( nr.isConst() ){ + subs.push_back( nr ); + exp[n].push_back( n.eqNode( nr ) ); + retVal = true; + }else{ + subs.push_back( n ); + } + } + } + //return true if the substitution is non-trivial + return retVal; + } +#endif + return false; +} + Theory::PPAssertStatus TheoryBV::ppAssert(TNode in, SubstitutionMap& outSubstitutions) { switch(in.getKind()) { case kind::EQUAL: diff --git a/src/theory/bv/theory_bv.h b/src/theory/bv/theory_bv.h index ba2a4fc2a..0709ca427 100644 --- a/src/theory/bv/theory_bv.h +++ b/src/theory/bv/theory_bv.h @@ -79,6 +79,10 @@ public: std::string identify() const { return std::string("TheoryBV"); } + /** equality engine */ + eq::EqualityEngine * getEqualityEngine(); + bool getCurrentSubstitution( int effort, std::vector< Node >& vars, std::vector< Node >& subs, std::map< Node, std::vector< Node > >& exp ); + PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions); void enableCoreTheorySlicer(); diff --git a/src/theory/datatypes/theory_datatypes.cpp b/src/theory/datatypes/theory_datatypes.cpp index a2f995935..59b9f1d96 100644 --- a/src/theory/datatypes/theory_datatypes.cpp +++ b/src/theory/datatypes/theory_datatypes.cpp @@ -865,6 +865,11 @@ void TheoryDatatypes::merge( Node t1, Node t2 ){ } //d_consEqc[t1] = true; } + //AJR: do this? + //else if( cons2.isConst() ){ + // //prefer the constant + // eqc1->d_constructor = cons2; + //} //d_consEqc[t2] = false; } }else{ @@ -2090,6 +2095,9 @@ TNode TheoryDatatypes::getRepresentative( TNode a ){ } } +bool TheoryDatatypes::getCurrentSubstitution( int effort, std::vector< Node >& vars, std::vector< Node >& subs, std::map< Node, std::vector< Node > >& exp ) { + return false; +} void TheoryDatatypes::printModelDebug( const char* c ){ if(! (Trace.isOn(c))) { diff --git a/src/theory/datatypes/theory_datatypes.h b/src/theory/datatypes/theory_datatypes.h index 5722e7822..49c45590c 100644 --- a/src/theory/datatypes/theory_datatypes.h +++ b/src/theory/datatypes/theory_datatypes.h @@ -266,6 +266,9 @@ public: void collectModelInfo( TheoryModel* m, bool fullModel ); void shutdown() { } std::string identify() const { return std::string("TheoryDatatypes"); } + /** equality engine */ + eq::EqualityEngine * getEqualityEngine() { return &d_equalityEngine; } + bool getCurrentSubstitution( int effort, std::vector< Node >& vars, std::vector< Node >& subs, std::map< Node, std::vector< Node > >& exp ); /** debug print */ void printModelDebug( const char* c ); /** entailment check */ @@ -313,9 +316,6 @@ private: bool areEqual( TNode a, TNode b ); bool areDisequal( TNode a, TNode b ); TNode getRepresentative( TNode a ); -public: - /** get equality engine */ - eq::EqualityEngine* getEqualityEngine() { return &d_equalityEngine; } };/* class TheoryDatatypes */ }/* CVC4::theory::datatypes namespace */ diff --git a/src/theory/quantifiers/bounded_integers.cpp b/src/theory/quantifiers/bounded_integers.cpp index 7184624da..54853ceaf 100755 --- a/src/theory/quantifiers/bounded_integers.cpp +++ b/src/theory/quantifiers/bounded_integers.cpp @@ -20,6 +20,7 @@ #include "theory/quantifiers/model_engine.h" #include "theory/quantifiers/quant_util.h" #include "theory/quantifiers/term_database.h" +#include "theory/theory_engine.h" using namespace CVC4; using namespace std; @@ -30,7 +31,7 @@ using namespace CVC4::kind; 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() ){ + if( options::fmfBoundLazy() ){ d_proxy_range = isProxy ? r : NodeManager::currentNM()->mkSkolem( "pbir", r.getType() ); }else{ d_proxy_range = r; @@ -232,14 +233,12 @@ void BoundedIntegers::processLiteral( Node q, Node lit, bool pol, } }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; } @@ -330,6 +329,7 @@ void BoundedIntegers::registerQuantifier( Node f ) { setBoundedVar( f, v, BOUND_SET_MEMBER ); setBoundVar = true; d_setm_range[f][v] = bound_lit_map[0][v][1]; + d_setm_range_lit[f][v] = bound_lit_map[0][v]; Trace("bound-int") << "Variable " << v << " is bound because of set membership literal " << bound_lit_map[0][v] << std::endl; } if( setBoundVar ){ @@ -515,6 +515,63 @@ Node BoundedIntegers::getSetRangeValue( Node q, Node v, RepSetIterator * rsi ) { 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; + //map to term model + if( sr.getKind()!=EMPTYSET ){ + std::map< Node, Node > val_to_term; + while( sr.getKind()==UNION ){ + Assert( sr[1].getKind()==kind::SINGLETON ); + val_to_term[ sr[1][0] ] = sr[1][0]; + sr = sr[0]; + } + Assert( sr.getKind()==kind::SINGLETON ); + val_to_term[ sr[0] ] = sr[0]; + //must look back at assertions, not term database (theory of sets introduces extraneous terms internally) + Theory* theory = d_quantEngine->getTheoryEngine()->theoryOf( THEORY_SETS ); + if( theory ){ + context::CDList<Assertion>::const_iterator it = theory->facts_begin(), it_end = theory->facts_end(); + for( unsigned i = 0; it != it_end; ++ it, ++i ){ + Node lit = (*it).assertion; + if( lit.getKind()==kind::MEMBER ){ + Node vr = d_quantEngine->getModel()->getCurrentModelValue( lit[0] ); + Trace("bound-int-rsi-debug") << "....membership for " << lit << " ==> " << vr << std::endl; + Trace("bound-int-rsi-debug") << " " << (val_to_term.find( vr )!=val_to_term.end()) << " " << d_quantEngine->getEqualityQuery()->areEqual( d_setm_range_lit[q][v][1], lit[1] ) << std::endl; + if( val_to_term.find( vr )!=val_to_term.end() ){ + if( d_quantEngine->getEqualityQuery()->areEqual( d_setm_range_lit[q][v][1], lit[1] ) ){ + Trace("bound-int-rsi") << " Map value to term : " << vr << " -> " << lit[0] << std::endl; + val_to_term[ vr ] = lit[0]; + } + } + } + } + } + //rebuild value + Node nsr; + for( std::map< Node, Node >::iterator it = val_to_term.begin(); it != val_to_term.end(); ++it ){ + Node nv = NodeManager::currentNM()->mkNode( kind::SINGLETON, it->second ); + if( nsr.isNull() ){ + nsr = nv; + }else{ + nsr = NodeManager::currentNM()->mkNode( kind::UNION, nsr, nv ); + } + } + Trace("bound-int-rsi") << "...reconstructed " << nsr << std::endl; + return nsr; + + /* + Node lit = d_setm_range_lit[q][v]; + Trace("bound-int-rsi-debug") << "Bounded from lit " << lit << std::endl; + Node f = d_quantEngine->getTermDatabase()->getMatchOperator( lit ); + TermArgTrie * ta = d_quantEngine->getTermDatabase()->getTermArgTrie( f ); + if( ta ){ + Trace("bound-int-rsi-debug") << "Got term index for " << f << std::endl; + for( std::map< TNode, TermArgTrie >::iterator it = ta->d_data.begin(); it != ta->d_data.end(); ++it ){ + + } + + } + */ + } + } return sr; } diff --git a/src/theory/quantifiers/bounded_integers.h b/src/theory/quantifiers/bounded_integers.h index ab4bcba96..c3fb05641 100755 --- a/src/theory/quantifiers/bounded_integers.h +++ b/src/theory/quantifiers/bounded_integers.h @@ -60,6 +60,7 @@ private: std::map< Node, std::map< Node, Node > > d_nground_range; //set membership range std::map< Node, std::map< Node, Node > > d_setm_range; + std::map< Node, std::map< Node, Node > > d_setm_range_lit; void hasFreeVar( Node f, Node n ); void process( Node f, Node n, bool pol, std::map< Node, unsigned >& bound_lit_type_map, diff --git a/src/theory/quantifiers/ce_guided_single_inv.cpp b/src/theory/quantifiers/ce_guided_single_inv.cpp index 3177739ac..981abea94 100755 --- a/src/theory/quantifiers/ce_guided_single_inv.cpp +++ b/src/theory/quantifiers/ce_guided_single_inv.cpp @@ -658,17 +658,22 @@ bool CegConjectureSingleInv::check( std::vector< Node >& lems ) { } } -Node CegConjectureSingleInv::constructSolution( std::vector< unsigned >& indices, unsigned i, unsigned index ) { +Node CegConjectureSingleInv::constructSolution( std::vector< unsigned >& indices, unsigned i, unsigned index, std::map< Node, Node >& weak_imp ) { Assert( index<d_inst.size() ); Assert( i<d_inst[index].size() ); unsigned uindex = indices[index]; - if( index==d_inst.size()-1 ){ + if( index==indices.size()-1 ){ return d_inst[uindex][i]; }else{ Node cond = d_lemmas_produced[uindex]; + //weaken based on unsat core + std::map< Node, Node >::iterator itw = weak_imp.find( cond ); + if( itw!=weak_imp.end() ){ + cond = itw->second; + } cond = TermDb::simpleNegate( cond ); Node ite1 = d_inst[uindex][i]; - Node ite2 = constructSolution( indices, i, index+1 ); + Node ite2 = constructSolution( indices, i, index+1, weak_imp ); return NodeManager::currentNM()->mkNode( ITE, cond, ite1, ite2 ); } } @@ -753,12 +758,35 @@ Node CegConjectureSingleInv::getSolution( unsigned sol_index, TypeNode stn, int& //construct the solution Trace("csi-sol") << "Sort solution return values " << sol_index << std::endl; + bool useUnsatCore = false; + std::vector< Node > active_lemmas; + //minimize based on unsat core, if possible + std::map< Node, Node > weak_imp; + if( options::cegqiSolMinCore() ){ + if( options::cegqiSolMinInst() ){ + if( d_qe->getUnsatCoreLemmas( active_lemmas, weak_imp ) ){ + useUnsatCore = true; + } + }else{ + if( d_qe->getUnsatCoreLemmas( active_lemmas ) ){ + useUnsatCore = true; + } + } + } Assert( d_lemmas_produced.size()==d_inst.size() ); std::vector< unsigned > indices; for( unsigned i=0; i<d_lemmas_produced.size(); i++ ){ - Assert( sol_index<d_inst[i].size() ); - indices.push_back( i ); + bool incl = true; + if( useUnsatCore ){ + incl = std::find( active_lemmas.begin(), active_lemmas.end(), d_lemmas_produced[i] )!=active_lemmas.end(); + } + if( incl ){ + Assert( sol_index<d_inst[i].size() ); + indices.push_back( i ); + } } + Trace("csi-sol") << "...included " << indices.size() << " / " << d_lemmas_produced.size() << " instantiations." << std::endl; + Assert( !indices.empty() ); //sort indices based on heuristic : currently, do all constant returns first (leads to simpler conditions) // TODO : to minimize solution size, put the largest term last sortSiInstanceIndices ssii; @@ -766,7 +794,7 @@ Node CegConjectureSingleInv::getSolution( unsigned sol_index, TypeNode stn, int& ssii.d_i = sol_index; std::sort( indices.begin(), indices.end(), ssii ); Trace("csi-sol") << "Construct solution" << std::endl; - s = constructSolution( indices, sol_index, 0 ); + s = constructSolution( indices, sol_index, 0, weak_imp ); 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() ); } diff --git a/src/theory/quantifiers/ce_guided_single_inv.h b/src/theory/quantifiers/ce_guided_single_inv.h index 4d2f9a0e5..feadeca39 100755 --- a/src/theory/quantifiers/ce_guided_single_inv.h +++ b/src/theory/quantifiers/ce_guided_single_inv.h @@ -74,7 +74,7 @@ private: std::map< Node, std::vector< Node > >& teq, Node n, std::vector< Node >& conj ); //constructing solution - Node constructSolution( std::vector< unsigned >& indices, unsigned i, unsigned index ); + Node constructSolution( std::vector< unsigned >& indices, unsigned i, unsigned index, std::map< Node, Node >& weak_imp ); Node postProcessSolution( Node n ); private: //list of skolems for each argument of programs diff --git a/src/theory/quantifiers/ceg_instantiator.cpp b/src/theory/quantifiers/ceg_instantiator.cpp index cd263e90c..0fe4b98c7 100755 --- a/src/theory/quantifiers/ceg_instantiator.cpp +++ b/src/theory/quantifiers/ceg_instantiator.cpp @@ -749,6 +749,7 @@ bool CegInstantiator::doAddInstantiationInc( Node n, Node pv, Node pv_coeff, int Trace("cbqi-inst") << pv_coeff << " * "; } Trace("cbqi-inst") << pv << " -> " << n << std::endl; + Assert( n.getType().isSubtypeOf( pv.getType() ) ); } //must ensure variables have been computed for n computeProgVars( n ); @@ -772,6 +773,7 @@ bool CegInstantiator::doAddInstantiationInc( Node n, Node pv, Node pv_coeff, int std::vector< Node > new_has_coeff; Trace("cbqi-inst-debug2") << "Applying substitutions..." << std::endl; for( unsigned j=0; j<sf.d_subs.size(); j++ ){ + Trace("cbqi-inst-debug2") << " Apply for " << sf.d_subs[j] << std::endl; Assert( d_prog_var.find( sf.d_subs[j] )!=d_prog_var.end() ); if( d_prog_var[sf.d_subs[j]].find( pv )!=d_prog_var[sf.d_subs[j]].end() ){ prev_subs[j] = sf.d_subs[j]; @@ -1629,10 +1631,11 @@ int CegInstantiator::solve_arith( Node pv, Node atom, Node& veq_c, Node& val, No real_part.push_back( msum[it->first].isNull() ? it->first : NodeManager::currentNM()->mkNode( MULT, msum[it->first], it->first ) ); } } - for( unsigned t=0; t<2; t++ ){ - if( !vts_coeff[t].isNull() ){ - vts_coeff[t] = Rewriter::rewrite( NodeManager::currentNM()->mkNode( MULT, rcoeff, vts_coeff[t] ) ); - } + //remove delta TODO: check this + vts_coeff[1] = Node::null(); + //multiply inf + if( !vts_coeff[0].isNull() ){ + vts_coeff[0] = Rewriter::rewrite( NodeManager::currentNM()->mkNode( MULT, rcoeff, vts_coeff[0] ) ); } realPart = real_part.empty() ? d_zero : ( real_part.size()==1 ? real_part[0] : NodeManager::currentNM()->mkNode( PLUS, real_part ) ); Assert( d_out->isEligibleForInstantiation( realPart ) ); @@ -1645,7 +1648,7 @@ int CegInstantiator::solve_arith( Node pv, Node atom, Node& veq_c, Node& val, No int ires_use = ( msum[pv].isNull() || msum[pv].getConst<Rational>().sgn()==1 ) ? 1 : -1; val = Rewriter::rewrite( NodeManager::currentNM()->mkNode( ires_use==-1 ? PLUS : MINUS, NodeManager::currentNM()->mkNode( ires_use==-1 ? MINUS : PLUS, val, realPart ), - NodeManager::currentNM()->mkNode( TO_INTEGER, realPart ) ) ); + NodeManager::currentNM()->mkNode( TO_INTEGER, realPart ) ) ); //TODO: round up for upper bounds? Trace("cbqi-inst-debug") << "result : " << val << std::endl; Assert( val.getType().isInteger() ); } diff --git a/src/theory/quantifiers/full_model_check.cpp b/src/theory/quantifiers/full_model_check.cpp index a0665cb7f..be608aeaa 100755 --- a/src/theory/quantifiers/full_model_check.cpp +++ b/src/theory/quantifiers/full_model_check.cpp @@ -668,7 +668,7 @@ bool FullModelChecker::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, } } if( addInst ){ - if( options::fmfBoundInt() ){ + if( options::fmfBound() ){ std::vector< Node > cond; cond.push_back(d_quant_cond[f]); cond.insert( cond.end(), inst.begin(), inst.end() ); diff --git a/src/theory/quantifiers/inst_match.cpp b/src/theory/quantifiers/inst_match.cpp index 8818175db..7e5424d9c 100755 --- a/src/theory/quantifiers/inst_match.cpp +++ b/src/theory/quantifiers/inst_match.cpp @@ -199,31 +199,90 @@ bool InstMatchTrie::removeInstMatch( QuantifiersEngine* qe, Node q, std::vector< } } -void InstMatchTrie::print( std::ostream& out, Node q, std::vector< TNode >& terms ) const { +bool InstMatchTrie::recordInstLemma( Node q, std::vector< Node >& m, Node lem, ImtIndexOrder* imtio, int index ){ + if( index==(int)q[0].getNumChildren() || ( imtio && index==(int)imtio->d_order.size() ) ){ + setInstLemma( lem ); + return true; + }else{ + int i_index = imtio ? imtio->d_order[index] : index; + std::map< Node, InstMatchTrie >::iterator it = d_data.find( m[i_index] ); + if( it!=d_data.end() ){ + return it->second.recordInstLemma( q, m, lem, imtio, index+1 ); + }else{ + return false; + } + } +} + +void InstMatchTrie::print( std::ostream& out, Node q, std::vector< TNode >& terms, bool& firstTime, bool useActive, std::vector< Node >& active ) const { if( terms.size()==q[0].getNumChildren() ){ - out << " ( "; - for( unsigned i=0; i<terms.size(); i++ ){ - if( i>0 ){ out << ", ";} - out << terms[i]; + bool print; + if( useActive ){ + if( hasInstLemma() ){ + Node lem = getInstLemma(); + print = std::find( active.begin(), active.end(), lem )!=active.end(); + }else{ + print = false; + } + }else{ + print = true; + } + if( print ){ + if( firstTime ){ + out << "(instantiation " << q << std::endl; + firstTime = false; + } + out << " ( "; + for( unsigned i=0; i<terms.size(); i++ ){ + if( i>0 ){ out << ", ";} + out << terms[i]; + } + out << " )" << std::endl; } - out << " )" << std::endl; }else{ for( std::map< Node, InstMatchTrie >::const_iterator it = d_data.begin(); it != d_data.end(); ++it ){ terms.push_back( it->first ); - it->second.print( out, q, terms ); + it->second.print( out, q, terms, firstTime, useActive, active ); terms.pop_back(); } } } -void InstMatchTrie::getInstantiations( std::vector< Node >& insts, Node q, std::vector< Node >& terms, QuantifiersEngine * qe ) const { +void InstMatchTrie::getInstantiations( std::vector< Node >& insts, Node q, std::vector< Node >& terms, QuantifiersEngine * qe, bool useActive, std::vector< Node >& active ) const { if( terms.size()==q[0].getNumChildren() ){ - //insts.push_back( q[1].substitute( vars.begin(), vars.end(), terms.begin(), terms.end() ) ); - insts.push_back( qe->getInstantiation( q, terms, true ) ); + if( useActive ){ + if( hasInstLemma() ){ + Node lem = getInstLemma(); + if( std::find( active.begin(), active.end(), lem )!=active.end() ){ + insts.push_back( lem ); + } + } + }else{ + insts.push_back( qe->getInstantiation( q, terms, true ) ); + } }else{ for( std::map< Node, InstMatchTrie >::const_iterator it = d_data.begin(); it != d_data.end(); ++it ){ terms.push_back( it->first ); - it->second.getInstantiations( insts, q, terms, qe ); + it->second.getInstantiations( insts, q, terms, qe, useActive, active ); + terms.pop_back(); + } + } +} + +void InstMatchTrie::getExplanationForInstLemmas( Node q, std::vector< Node >& terms, std::vector< Node >& lems, std::map< Node, Node >& quant, std::map< Node, std::vector< Node > >& tvec ) const { + if( terms.size()==q[0].getNumChildren() ){ + if( hasInstLemma() ){ + Node lem = getInstLemma(); + if( std::find( lems.begin(), lems.end(), lem )!=lems.end() ){ + quant[lem] = q; + tvec[lem].clear(); + tvec[lem].insert( tvec[lem].end(), terms.begin(), terms.end() ); + } + } + }else{ + for( std::map< Node, InstMatchTrie >::const_iterator it = d_data.begin(); it != d_data.end(); ++it ){ + terms.push_back( it->first ); + it->second.getExplanationForInstLemmas( q, terms, lems, quant, tvec ); terms.pop_back(); } } @@ -301,8 +360,7 @@ bool CDInstMatchTrie::removeInstMatch( QuantifiersEngine* qe, Node q, std::vecto return false; } }else{ - Node n = m[index]; - std::map< Node, CDInstMatchTrie* >::iterator it = d_data.find( n ); + std::map< Node, CDInstMatchTrie* >::iterator it = d_data.find( m[index] ); if( it!=d_data.end() ){ return it->second->removeInstMatch( qe, q, m, index+1 ); }else{ @@ -311,34 +369,99 @@ bool CDInstMatchTrie::removeInstMatch( QuantifiersEngine* qe, Node q, std::vecto } } -void CDInstMatchTrie::print( std::ostream& out, Node q, std::vector< TNode >& terms ) const{ +bool CDInstMatchTrie::recordInstLemma( Node q, std::vector< Node >& m, Node lem, int index ) { + if( index==(int)q[0].getNumChildren() ){ + if( d_valid.get() ){ + setInstLemma( lem ); + return true; + }else{ + return false; + } + }else{ + std::map< Node, CDInstMatchTrie* >::iterator it = d_data.find( m[index] ); + if( it!=d_data.end() ){ + return it->second->recordInstLemma( q, m, lem, index+1 ); + }else{ + return false; + } + } +} + +void CDInstMatchTrie::print( std::ostream& out, Node q, std::vector< TNode >& terms, bool& firstTime, bool useActive, std::vector< Node >& active ) const{ if( d_valid.get() ){ if( terms.size()==q[0].getNumChildren() ){ - out << " ( "; - for( unsigned i=0; i<terms.size(); i++ ){ - if( i>0 ) out << ", "; - out << terms[i]; + bool print; + if( useActive ){ + if( hasInstLemma() ){ + Node lem = getInstLemma(); + print = std::find( active.begin(), active.end(), lem )!=active.end(); + }else{ + print = false; + } + }else{ + print = true; + } + if( print ){ + if( firstTime ){ + out << "(instantiation " << q << std::endl; + firstTime = false; + } + out << " ( "; + for( unsigned i=0; i<terms.size(); i++ ){ + if( i>0 ) out << " "; + out << terms[i]; + } + out << " )" << std::endl; } - out << " )" << std::endl; }else{ for( std::map< Node, CDInstMatchTrie* >::const_iterator it = d_data.begin(); it != d_data.end(); ++it ){ terms.push_back( it->first ); - it->second->print( out, q, terms ); + it->second->print( out, q, terms, firstTime, useActive, active ); terms.pop_back(); } } } } -void CDInstMatchTrie::getInstantiations( std::vector< Node >& insts, Node q, std::vector< Node >& terms, QuantifiersEngine * qe ) const{ +void CDInstMatchTrie::getInstantiations( std::vector< Node >& insts, Node q, std::vector< Node >& terms, QuantifiersEngine * qe, bool useActive, std::vector< Node >& active ) const{ if( d_valid.get() ){ if( terms.size()==q[0].getNumChildren() ){ - //insts.push_back( q[1].substitute( vars.begin(), vars.end(), terms.begin(), terms.end() ) ); - insts.push_back( qe->getInstantiation( q, terms, true ) ); + if( useActive ){ + if( hasInstLemma() ){ + Node lem; + if( std::find( active.begin(), active.end(), lem )!=active.end() ){ + insts.push_back( lem ); + } + } + }else{ + insts.push_back( qe->getInstantiation( q, terms, true ) ); + } + }else{ + for( std::map< Node, CDInstMatchTrie* >::const_iterator it = d_data.begin(); it != d_data.end(); ++it ){ + terms.push_back( it->first ); + it->second->getInstantiations( insts, q, terms, qe, useActive, active ); + terms.pop_back(); + } + } + } +} + + +void CDInstMatchTrie::getExplanationForInstLemmas( Node q, std::vector< Node >& terms, std::vector< Node >& lems, std::map< Node, Node >& quant, std::map< Node, std::vector< Node > >& tvec ) const { + if( d_valid.get() ){ + if( terms.size()==q[0].getNumChildren() ){ + if( hasInstLemma() ){ + Node lem; + if( std::find( lems.begin(), lems.end(), lem )!=lems.end() ){ + quant[lem] = q; + tvec[lem].clear(); + tvec[lem].insert( tvec[lem].end(), terms.begin(), terms.end() ); + } + } }else{ for( std::map< Node, CDInstMatchTrie* >::const_iterator it = d_data.begin(); it != d_data.end(); ++it ){ terms.push_back( it->first ); - it->second->getInstantiations( insts, q, terms, qe ); + it->second->getExplanationForInstLemmas( q, terms, lems, quant, tvec ); terms.pop_back(); } } diff --git a/src/theory/quantifiers/inst_match.h b/src/theory/quantifiers/inst_match.h index ad287c1a3..68446922f 100755 --- a/src/theory/quantifiers/inst_match.h +++ b/src/theory/quantifiers/inst_match.h @@ -96,12 +96,19 @@ public: public: std::vector< int > d_order; };/* class InstMatchTrie ImtIndexOrder */ - /** the data */ std::map< Node, InstMatchTrie > d_data; private: - void print( std::ostream& out, Node q, std::vector< TNode >& terms ) const; - void getInstantiations( std::vector< Node >& insts, Node q, std::vector< Node >& terms, QuantifiersEngine * qe ) const; + void print( std::ostream& out, Node q, std::vector< TNode >& terms, bool& firstTime, bool useActive, std::vector< Node >& active ) const; + void getInstantiations( std::vector< Node >& insts, Node q, std::vector< Node >& terms, QuantifiersEngine * qe, bool useActive, std::vector< Node >& active ) const; + void getExplanationForInstLemmas( Node q, std::vector< Node >& terms, std::vector< Node >& lems, std::map< Node, Node >& quant, std::map< Node, std::vector< Node > >& tvec ) const; +private: + void setInstLemma( Node n ){ + d_data.clear(); + d_data[n].clear(); + } + bool hasInstLemma() const { return !d_data.empty(); } + Node getInstLemma() const { return d_data.begin()->first; } public: InstMatchTrie(){} ~InstMatchTrie(){} @@ -129,14 +136,19 @@ public: bool addInstMatch( QuantifiersEngine* qe, Node f, std::vector< Node >& m, bool modEq = false, ImtIndexOrder* imtio = NULL, bool onlyExist = false, int index = 0 ); bool removeInstMatch( QuantifiersEngine* qe, Node f, std::vector< Node >& m, ImtIndexOrder* imtio = NULL, int index = 0 ); - void print( std::ostream& out, Node q ) const{ + bool recordInstLemma( Node q, std::vector< Node >& m, Node lem, ImtIndexOrder* imtio = NULL, int index = 0 ); + void print( std::ostream& out, Node q, bool& firstTime, bool useActive, std::vector< Node >& active ) const{ std::vector< TNode > terms; - print( out, q, terms ); + print( out, q, terms, firstTime, useActive, active ); } - void getInstantiations( std::vector< Node >& insts, Node q, QuantifiersEngine * qe ) { + void getInstantiations( std::vector< Node >& insts, Node q, QuantifiersEngine * qe, bool useActive, std::vector< Node >& active ) { std::vector< Node > terms; - getInstantiations( insts, q, terms, qe ); + getInstantiations( insts, q, terms, qe, useActive, active ); } + void getExplanationForInstLemmas( Node q, std::vector< Node >& lems, std::map< Node, Node >& quant, std::map< Node, std::vector< Node > >& tvec ) const { + std::vector< Node > terms; + getExplanationForInstLemmas( q, terms, lems, quant, tvec ); + } void clear() { d_data.clear(); } };/* class InstMatchTrie */ @@ -147,9 +159,17 @@ private: std::map< Node, CDInstMatchTrie* > d_data; /** is valid */ context::CDO< bool > d_valid; - - void print( std::ostream& out, Node q, std::vector< TNode >& terms ) const; - void getInstantiations( std::vector< Node >& insts, Node q, std::vector< Node >& terms, QuantifiersEngine * qe ) const; +private: + void print( std::ostream& out, Node q, std::vector< TNode >& terms, bool& firstTime, bool useActive, std::vector< Node >& active ) const; + void getInstantiations( std::vector< Node >& insts, Node q, std::vector< Node >& terms, QuantifiersEngine * qe, bool useActive, std::vector< Node >& active ) const; + void getExplanationForInstLemmas( Node q, std::vector< Node >& terms, std::vector< Node >& lems, std::map< Node, Node >& quant, std::map< Node, std::vector< Node > >& tvec ) const; +private: + void setInstLemma( Node n ){ + d_data.clear(); + d_data[n] = NULL; + } + bool hasInstLemma() const { return !d_data.empty(); } + Node getInstLemma() const { return d_data.begin()->first; } public: CDInstMatchTrie( context::Context* c ) : d_valid( c, false ){} ~CDInstMatchTrie(); @@ -177,14 +197,19 @@ public: bool addInstMatch( QuantifiersEngine* qe, Node q, std::vector< Node >& m, context::Context* c, bool modEq = false, int index = 0, bool onlyExist = false ); bool removeInstMatch( QuantifiersEngine* qe, Node q, std::vector< Node >& m, int index = 0 ); - void print( std::ostream& out, Node q ) const{ + bool recordInstLemma( Node q, std::vector< Node >& m, Node lem, int index = 0 ); + void print( std::ostream& out, Node q, bool& firstTime, bool useActive, std::vector< Node >& active ) const{ std::vector< TNode > terms; - print( out, q, terms ); + print( out, q, terms, firstTime, useActive, active ); } - void getInstantiations( std::vector< Node >& insts, Node q, QuantifiersEngine * qe ) { + void getInstantiations( std::vector< Node >& insts, Node q, QuantifiersEngine * qe, bool useActive, std::vector< Node >& active ) { std::vector< Node > terms; - getInstantiations( insts, q, terms, qe ); + getInstantiations( insts, q, terms, qe, useActive, active ); } + void getExplanationForInstLemmas( Node q, std::vector< Node >& lems, std::map< Node, Node >& quant, std::map< Node, std::vector< Node > >& tvec ) const { + std::vector< Node > terms; + getExplanationForInstLemmas( q, terms, lems, quant, tvec ); + } };/* class CDInstMatchTrie */ diff --git a/src/theory/quantifiers/inst_match_generator.cpp b/src/theory/quantifiers/inst_match_generator.cpp index 2d3bf76f6..b3df9ca5d 100755 --- a/src/theory/quantifiers/inst_match_generator.cpp +++ b/src/theory/quantifiers/inst_match_generator.cpp @@ -65,6 +65,24 @@ void InstMatchGenerator::setActiveAdd(bool val){ } } +int InstMatchGenerator::getActiveScore( QuantifiersEngine * qe ) { + if( Trigger::isAtomicTrigger( d_match_pattern ) ){ + Node f = qe->getTermDatabase()->getMatchOperator( d_match_pattern ); + unsigned ngt = qe->getTermDatabase()->getNumGroundTerms( f ); + Trace("trigger-active-sel-debug") << "Number of ground terms for " << f << " is " << ngt << std::endl; + return ngt; + }else if( d_match_pattern.getKind()==INST_CONSTANT ){ + TypeNode tn = d_match_pattern.getType(); + unsigned ngtt = qe->getTermDatabase()->getNumTypeGroundTerms( tn ); + Trace("trigger-active-sel-debug") << "Number of ground terms for " << tn << " is " << ngtt << std::endl; + return ngtt; +// }else if( d_match_pattern_getKind()==EQUAL || d_match_pattern.getKind()==IFF ){ + + }else{ + return -1; + } +} + void InstMatchGenerator::initialize( Node q, QuantifiersEngine* qe, std::vector< InstMatchGenerator * > & gens ){ if( !d_pattern.isNull() ){ Trace("inst-match-gen") << "Initialize, pattern term is " << d_pattern << std::endl; @@ -837,6 +855,14 @@ int InstMatchGeneratorSimple::addTerm( Node q, Node t, QuantifiersEngine* qe ){ return qe->addInstantiation( q, m ) ? 1 : 0; } +int InstMatchGeneratorSimple::getActiveScore( QuantifiersEngine * qe ) { + Node f = qe->getTermDatabase()->getMatchOperator( d_match_pattern ); + unsigned ngt = qe->getTermDatabase()->getNumGroundTerms( f ); + Trace("trigger-active-sel-debug") << "Number of ground terms for (simple) " << f << " is " << ngt << std::endl; + return ngt; +} + + }/* CVC4::theory::inst namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/quantifiers/inst_match_generator.h b/src/theory/quantifiers/inst_match_generator.h index 096774c51..65c5a1427 100755 --- a/src/theory/quantifiers/inst_match_generator.h +++ b/src/theory/quantifiers/inst_match_generator.h @@ -46,6 +46,8 @@ public: virtual int addTerm( Node q, Node t, QuantifiersEngine* qe ) { return 0; } /** set active add */ virtual void setActiveAdd( bool val ) {} + /** get active score */ + virtual int getActiveScore( QuantifiersEngine * qe ) { return 0; } };/* class IMGenerator */ class CandidateGenerator; @@ -116,6 +118,7 @@ public: bool d_active_add; void setActiveAdd( bool val ); + int getActiveScore( QuantifiersEngine * qe ); static InstMatchGenerator* mkInstMatchGenerator( Node q, Node pat, QuantifiersEngine* qe ); static InstMatchGenerator* mkInstMatchGenerator( Node q, std::vector< Node >& pats, QuantifiersEngine* qe ); @@ -239,6 +242,8 @@ public: int addInstantiations( Node q, InstMatch& baseMatch, QuantifiersEngine* qe ); /** add ground term t, possibly add instantiations */ int addTerm( Node q, Node t, QuantifiersEngine* qe ); + /** get active score */ + int getActiveScore( QuantifiersEngine * qe ); };/* class InstMatchGeneratorSimple */ } diff --git a/src/theory/quantifiers/inst_strategy_e_matching.cpp b/src/theory/quantifiers/inst_strategy_e_matching.cpp index efd765c86..49e0a698f 100755 --- a/src/theory/quantifiers/inst_strategy_e_matching.cpp +++ b/src/theory/quantifiers/inst_strategy_e_matching.cpp @@ -149,11 +149,7 @@ void InstStrategyUserPatterns::addUserPattern( Node q, Node pat ){ InstStrategyAutoGenTriggers::InstStrategyAutoGenTriggers( QuantifiersEngine* qe ) : InstStrategy( qe ){ //how to select trigger terms - if( options::triggerSelMode()==quantifiers::TRIGGER_SEL_DEFAULT ){ - d_tr_strategy = quantifiers::TRIGGER_SEL_MIN; - }else{ - d_tr_strategy = options::triggerSelMode(); - } + d_tr_strategy = options::triggerSelMode(); //whether to select new triggers during the search if( options::incrementTriggers() ){ d_regenerate_frequency = 3; @@ -211,6 +207,29 @@ int InstStrategyAutoGenTriggers::process( Node f, Theory::Effort effort, int e ) // d_processed_trigger.clear(); // d_quantEngine->getEqualityQuery()->setLiberal( true ); //} + if( options::triggerActiveSelMode()!=TRIGGER_ACTIVE_SEL_ALL ){ + int max_score = -1; + Trigger * max_trigger = NULL; + for( std::map< Trigger*, bool >::iterator itt = d_auto_gen_trigger[0][f].begin(); itt != d_auto_gen_trigger[0][f].end(); ++itt ){ + int score = itt->first->getActiveScore(); + if( options::triggerActiveSelMode()==TRIGGER_ACTIVE_SEL_MIN ){ + if( score>=0 && ( score<max_score || max_score<0 ) ){ + max_score = score; + max_trigger = itt->first; + } + }else{ + if( score>max_score ){ + max_score = score; + max_trigger = itt->first; + } + } + d_auto_gen_trigger[0][f][itt->first] = false; + } + if( max_trigger!=NULL ){ + d_auto_gen_trigger[0][f][max_trigger] = true; + } + } + bool hasInst = false; for( unsigned r=0; r<2; r++ ){ for( std::map< Trigger*, bool >::iterator itt = d_auto_gen_trigger[r][f].begin(); itt != d_auto_gen_trigger[r][f].end(); ++itt ){ diff --git a/src/theory/quantifiers/model_builder.cpp b/src/theory/quantifiers/model_builder.cpp index 10a5ae41b..7bbe06108 100755 --- a/src/theory/quantifiers/model_builder.cpp +++ b/src/theory/quantifiers/model_builder.cpp @@ -45,7 +45,7 @@ bool QModelBuilder::isQuantifierActive( Node f ) { bool QModelBuilder::optUseModel() { - return options::mbqiMode()!=MBQI_NONE || options::fmfBoundInt(); + return options::mbqiMode()!=MBQI_NONE || options::fmfBound(); } void QModelBuilder::debugModel( FirstOrderModel* fm ){ diff --git a/src/theory/quantifiers/model_engine.cpp b/src/theory/quantifiers/model_engine.cpp index 5d575969f..7658f2b6b 100755 --- a/src/theory/quantifiers/model_engine.cpp +++ b/src/theory/quantifiers/model_engine.cpp @@ -121,7 +121,7 @@ void ModelEngine::registerQuantifier( Node f ){ if( !tn.isSort() ){ if( !tn.getCardinality().isFinite() ){ if( tn.isInteger() ){ - if( !options::fmfBoundInt() ){ + if( !options::fmfBound() ){ canHandle = false; } }else{ diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp index 68f824c57..963889a85 100755 --- a/src/theory/quantifiers/quantifiers_rewriter.cpp +++ b/src/theory/quantifiers/quantifiers_rewriter.cpp @@ -85,48 +85,6 @@ bool QuantifiersRewriter::isCube( Node n ){ } } -int QuantifiersRewriter::getPurifyId( Node n ){ - if( !TermDb::hasBoundVarAttr( n ) ){ - return 0; - }else if( inst::Trigger::isAtomicTriggerKind( n.getKind() ) ){ - return 1; - }else if( TermDb::isBoolConnective( n.getKind() ) || n.getKind()==EQUAL || n.getKind()==BOUND_VARIABLE ){ - return 0; - }else{ - return -1; - } -} - -int QuantifiersRewriter::getPurifyIdLit2( Node n, std::map< Node, int >& visited ) { - std::map< Node, int >::iterator it = visited.find( n ); - if( visited.find( n )==visited.end() ){ - int ret = 0; - if( TermDb::hasBoundVarAttr( n ) ){ - ret = getPurifyId( n ); - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - int cid = getPurifyIdLit2( n[i], visited ); - if( cid!=0 ){ - if( ret==0 ){ - ret = cid; - }else if( cid!=ret ){ - ret = -2; - break; - } - } - } - } - visited[n] = ret; - return ret; - }else{ - return it->second; - } -} - -int QuantifiersRewriter::getPurifyIdLit( Node n ) { - std::map< Node, int > visited; - return getPurifyIdLit2( n, visited ); -} - void QuantifiersRewriter::addNodeToOrBuilder( Node n, NodeBuilder<>& t ){ if( n.getKind()==OR ){ for( int i=0; i<(int)n.getNumChildren(); i++ ){ @@ -202,6 +160,9 @@ RewriteResponse QuantifiersRewriter::preRewrite(TNode in) { } children.push_back( NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST,args) ); children.push_back( body[1] ); + if( body.getNumChildren()==3 ){ + children.push_back( body[2] ); + } Node n = NodeManager::currentNM()->mkNode( in.getKind(), children ); if( in!=n ){ Trace("quantifiers-pre-rewrite") << "*** pre-rewrite " << in << std::endl; @@ -881,29 +842,53 @@ Node QuantifiersRewriter::computeCondSplit( Node body, QAttributes& qa ){ return body; } -bool QuantifiersRewriter::isVariableElim( Node v, Node s, std::map< Node, std::vector< int > >& var_parent ) { +bool QuantifiersRewriter::isVariableElim( Node v, Node s ) { if( TermDb::containsTerm( s, v ) || !s.getType().isSubtypeOf( v.getType() ) ){ return false; }else{ - if( !var_parent.empty() ){ - std::map< Node, std::vector< int > >::iterator it = var_parent.find( v ); - if( it!=var_parent.end() ){ - Assert( !it->second.empty() ); - int id = getPurifyId( s ); - return it->second.size()==1 && it->second[0]==id; + return true; + } +} + +void QuantifiersRewriter::isVariableBoundElig( Node n, std::map< Node, int >& exclude, std::map< Node, std::map< int, bool > >& visited, bool hasPol, bool pol, + std::map< Node, bool >& elig_vars ) { + int vindex = hasPol ? ( pol ? 1 : -1 ) : 0; + if( visited[n].find( vindex )==visited[n].end() ){ + visited[n][vindex] = true; + if( elig_vars.find( n )!=elig_vars.end() ){ + //variable contained in a place apart from bounds, no longer eligible for elimination + elig_vars.erase( n ); + Trace("var-elim-ineq-debug") << "...found occurrence of " << n << ", mark ineligible" << std::endl; + }else{ + if( hasPol ){ + std::map< Node, int >::iterator itx = exclude.find( n ); + if( itx!=exclude.end() && itx->second==vindex ){ + //already processed this literal + return; + } + } + for( unsigned j=0; j<n.getNumChildren(); j++ ){ + bool newHasPol; + bool newPol; + QuantPhaseReq::getPolarity( n, j, hasPol, pol, newHasPol, newPol ); + isVariableBoundElig( n[j], exclude, visited, newHasPol, newPol, elig_vars ); + if( elig_vars.empty() ){ + break; + } } } - return true; + }else{ + //already visited } } bool QuantifiersRewriter::computeVariableElimLit( Node lit, bool pol, std::vector< Node >& args, std::vector< Node >& vars, std::vector< Node >& subs, - std::map< Node, std::vector< int > >& var_parent ) { - if( lit.getKind()==EQUAL && pol ){ + std::map< Node, std::map< bool, std::map< Node, bool > > >& num_bounds ) { + if( lit.getKind()==EQUAL && pol && options::varElimQuant() ){ for( unsigned i=0; i<2; i++ ){ std::vector< Node >::iterator ita = std::find( args.begin(), args.end(), lit[i] ); if( ita!=args.end() ){ - if( isVariableElim( lit[i], lit[1-i], var_parent ) ){ + if( isVariableElim( lit[i], lit[1-i] ) ){ Trace("var-elim-quant") << "Variable eliminate based on equality : " << lit[i] << " -> " << lit[1-i] << std::endl; vars.push_back( lit[i] ); subs.push_back( lit[1-i] ); @@ -912,28 +897,7 @@ bool QuantifiersRewriter::computeVariableElimLit( Node lit, bool pol, std::vecto } } } - //for arithmetic, solve the equality - if( lit[0].getType().isReal() ){ - std::map< Node, Node > msum; - if( QuantArith::getMonomialSumLit( lit, msum ) ){ - for( std::map< Node, Node >::iterator itm = msum.begin(); itm != msum.end(); ++itm ){ - std::vector< Node >::iterator ita = std::find( args.begin(), args.end(), itm->first ); - if( ita!=args.end() ){ - Node veq_c; - Node val; - int ires = QuantArith::isolate( itm->first, msum, veq_c, val, EQUAL ); - if( ires!=0 && veq_c.isNull() && isVariableElim( itm->first, val, var_parent ) ){ - Trace("var-elim-quant") << "Variable eliminate based on solved equality : " << itm->first << " -> " << val << std::endl; - vars.push_back( itm->first ); - subs.push_back( val ); - args.erase( ita ); - return true; - } - } - } - } - } - }else if( lit.getKind()==APPLY_TESTER && pol && lit[0].getKind()==BOUND_VARIABLE ){ + }else if( lit.getKind()==APPLY_TESTER && pol && lit[0].getKind()==BOUND_VARIABLE && options::dtVarExpandQuant() ){ Trace("var-elim-dt") << "Expand datatype variable based on : " << lit << std::endl; std::vector< Node >::iterator ita = std::find( args.begin(), args.end(), lit[0] ); if( ita!=args.end() ){ @@ -957,7 +921,7 @@ bool QuantifiersRewriter::computeVariableElimLit( Node lit, bool pol, std::vecto args.insert( args.end(), newVars.begin(), newVars.end() ); return true; } - }else if( lit.getKind()==BOUND_VARIABLE ){ + }else if( lit.getKind()==BOUND_VARIABLE && options::varElimQuant() ){ std::vector< Node >::iterator ita = std::find( args.begin(), args.end(), lit ); if( ita!=args.end() ){ Trace("var-elim-bool") << "Variable eliminate : " << lit << std::endl; @@ -967,24 +931,76 @@ bool QuantifiersRewriter::computeVariableElimLit( Node lit, bool pol, std::vecto return true; } } + if( ( lit.getKind()==EQUAL && lit[0].getType().isReal() && pol ) || ( ( lit.getKind()==GEQ || lit.getKind()==GT ) && options::varIneqElimQuant() ) ){ + //for arithmetic, solve the (in)equality + std::map< Node, Node > msum; + if( QuantArith::getMonomialSumLit( lit, msum ) ){ + for( std::map< Node, Node >::iterator itm = msum.begin(); itm != msum.end(); ++itm ){ + if( !itm->first.isNull() ){ + std::vector< Node >::iterator ita = std::find( args.begin(), args.end(), itm->first ); + if( ita!=args.end() ){ + if( lit.getKind()==EQUAL ){ + Assert( pol ); + Node veq_c; + Node val; + int ires = QuantArith::isolate( itm->first, msum, veq_c, val, EQUAL ); + if( ires!=0 && veq_c.isNull() && isVariableElim( itm->first, val ) ){ + Trace("var-elim-quant") << "Variable eliminate based on solved equality : " << itm->first << " -> " << val << std::endl; + vars.push_back( itm->first ); + subs.push_back( val ); + args.erase( ita ); + return true; + } + }else{ + Assert( lit.getKind()==GEQ || lit.getKind()==GT ); + //store that this literal is upper/lower bound for itm->first + Node veq_c; + Node val; + int ires = QuantArith::isolate( itm->first, msum, veq_c, val, lit.getKind() ); + if( ires!=0 && veq_c.isNull() ){ + bool is_upper = pol!=( ires==1 ); + Trace("var-elim-ineq-debug") << lit << " is a " << ( is_upper ? "upper" : "lower" ) << " bound for " << itm->first << std::endl; + Trace("var-elim-ineq-debug") << " pol/ires = " << pol << " " << ires << std::endl; + num_bounds[itm->first][is_upper][lit] = pol; + }else{ + Trace("var-elim-ineq-debug") << "...ineligible " << itm->first << " since it cannot be solved for (" << ires << ", " << veq_c << ")." << std::endl; + num_bounds[itm->first][true].clear(); + num_bounds[itm->first][false].clear(); + } + } + }else{ + if( lit.getKind()==GEQ || lit.getKind()==GT ){ + //compute variables in itm->first, these are not eligible for elimination + std::vector< Node > bvs; + TermDb::getBoundVars( itm->first, bvs ); + for( unsigned j=0; j<bvs.size(); j++ ){ + Trace("var-elim-ineq-debug") << "...ineligible " << bvs[j] << " since it is contained in monomial." << std::endl; + num_bounds[bvs[j]][true].clear(); + num_bounds[bvs[j]][false].clear(); + } + } + } + } + } + } + } + return false; } -Node QuantifiersRewriter::computeVarElimination2( Node body, std::vector< Node >& args, QAttributes& qa, std::map< Node, std::vector< int > >& var_parent ){ +Node QuantifiersRewriter::computeVarElimination2( Node body, std::vector< Node >& args, QAttributes& qa ){ Trace("var-elim-quant-debug") << "Compute var elimination for " << body << std::endl; + std::map< Node, std::map< bool, std::map< Node, bool > > > num_bounds; QuantPhaseReq qpr( body ); std::vector< Node > vars; std::vector< Node > subs; for( std::map< Node, bool >::iterator it = qpr.d_phase_reqs.begin(); it != qpr.d_phase_reqs.end(); ++it ){ - //Notice() << " " << it->first << " -> " << ( it->second ? "true" : "false" ) << std::endl; - if( ( it->first.getKind()==EQUAL && it->second && options::varElimQuant() ) || - ( it->first.getKind()==APPLY_TESTER && it->second && it->first[0].getKind()==BOUND_VARIABLE && options::dtVarExpandQuant() ) || - ( it->first.getKind()==BOUND_VARIABLE && options::varElimQuant() ) ){ - if( computeVariableElimLit( it->first, it->second, args, vars, subs, var_parent ) ){ - break; - } + Trace("var-elim-quant-debug") << " phase req : " << it->first << " -> " << ( it->second ? "true" : "false" ) << std::endl; + if( computeVariableElimLit( it->first, it->second, args, vars, subs, num_bounds ) ){ + break; } } + if( !vars.empty() ){ Trace("var-elim-quant-debug") << "VE " << vars.size() << "/" << args.size() << std::endl; //remake with eliminated nodes @@ -994,21 +1010,65 @@ Node QuantifiersRewriter::computeVarElimination2( Node body, std::vector< Node > qa.d_ipl = qa.d_ipl.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); } Trace("var-elim-quant") << "Return " << body << std::endl; + }else{ + //collect all variables that have only upper/lower bounds + std::map< Node, bool > elig_vars; + for( std::map< Node, std::map< bool, std::map< Node, bool > > >::iterator it = num_bounds.begin(); it != num_bounds.end(); ++it ){ + if( it->second.find( true )==it->second.end() ){ + Trace("var-elim-ineq-debug") << "Variable " << it->first << " has only lower bounds." << std::endl; + elig_vars[it->first] = false; + }else if( it->second.find( false )==it->second.end() ){ + Trace("var-elim-ineq-debug") << "Variable " << it->first << " has only upper bounds." << std::endl; + elig_vars[it->first] = true; + } + } + if( !elig_vars.empty() ){ + std::vector< Node > inactive_vars; + std::map< Node, std::map< int, bool > > visited; + std::map< Node, int > exclude; + for( std::map< Node, bool >::iterator it = qpr.d_phase_reqs.begin(); it != qpr.d_phase_reqs.end(); ++it ){ + if( it->first.getKind()==GEQ || it->first.getKind()==GT ){ + exclude[ it->first ] = it->second ? -1 : 1; + Trace("var-elim-ineq-debug") << "...exclude " << it->first << " at polarity " << exclude[ it->first ] << std::endl; + } + } + //traverse the body, invalidate variables if they occur in places other than the bounds they occur in + isVariableBoundElig( body, exclude, visited, true, true, elig_vars ); + + if( !elig_vars.empty() ){ + if( !qa.d_ipl.isNull() ){ + isVariableBoundElig( qa.d_ipl, exclude, visited, false, true, elig_vars ); + } + for( std::map< Node, bool >::iterator itev = elig_vars.begin(); itev != elig_vars.end(); ++itev ){ + Node v = itev->first; + Trace("var-elim-ineq-debug") << v << " is eligible for elimination." << std::endl; + //do substitution corresponding to infinite projection, all literals involving unbounded variable go to true/false + std::vector< Node > terms; + std::vector< Node > subs; + for( std::map< Node, bool >::iterator itb = num_bounds[v][elig_vars[v]].begin(); itb != num_bounds[v][elig_vars[v]].end(); ++itb ){ + Trace("var-elim-ineq-debug") << " subs : " << itb->first << " -> " << itb->second << std::endl; + terms.push_back( itb->first ); + subs.push_back( NodeManager::currentNM()->mkConst( itb->second ) ); + } + body = body.substitute( terms.begin(), terms.end(), subs.begin(), subs.end() ); + Trace("var-elim-ineq-debug") << "Return " << body << std::endl; + //eliminate from args + std::vector< Node >::iterator ita = std::find( args.begin(), args.end(), v ); + Assert( ita!=args.end() ); + args.erase( ita ); + } + } + } } return body; } Node QuantifiersRewriter::computeVarElimination( Node body, std::vector< Node >& args, QAttributes& qa ){ - //the parent id's for each variable, if using purifyQuant - std::map< Node, std::vector< int > > var_parent; - if( options::purifyQuant() ){ - body = computePurify( body, args, var_parent ); - } if( options::varElimQuant() || options::dtVarExpandQuant() ){ Node prev; do{ prev = body; - body = computeVarElimination2( body, args, qa, var_parent ); + body = computeVarElimination2( body, args, qa ); }while( prev!=body && !args.empty() ); } return body; @@ -1355,9 +1415,7 @@ bool QuantifiersRewriter::doOperation( Node q, int computeOption, QAttributes& q }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_PURIFY_EXPAND ){ - return options::purifyQuant() && is_std; + return ( options::varElimQuant() || options::dtVarExpandQuant() ) && is_std; }else{ return false; } @@ -1391,14 +1449,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_PURIFY_EXPAND ){ - std::vector< Node > conj; - computePurifyExpand( n, conj, args, qa ); - if( !conj.empty() ){ - return conj.size()==1 ? conj[0] : NodeManager::currentNM()->mkNode( AND, conj ); - }else{ - return f; - } } Trace("quantifiers-rewrite-debug") << "Compute Operation: return " << n << ", " << args.size() << std::endl; if( f[1]==n && args.size()==f[0].getNumChildren() ){ @@ -1630,104 +1680,3 @@ Node QuantifiersRewriter::preprocess( Node n, bool isInst ) { return n; } -Node QuantifiersRewriter::computePurify2( Node body, std::vector< Node >& args, std::map< Node, Node >& visited, std::map< Node, Node >& var_to_term, - std::map< Node, std::vector< int > >& var_parent, int parentId ){ - std::map< Node, Node >::iterator it = visited.find( body ); - if( it!=visited.end() ){ - return it->second; - }else{ - Node ret = body; - if( body.getNumChildren()>0 && body.getKind()!=FORALL && TermDb::hasBoundVarAttr( ret ) ){ - std::vector< Node > children; - bool childrenChanged = false; - int id = getPurifyId( body ); - for( unsigned i=0; i<body.getNumChildren(); i++ ){ - Node bi; - if( body.getKind()==EQUAL && i==1 ){ - int cid = getPurifyId( children[0] ); - bi = computePurify2( body[i], args, visited, var_to_term, var_parent, cid ); - if( children[0].getKind()==BOUND_VARIABLE ){ - cid = getPurifyId( bi ); - if( cid!=0 && std::find( var_parent[children[0]].begin(), var_parent[children[0]].end(), cid )==var_parent[children[0]].end() ){ - var_parent[children[0]].push_back( id ); - } - } - }else{ - bi = computePurify2( body[i], args, visited, var_to_term, var_parent, id ); - } - childrenChanged = childrenChanged || bi!=body[i]; - children.push_back( bi ); - if( id!=0 && bi.getKind()==BOUND_VARIABLE ){ - if( std::find( var_parent[bi].begin(), var_parent[bi].end(), id )==var_parent[bi].end() ){ - var_parent[bi].push_back( id ); - } - } - } - if( childrenChanged ){ - if( body.getMetaKind() == kind::metakind::PARAMETERIZED ){ - children.insert( children.begin(), body.getOperator() ); - } - ret = NodeManager::currentNM()->mkNode( body.getKind(), children ); - } - if( parentId!=0 && parentId!=id ){ - Node v = NodeManager::currentNM()->mkBoundVar( ret.getType() ); - var_to_term[v] = Rewriter::rewrite( v.eqNode( ret ) ); - ret = v; - args.push_back( v ); - } - } - visited[body] = ret; - return ret; - } -} - -Node QuantifiersRewriter::computePurify( Node body, std::vector< Node >& args, std::map< Node, std::vector< int > >& var_parent ) { - std::map< Node, Node > visited; - std::map< Node, Node > var_to_term; - Node pbody = computePurify2( body, args, visited, var_to_term, var_parent, 0 ); - if( pbody==body ){ - return body; - }else{ - Trace("quantifiers-rewrite-purify") << "Purify : " << body << std::endl; - Trace("quantifiers-rewrite-purify") << " Got : " << pbody << std::endl; - for( std::map< Node, Node >::iterator it = var_to_term.begin(); it != var_to_term.end(); ++it ){ - Trace("quantifiers-rewrite-purify") << " " << it->first << " : " << it->second << std::endl; - } - Trace("quantifiers-rewrite-purify") << "Variable parents : " << std::endl; - for( std::map< Node, std::vector< int > >::iterator it = var_parent.begin(); it != var_parent.end(); ++it ){ - Trace("quantifiers-rewrite-purify") << " " << it->first << " -> "; - for( unsigned i=0; i<it->second.size(); i++ ){ - Trace("quantifiers-rewrite-purify") << it->second[i] << " "; - } - Trace("quantifiers-rewrite-purify") << std::endl; - } - Trace("quantifiers-rewrite-purify") << std::endl; - std::vector< Node > disj; - for( std::map< Node, Node >::iterator it = var_to_term.begin(); it != var_to_term.end(); ++it ){ - disj.push_back( it->second.negate() ); - } - Node res; - if( disj.empty() ){ - res = pbody; - }else{ - disj.push_back( pbody ); - res = NodeManager::currentNM()->mkNode( OR, disj ); - } - Trace("quantifiers-rewrite-purify") << "Constructed : " << res << std::endl << std::endl; - return res; - } -} - -void QuantifiersRewriter::computePurifyExpand( Node body, std::vector< Node >& conj, std::vector< Node >& args, QAttributes& qa ) { - if( body.getKind()==OR ){ - Trace("quantifiers-rewrite-purify-exp") << "Purify expansion : " << body << std::endl; - std::map< int, std::vector< Node > > disj; - std::map< int, std::vector< Node > > fvs; - for( unsigned i=0; i<body.getNumChildren(); i++ ){ - int pid CVC4_UNUSED = getPurifyIdLit( body[i] ); - } - //mark as an attribute - //Node attr = NodeManager::currentNM()->mkNode( INST_ATTRIBUTE, body ); - } -} - diff --git a/src/theory/quantifiers/quantifiers_rewriter.h b/src/theory/quantifiers/quantifiers_rewriter.h index 776517109..60dc8ab10 100755 --- a/src/theory/quantifiers/quantifiers_rewriter.h +++ b/src/theory/quantifiers/quantifiers_rewriter.h @@ -35,8 +35,6 @@ public: static bool isClause( Node n ); static bool isLiteral( Node n ); static bool isCube( Node n ); - 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 ); @@ -49,12 +47,12 @@ private: std::vector< Node >& new_vars, std::vector< Node >& new_conds ); 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 ); + static bool isVariableElim( Node v, Node s ); + static void isVariableBoundElig( Node n, std::map< Node, int >& exclude, std::map< Node, std::map< int, bool > >& visited, bool hasPol, bool pol, + std::map< Node, bool >& elig_vars ); static bool computeVariableElimLit( Node n, bool pol, std::vector< Node >& args, std::vector< Node >& var, std::vector< Node >& subs, - std::map< Node, std::vector< int > >& var_parent ); - static Node computePurify2( Node body, std::vector< Node >& args, std::map< Node, Node >& visited, std::map< Node, Node >& var_to_term, - std::map< Node, std::vector< int > >& var_parent, int parentId ); - static Node computeVarElimination2( Node body, std::vector< Node >& args, QAttributes& qa, std::map< Node, std::vector< int > >& var_parent ); + std::map< Node, std::map< bool, std::map< Node, bool > > >& num_bounds ); + static Node computeVarElimination2( Node body, std::vector< Node >& args, QAttributes& qa ); private: static Node computeElimSymbols( Node body ); static Node computeMiniscoping( std::vector< Node >& args, Node body, QAttributes& qa ); @@ -65,8 +63,6 @@ private: static Node computePrenex( Node body, std::vector< Node >& args, bool pol ); 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 ); private: enum{ COMPUTE_ELIM_SYMBOLS = 0, @@ -76,7 +72,6 @@ private: COMPUTE_PRENEX, COMPUTE_VAR_ELIMINATION, COMPUTE_COND_SPLIT, - COMPUTE_PURIFY_EXPAND, //COMPUTE_FLATTEN_ARGS_UF, //COMPUTE_CNF, COMPUTE_LAST diff --git a/src/theory/quantifiers/trigger.cpp b/src/theory/quantifiers/trigger.cpp index ee091919d..2faed3af0 100755 --- a/src/theory/quantifiers/trigger.cpp +++ b/src/theory/quantifiers/trigger.cpp @@ -386,9 +386,9 @@ bool Trigger::isCbqiKind( Kind k ) { bool Trigger::isSimpleTrigger( Node n ){ Node t = n.getKind()==NOT ? n[0] : n; - if( n.getKind()==IFF || n.getKind()==EQUAL ){ - if( !quantifiers::TermDb::hasInstConstAttr( n[1] ) ){ - t = n[0]; + if( t.getKind()==IFF || t.getKind()==EQUAL ){ + if( !quantifiers::TermDb::hasInstConstAttr( t[1] ) ){ + t = t[0]; } } if( isAtomicTrigger( t ) ){ @@ -742,6 +742,10 @@ InstMatchGenerator* Trigger::getInstMatchGenerator( Node q, Node n ) { } } +int Trigger::getActiveScore() { + return d_mg->getActiveScore( d_quantEngine ); +} + Trigger* TriggerTrie::getTrigger2( std::vector< Node >& nodes ){ if( nodes.empty() ){ return d_tr.empty() ? NULL : d_tr[0]; diff --git a/src/theory/quantifiers/trigger.h b/src/theory/quantifiers/trigger.h index a3da4d398..6d7bf1f4d 100755 --- a/src/theory/quantifiers/trigger.h +++ b/src/theory/quantifiers/trigger.h @@ -134,6 +134,7 @@ class Trigger { } Trace(c) << " )"; } + int getActiveScore(); private: /** trigger constructor */ Trigger( QuantifiersEngine* ie, Node f, std::vector< Node >& nodes ); diff --git a/src/theory/quantifiers_engine.cpp b/src/theory/quantifiers_engine.cpp index d81efe1da..c08fee712 100644 --- a/src/theory/quantifiers_engine.cpp +++ b/src/theory/quantifiers_engine.cpp @@ -98,7 +98,7 @@ QuantifiersEngine::QuantifiersEngine(context::Context* c, context::UserContext* //the model object if( options::mbqiMode()==quantifiers::MBQI_FMC || - options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL || options::fmfBoundInt() || + options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL || options::fmfBound() || options::mbqiMode()==quantifiers::MBQI_TRUST ){ d_model = new quantifiers::fmcheck::FirstOrderModelFmc( this, c, "FirstOrderModelFmc" ); }else if( options::mbqiMode()==quantifiers::MBQI_ABS ){ @@ -129,6 +129,8 @@ QuantifiersEngine::QuantifiersEngine(context::Context* c, context::UserContext* d_fs = NULL; d_rel_dom = NULL; d_builder = NULL; + + d_trackInstLemmas = options::proof() && options::trackInstLemmas(); d_total_inst_count_debug = 0; //allow theory combination to go first, once initially @@ -224,9 +226,14 @@ void QuantifiersEngine::finishInit(){ } } } + if( options::ceGuidedInst() ){ + d_ceg_inst = new quantifiers::CegInstantiation( this, c ); + d_modules.push_back( d_ceg_inst ); + needsBuilder = true; + } //finite model finding if( options::finiteModelFind() ){ - if( options::fmfBoundInt() ){ + if( options::fmfBound() ){ d_bint = new quantifiers::BoundedIntegers( c, this ); d_modules.push_back( d_bint ); } @@ -238,11 +245,6 @@ void QuantifiersEngine::finishInit(){ d_rr_engine = new quantifiers::RewriteEngine( c, this ); d_modules.push_back(d_rr_engine); } - if( options::ceGuidedInst() ){ - d_ceg_inst = new quantifiers::CegInstantiation( this, c ); - d_modules.push_back( d_ceg_inst ); - needsBuilder = true; - } if( options::ltePartialInst() ){ d_lte_part_inst = new quantifiers::LtePartialInst( this, c ); d_modules.push_back( d_lte_part_inst ); @@ -280,9 +282,9 @@ void QuantifiersEngine::finishInit(){ } if( needsBuilder ){ - Trace("quant-engine-debug") << "Initialize model engine, mbqi : " << options::mbqiMode() << " " << options::fmfBoundInt() << std::endl; + Trace("quant-engine-debug") << "Initialize model engine, mbqi : " << options::mbqiMode() << " " << options::fmfBound() << std::endl; if( options::mbqiMode()==quantifiers::MBQI_FMC || options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL || - options::mbqiMode()==quantifiers::MBQI_TRUST || options::fmfBoundInt() ){ + options::mbqiMode()==quantifiers::MBQI_TRUST || options::fmfBound() ){ Trace("quant-engine-debug") << "...make fmc builder." << std::endl; d_builder = new quantifiers::fmcheck::FullModelChecker( c, this ); }else if( options::mbqiMode()==quantifiers::MBQI_ABS ){ @@ -719,7 +721,7 @@ void QuantifiersEngine::propagate( Theory::Effort level ){ } Node QuantifiersEngine::getNextDecisionRequest(){ - for( int i=0; i<(int)d_modules.size(); i++ ){ + for( unsigned i=0; i<d_modules.size(); i++ ){ Node n = d_modules[i]->getNextDecisionRequest(); if( !n.isNull() ){ return n; @@ -1168,6 +1170,16 @@ bool QuantifiersEngine::addInstantiation( Node q, std::vector< Node >& terms, bo } } } + if( d_trackInstLemmas ){ + bool recorded; + if( options::incrementalSolving() ){ + recorded = d_c_inst_match_trie[q]->recordInstLemma( q, terms, lem ); + }else{ + recorded = d_inst_match_trie[q].recordInstLemma( q, terms, lem ); + } + Trace("inst-add-debug") << "...was recorded : " << recorded << std::endl; + Assert( recorded ); + } Trace("inst-add-debug") << " --> Success." << std::endl; ++(d_statistics.d_instantiations); return true; @@ -1229,7 +1241,7 @@ bool QuantifiersEngine::getInstWhenNeedsCheck( Theory::Effort e ) { if( e==Theory::EFFORT_LAST_CALL ){ //with bounded integers, skip every other last call, // since matching loops may occur with infinite quantification - if( d_ierCounter_lc%2==0 && options::fmfBoundInt() ){ + if( d_ierCounter_lc%2==0 && options::fmfBound() ){ performCheck = false; } } @@ -1273,36 +1285,106 @@ void QuantifiersEngine::flushLemmas(){ } } +bool QuantifiersEngine::getUnsatCoreLemmas( std::vector< Node >& active_lemmas ) { + //only if unsat core available + bool isUnsatCoreAvailable = false; + if( options::proof() ){ + isUnsatCoreAvailable = ProofManager::currentPM()->unsatCoreAvailable(); + } + if( isUnsatCoreAvailable ){ + Trace("inst-unsat-core") << "Get instantiations in unsat core..." << std::endl; + ProofManager::currentPM()->getLemmasInUnsatCore(theory::THEORY_QUANTIFIERS, active_lemmas); + if( Trace.isOn("inst-unsat-core") ){ + Trace("inst-unsat-core") << "Quantifiers lemmas in unsat core: " << std::endl; + for (unsigned i = 0; i < active_lemmas.size(); ++i) { + Trace("inst-unsat-core") << " " << active_lemmas[i] << std::endl; + } + Trace("inst-unsat-core") << std::endl; + } + return true; + }else{ + return false; + } +} + +bool QuantifiersEngine::getUnsatCoreLemmas( std::vector< Node >& active_lemmas, std::map< Node, Node >& weak_imp ) { + if( getUnsatCoreLemmas( active_lemmas ) ){ + for (unsigned i = 0; i < active_lemmas.size(); ++i) { + Node n = ProofManager::currentPM()->getWeakestImplicantInUnsatCore(active_lemmas[i]); + if( n!=active_lemmas[i] ){ + Trace("inst-unsat-core") << " weaken : " << active_lemmas[i] << " -> " << n << std::endl; + } + weak_imp[active_lemmas[i]] = n; + } + return true; + }else{ + return false; + } +} + +void QuantifiersEngine::getExplanationForInstLemmas( std::vector< Node >& lems, std::map< Node, Node >& quant, std::map< Node, std::vector< Node > >& tvec ) { + if( d_trackInstLemmas ){ + if( options::incrementalSolving() ){ + for( std::map< Node, inst::CDInstMatchTrie* >::iterator it = d_c_inst_match_trie.begin(); it != d_c_inst_match_trie.end(); ++it ){ + it->second->getExplanationForInstLemmas( it->first, lems, quant, tvec ); + } + }else{ + for( std::map< Node, inst::InstMatchTrie >::iterator it = d_inst_match_trie.begin(); it != d_inst_match_trie.end(); ++it ){ + it->second.getExplanationForInstLemmas( it->first, lems, quant, tvec ); + } + } +#ifdef CVC4_ASSERTIONS + for( unsigned j=0; j<lems.size(); j++ ){ + Assert( quant.find( lems[j] )!=quant.end() ); + Assert( tvec.find( lems[j] )!=tvec.end() ); + } +#endif + }else{ + Assert( false ); + } +} + void QuantifiersEngine::printInstantiations( std::ostream& out ) { + bool useUnsatCore = false; + std::vector< Node > active_lemmas; + if( d_trackInstLemmas && getUnsatCoreLemmas( active_lemmas ) ){ + useUnsatCore = true; + } + bool printed = false; for( BoolMap::iterator it = d_skolemized.begin(); it != d_skolemized.end(); ++it ){ Node q = (*it).first; printed = true; - out << "Skolem constants of " << q << " : " << std::endl; + out << "(skolem " << q << std::endl; out << " ( "; for( unsigned i=0; i<d_term_db->d_skolem_constants[q].size(); i++ ){ - if( i>0 ){ out << ", "; } + if( i>0 ){ out << " "; } out << d_term_db->d_skolem_constants[q][i]; } out << " )" << std::endl; - out << std::endl; + out << ")" << std::endl; } if( options::incrementalSolving() ){ for( std::map< Node, inst::CDInstMatchTrie* >::iterator it = d_c_inst_match_trie.begin(); it != d_c_inst_match_trie.end(); ++it ){ - printed = true; - out << "Instantiations of " << it->first << " : " << std::endl; - it->second->print( out, it->first ); + bool firstTime = true; + it->second->print( out, it->first, firstTime, useUnsatCore, active_lemmas ); + if( !firstTime ){ + out << ")" << std::endl; + } + printed = printed || !firstTime; } }else{ for( std::map< Node, inst::InstMatchTrie >::iterator it = d_inst_match_trie.begin(); it != d_inst_match_trie.end(); ++it ){ - printed = true; - out << "Instantiations of " << it->first << " : " << std::endl; - it->second.print( out, it->first ); - out << std::endl; + bool firstTime = true; + it->second.print( out, it->first, firstTime, useUnsatCore, active_lemmas ); + if( !firstTime ){ + out << ")" << std::endl; + } + printed = printed || !firstTime; } } if( !printed ){ - out << "No instantiations." << std::endl; + out << "No instantiations" << std::endl; } } @@ -1315,13 +1397,19 @@ void QuantifiersEngine::printSynthSolution( std::ostream& out ) { } void QuantifiersEngine::getInstantiations( std::map< Node, std::vector< Node > >& insts ) { + bool useUnsatCore = false; + std::vector< Node > active_lemmas; + if( d_trackInstLemmas && getUnsatCoreLemmas( active_lemmas ) ){ + useUnsatCore = true; + } + if( options::incrementalSolving() ){ for( std::map< Node, inst::CDInstMatchTrie* >::iterator it = d_c_inst_match_trie.begin(); it != d_c_inst_match_trie.end(); ++it ){ - it->second->getInstantiations( insts[it->first], it->first, this ); + it->second->getInstantiations( insts[it->first], it->first, this, useUnsatCore, active_lemmas ); } }else{ for( std::map< Node, inst::InstMatchTrie >::iterator it = d_inst_match_trie.begin(); it != d_inst_match_trie.end(); ++it ){ - it->second.getInstantiations( insts[it->first], it->first, this ); + it->second.getInstantiations( insts[it->first], it->first, this, useUnsatCore, active_lemmas ); } } } diff --git a/src/theory/quantifiers_engine.h b/src/theory/quantifiers_engine.h index 1f4a04218..08ca0564b 100644 --- a/src/theory/quantifiers_engine.h +++ b/src/theory/quantifiers_engine.h @@ -143,6 +143,9 @@ private: quantifiers::QuantAntiSkolem * d_anti_skolem; /** quantifiers instantiation propagtor */ quantifiers::InstPropagator * d_inst_prop; +private: + /** whether we are tracking instantiation lemmas */ + bool d_trackInstLemmas; public: //effort levels enum { QEFFORT_CONFLICT, @@ -363,6 +366,11 @@ public: void printSynthSolution( std::ostream& out ); /** get instantiations */ void getInstantiations( std::map< Node, std::vector< Node > >& insts ); + /** get unsat core lemmas */ + bool getUnsatCoreLemmas( std::vector< Node >& active_lemmas ); + bool getUnsatCoreLemmas( std::vector< Node >& active_lemmas, std::map< Node, Node >& weak_imp ); + /** get inst for lemmas */ + void getExplanationForInstLemmas( std::vector< Node >& lems, std::map< Node, Node >& quant, std::map< Node, std::vector< Node > >& tvec ); /** statistics class */ class Statistics { public: diff --git a/src/theory/rep_set.cpp b/src/theory/rep_set.cpp index 184553ba7..86bcb2cac 100644 --- a/src/theory/rep_set.cpp +++ b/src/theory/rep_set.cpp @@ -366,12 +366,12 @@ int RepSetIterator::resetIndex( int i, bool initial ) { 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]; } + Assert( srv.getKind()==kind::SINGLETON ); d_setm_bounds[v].push_back( srv[0] ); d_domain[v].push_back( d_setm_bounds[v].size() ); }else{ diff --git a/src/theory/sep/theory_sep.cpp b/src/theory/sep/theory_sep.cpp index 836a04afa..dcba4c379 100644 --- a/src/theory/sep/theory_sep.cpp +++ b/src/theory/sep/theory_sep.cpp @@ -93,6 +93,7 @@ Node TheorySep::ppRewrite(TNode term) { //must process assertions at preprocess so that quantified assertions are processed properly void TheorySep::processAssertions( std::vector< Node >& assertions ) { + d_pp_nils.clear(); std::map< Node, bool > visited; for( unsigned i=0; i<assertions.size(); i++ ){ processAssertion( assertions[i], visited ); @@ -102,7 +103,11 @@ void TheorySep::processAssertions( std::vector< Node >& assertions ) { 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 ){ + if( n.getKind()==kind::SEP_NIL ){ + if( std::find( d_pp_nils.begin(), d_pp_nils.end(), n )==d_pp_nils.end() ){ + d_pp_nils.push_back( n ); + } + }else 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 ); @@ -170,7 +175,7 @@ void TheorySep::preRegisterTermRec(TNode t, std::map< TNode, bool >& visited ) { 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; + setNilRef( tn, t ); }else{ Node nr = it->second; Trace("sep-prereg") << "...reference is " << nr << "." << std::endl; @@ -271,32 +276,71 @@ void TheorySep::collectModelInfo( TheoryModel* m, bool fullModel ) { // Send the equality engine information to the model m->assertEqualityEngine( &d_equalityEngine ); + +} + +void TheorySep::postProcessModel(TheoryModel* m) { + Trace("sep-model") << "Printing model for TheorySep..." << std::endl; - 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; + std::vector< Node > sep_children; + Node m_neq; + Node m_heap; + for( std::map< TypeNode, Node >::iterator it = d_base_label.begin(); it != d_base_label.end(); ++it ){ + //should only be constructing for one heap type + Assert( m_heap.isNull() ); + Assert( d_loc_to_data_type.find( it->first )!=d_loc_to_data_type.end() ); + Trace("sep-model") << "Model for heap, type = " << it->first << " with data type " << d_loc_to_data_type[it->first] << " : " << std::endl; + TypeEnumerator te_range( d_loc_to_data_type[it->first] ); + //m->d_comment_str << "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; + //m->d_comment_str << " [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 ); + std::vector< Node > pto_children; + Node l = d_label_model[it->second].d_heap_locs_model[j][0]; + Assert( l.isConst() ); + pto_children.push_back( l ); + Trace("sep-model") << " " << l << " -> "; + //m->d_comment_str << " " << l << " -> "; + if( d_pto_model[l].isNull() ){ + Trace("sep-model") << "_"; + //m->d_comment_str << "_"; + pto_children.push_back( *te_range ); + }else{ + Trace("sep-model") << d_pto_model[l]; + //m->d_comment_str << d_pto_model[l]; + Node vpto = d_valuation.getModel()->getRepresentative( d_pto_model[l] ); + Assert( vpto.isConst() ); + pto_children.push_back( vpto ); } + Trace("sep-model") << std::endl; + //m->d_comment_str << std::endl; + sep_children.push_back( NodeManager::currentNM()->mkNode( kind::SEP_PTO, pto_children ) ); } - 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; } + Node nil = getNilRef( it->first ); + Node vnil = d_valuation.getModel()->getRepresentative( nil ); + m_neq = NodeManager::currentNM()->mkNode( nil.getType().isBoolean() ? kind::IFF : kind::EQUAL, nil, vnil ); + Trace("sep-model") << "sep.nil = " << vnil << std::endl; + Trace("sep-model") << std::endl; + //m->d_comment_str << "sep.nil = " << vnil << std::endl; + //m->d_comment_str << std::endl; + if( sep_children.empty() ){ + TypeEnumerator te_domain( it->first ); + m_heap = NodeManager::currentNM()->mkNode( kind::SEP_EMP, *te_domain ); + }else if( sep_children.size()==1 ){ + m_heap = sep_children[0]; + }else{ + m_heap = NodeManager::currentNM()->mkNode( kind::SEP_STAR, sep_children ); + } + m->setHeapModel( m_heap, m_neq ); + //m->d_comment_str << m->d_sep_heap << std::endl; + //m->d_comment_str << m->d_sep_nil_eq << std::endl; } + Trace("sep-model") << "Finished printing model for TheorySep." << std::endl; } ///////////////////////////////////////////////////////////////////////////// @@ -307,6 +351,13 @@ void TheorySep::collectModelInfo( TheoryModel* m, bool fullModel ) void TheorySep::presolve() { Trace("sep-pp") << "Presolving" << std::endl; //TODO: cleanup if incremental? + + //we must preregister all instances of sep.nil to ensure they are made equal + for( unsigned i=0; i<d_pp_nils.size(); i++ ){ + std::map< TNode, bool > visited; + preRegisterTermRec( d_pp_nils[i], visited ); + } + d_pp_nils.clear(); } @@ -445,6 +496,11 @@ void TheorySep::check(Effort e) { Node ilem = s.eqNode( empSet ); Trace("sep-lemma-debug") << "Sep::Lemma : wand reduction, disjoint : " << ilem << std::endl; c_lems.push_back( ilem ); + //nil does not occur in labels[0] + Node nr = getNilRef( tn ); + Node nrlem = NodeManager::currentNM()->mkNode( kind::MEMBER, nr, labels[0] ).negate(); + Trace("sep-lemma") << "Sep::Lemma: sep.nil not in wand antecedant heap : " << nrlem << std::endl; + d_out->lemma( nrlem ); } //send out definitional lemmas for introduced sets for( unsigned j=0; j<c_lems.size(); j++ ){ @@ -458,8 +514,9 @@ void TheorySep::check(Effort e) { 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 ); + //not needed anymore: semantics of sep.nil is enforced globally + //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 ); @@ -702,33 +759,19 @@ void TheorySep::check(Effort e) { 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 ); - } - } + //new refinement + //instantiate the label + 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{ - //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 = Rewriter::rewrite( inst ); + if( inst==( polarity ? d_true : d_false ) ){ 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() ); } + conc.push_back( polarity ? inst : inst.negate() ); } if( inst_success ){ std::vector< Node > lemc; @@ -737,11 +780,12 @@ void TheorySep::check(Effort e) { 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() ){ + 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; @@ -773,6 +817,10 @@ void TheorySep::check(Effort e) { } +bool TheorySep::needsCheckLastEffort() { + return hasFacts(); +} + Node TheorySep::getNextDecisionRequest() { for( unsigned i=0; i<d_neg_guards.size(); i++ ){ Node g = d_neg_guards[i]; @@ -897,6 +945,9 @@ TypeNode TheorySep::getReferenceType2( Node atom, int& card, int index, Node n, Trace("sep-type-debug") << "visit : " << n << " : " << atom << " " << index << std::endl; visited[n] = -1; if( n.getKind()==kind::SEP_PTO ){ + //TODO: when THEORY_SETS supports mixed Int/Real sets + //TypeNode tn1 = n[0].getType().getBaseType(); + //TypeNode tn2 = n[1].getType().getBaseType(); TypeNode tn1 = n[0].getType(); TypeNode tn2 = n[1].getType(); if( quantifiers::TermDb::hasBoundVarAttr( n[0] ) ){ @@ -908,6 +959,13 @@ TypeNode TheorySep::getReferenceType2( Node atom, int& card, int index, Node n, } std::map< TypeNode, TypeNode >::iterator itt = d_loc_to_data_type.find( tn1 ); if( itt==d_loc_to_data_type.end() ){ + if( !d_loc_to_data_type.empty() ){ + TypeNode te1 = d_loc_to_data_type.begin()->first; + std::stringstream ss; + ss << "ERROR: specifying heap constraints for two different types : " << tn1 << " -> " << tn2 << " and " << te1 << " -> " << d_loc_to_data_type[te1] << std::endl; + throw LogicException(ss.str()); + Assert( false ); + } 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{ @@ -984,7 +1042,7 @@ Node TheorySep::getBaseLabel( TypeNode tn ) { 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, "" ); + Node n_lbl = NodeManager::currentNM()->mkSkolem( ss.str(), ltn, "base label" ); d_base_label[tn] = n_lbl; //make reference bound Trace("sep") << "Make reference bound label for " << tn << std::endl; @@ -1021,6 +1079,13 @@ Node TheorySep::getBaseLabel( TypeNode tn ) { //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 ); + + //assert that nil ref is not in base label + Node nr = getNilRef( tn ); + Node nrlem = NodeManager::currentNM()->mkNode( kind::MEMBER, nr, n_lbl ).negate(); + Trace("sep-lemma") << "Sep::Lemma: sep.nil not in base label " << tn << " : " << nrlem << std::endl; + d_out->lemma( nrlem ); + return n_lbl; }else{ return it->second; @@ -1031,13 +1096,18 @@ 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; + setNilRef( tn, nil ); return nil; }else{ return it->second; } } +void TheorySep::setNilRef( TypeNode tn, Node n ) { + Assert( n.getType()==tn ); + d_nil_ref[tn] = n; +} + Node TheorySep::mkUnion( TypeNode tn, std::vector< Node >& locs ) { Node u; if( locs.empty() ){ @@ -1067,7 +1137,7 @@ Node TheorySep::getLabel( Node atom, int child, Node lbl ) { 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, "" ); + Node n_lbl = NodeManager::currentNM()->mkSkolem( ss.str(), ltn, "sep label" ); d_label_map[atom][lbl][child] = n_lbl; d_label_map_parent[n_lbl] = lbl; return n_lbl; @@ -1149,11 +1219,40 @@ Node TheorySep::instantiateLabel( Node n, Node o_lbl, Node lbl, Node lbl_v, std: return Node::null(); } } + Node empSet = NodeManager::currentNM()->mkConst(EmptySet(rtn.toType())); if( n.getKind()==kind::SEP_STAR ){ + //disjoint contraints + Node vsu; + std::vector< Node > vs; + 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; + Node lbl_mval = d_label_model[sub_lbl].getValue( rtn ); + for( unsigned j=0; j<vs.size(); j++ ){ + children.push_back( NodeManager::currentNM()->mkNode( kind::INTERSECTION, lbl_mval, vs[j] ).eqNode( empSet ) ); + } + vs.push_back( lbl_mval ); + if( vsu.isNull() ){ + vsu = lbl_mval; + }else{ + vsu = NodeManager::currentNM()->mkNode( kind::UNION, vsu, lbl_mval ); + } + } + children.push_back( vsu.eqNode( lbl ) ); + + //return the lemma Assert( children.size()>1 ); return NodeManager::currentNM()->mkNode( kind::AND, children ); }else{ - return NodeManager::currentNM()->mkNode( kind::OR, children[0].negate(), children[1] ); + std::vector< Node > wchildren; + //disjoint constraints + Node sub_lbl_0 = d_label_map[n][lbl][0]; + Node lbl_mval_0 = d_label_model[sub_lbl_0].getValue( rtn ); + wchildren.push_back( NodeManager::currentNM()->mkNode( kind::INTERSECTION, lbl_mval_0, lbl ).eqNode( empSet ).negate() ); + + //return the lemma + wchildren.push_back( children[0].negate() ); + wchildren.push_back( children[1] ); + return NodeManager::currentNM()->mkNode( kind::OR, wchildren ); } }else{ //nested star/wand, label it and return @@ -1266,8 +1365,12 @@ void TheorySep::computeLabelModel( Node lbl, std::map< Node, Node >& tmodel ) { 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 ); + if( v_val.getKind()==kind::SINGLETON ){ + d_label_model[lbl].d_heap_locs_model.push_back( v_val ); + }else{ + throw Exception("Could not establish value of heap in model."); + Assert( false ); + } } //end hack for( unsigned j=0; j<d_label_model[lbl].d_heap_locs_model.size(); j++ ){ diff --git a/src/theory/sep/theory_sep.h b/src/theory/sep/theory_sep.h index 852a36721..29e7a008c 100644 --- a/src/theory/sep/theory_sep.h +++ b/src/theory/sep/theory_sep.h @@ -50,6 +50,8 @@ class TheorySep : public Theory { /** True node for predicates = false */ Node d_false; + + std::vector< Node > d_pp_nils; Node mkAnd( std::vector< TNode >& assumptions ); @@ -106,6 +108,7 @@ class TheorySep : public Theory { public: void collectModelInfo(TheoryModel* m, bool fullModel); + void postProcessModel(TheoryModel* m); ///////////////////////////////////////////////////////////////////////////// // NOTIFICATIONS @@ -126,6 +129,8 @@ class TheorySep : public Theory { void check(Effort e); + bool needsCheckLastEffort(); + private: // NotifyClass: template helper class for d_equalityEngine - handles call-back from congruence closure module @@ -243,6 +248,7 @@ class TheorySep : public Theory { //get the base label for the spatial assertion Node getBaseLabel( TypeNode tn ); Node getNilRef( TypeNode tn ); + void setNilRef( TypeNode tn, Node n ); 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 ); diff --git a/src/theory/sep/theory_sep_rewriter.cpp b/src/theory/sep/theory_sep_rewriter.cpp index d58c2c13d..3e74bd61e 100644 --- a/src/theory/sep/theory_sep_rewriter.cpp +++ b/src/theory/sep/theory_sep_rewriter.cpp @@ -24,6 +24,7 @@ namespace sep { void TheorySepRewriter::getStarChildren( Node n, std::vector< Node >& s_children, std::vector< Node >& ns_children ){ Assert( n.getKind()==kind::SEP_STAR ); + Node tr = NodeManager::currentNM()->mkConst( true ); for( unsigned i=0; i<n.getNumChildren(); i++ ){ if( n[i].getKind()==kind::SEP_EMP ){ s_children.push_back( n[i] ); @@ -36,26 +37,19 @@ void TheorySepRewriter::getStarChildren( Node n, std::vector< Node >& 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( std::find( s_children.begin(), s_children.end(), tr )==s_children.end() ){ + to_add = tr; } + }else if( temp_s_children.size()==1 ){ + to_add = temp_s_children[0]; + }else{ + to_add = NodeManager::currentNM()->mkNode( kind::AND, temp_s_children ); } 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() ){ + }else if( to_add.getKind()!=kind::SEP_EMP || s_children.empty() ){ //remove sep emp s_children.push_back( to_add ); } } diff --git a/src/theory/strings/theory_strings.cpp b/src/theory/strings/theory_strings.cpp index 57344236e..7caa1cbb1 100644 --- a/src/theory/strings/theory_strings.cpp +++ b/src/theory/strings/theory_strings.cpp @@ -37,7 +37,7 @@ namespace CVC4 { namespace theory { namespace strings { -Node TheoryStrings::TermIndex::add( Node n, unsigned index, TheoryStrings* t, Node er, std::vector< Node >& c ) { +Node TheoryStrings::TermIndex::add( TNode n, unsigned index, TheoryStrings* t, Node er, std::vector< Node >& c ) { if( index==n.getNumChildren() ){ if( d_data.isNull() ){ d_data = n; @@ -45,7 +45,7 @@ Node TheoryStrings::TermIndex::add( Node n, unsigned index, TheoryStrings* t, No return d_data; }else{ Assert( index<n.getNumChildren() ); - Node nir = t->getRepresentative( n[index] ); + TNode nir = t->getRepresentative( n[index] ); //if it is empty, and doing CONCAT, ignore if( nir==er && n.getKind()==kind::STRING_CONCAT ){ return add( n, index+1, t, er, c ); @@ -69,21 +69,20 @@ TheoryStrings::TheoryStrings(context::Context* c, context::UserContext* u, d_infer_exp(c), d_nf_pairs(c), d_loop_antec(u), - d_length_intro_vars(u), d_pregistered_terms_cache(u), d_registered_terms_cache(u), + d_length_lemma_terms_cache(u), + d_skolem_ne_reg_cache(u), d_preproc(u), d_preproc_cache(u), d_extf_infer_cache(c), + d_extf_infer_cache_u(u), 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), - d_ext_func_terms(c), + d_has_extf(c, false ), d_regexp_memberships(c), d_regexp_ucached(u), d_regexp_ccached(c), @@ -98,15 +97,28 @@ TheoryStrings::TheoryStrings(context::Context* c, context::UserContext* u, d_cardinality_lits(u), d_curr_cardinality(c, 0) { + d_extt = new ExtTheory( this ); + d_extt->addFunctionKind( kind::STRING_SUBSTR ); + d_extt->addFunctionKind( kind::STRING_STRIDOF ); + d_extt->addFunctionKind( kind::STRING_ITOS ); + d_extt->addFunctionKind( kind::STRING_U16TOS ); + d_extt->addFunctionKind( kind::STRING_U32TOS ); + d_extt->addFunctionKind( kind::STRING_STOI ); + d_extt->addFunctionKind( kind::STRING_STOU16 ); + d_extt->addFunctionKind( kind::STRING_STOU32 ); + d_extt->addFunctionKind( kind::STRING_STRREPL ); + d_extt->addFunctionKind( kind::STRING_STRCTN ); + d_extt->addFunctionKind( kind::STRING_IN_REGEXP ); + // The kinds we are treating as function application in congruence d_equalityEngine.addFunctionKind(kind::STRING_IN_REGEXP); d_equalityEngine.addFunctionKind(kind::STRING_LENGTH); d_equalityEngine.addFunctionKind(kind::STRING_CONCAT); - d_equalityEngine.addFunctionKind(kind::STRING_STRCTN); - d_equalityEngine.addFunctionKind(kind::STRING_SUBSTR); - d_equalityEngine.addFunctionKind(kind::STRING_ITOS); - d_equalityEngine.addFunctionKind(kind::STRING_STOI); if( options::stringLazyPreproc() ){ + d_equalityEngine.addFunctionKind(kind::STRING_STRCTN); + d_equalityEngine.addFunctionKind(kind::STRING_SUBSTR); + d_equalityEngine.addFunctionKind(kind::STRING_ITOS); + d_equalityEngine.addFunctionKind(kind::STRING_STOI); d_equalityEngine.addFunctionKind(kind::STRING_U16TOS); d_equalityEngine.addFunctionKind(kind::STRING_STOU16); d_equalityEngine.addFunctionKind(kind::STRING_U32TOS); @@ -130,6 +142,7 @@ TheoryStrings::~TheoryStrings() { for( std::map< Node, EqcInfo* >::iterator it = d_eqc_info.begin(); it != d_eqc_info.end(); ++it ){ delete it->second; } + delete d_extt; } Node TheoryStrings::getRepresentative( Node t ) { @@ -157,28 +170,16 @@ bool TheoryStrings::areEqual( Node a, Node b ){ bool TheoryStrings::areDisequal( Node a, Node b ){ if( a==b ){ return false; - } else { - if( a.getType().isString() ) { - for( unsigned i=0; i<2; i++ ) { - Node ac = a.getKind()==kind::STRING_CONCAT ? a[i==0 ? 0 : a.getNumChildren()-1] : a; - Node bc = b.getKind()==kind::STRING_CONCAT ? b[i==0 ? 0 : b.getNumChildren()-1] : b; - if( ac.isConst() && bc.isConst() ){ - CVC4::String as = ac.getConst<String>(); - CVC4::String bs = bc.getConst<String>(); - int slen = as.size() > bs.size() ? bs.size() : as.size(); - bool flag = i == 1 ? as.rstrncmp(bs, slen): as.strncmp(bs, slen); - if(!flag) { - return true; - } - } - } - } + }else{ if( hasTerm( a ) && hasTerm( b ) ) { - if( d_equalityEngine.areDisequal( a, b, false ) ){ - return true; - } + Node ar = d_equalityEngine.getRepresentative( a ); + Node br = d_equalityEngine.getRepresentative( b ); + return ( ar!=br && ar.isConst() && br.isConst() ) || d_equalityEngine.areDisequal( ar, br, false ); + }else{ + Node ar = getRepresentative( a ); + Node br = getRepresentative( b ); + return ar!=br && ar.isConst() && br.isConst(); } - return false; } } @@ -286,6 +287,50 @@ Node TheoryStrings::explain( TNode literal ){ } } +bool TheoryStrings::getCurrentSubstitution( int effort, std::vector< Node >& vars, + std::vector< Node >& subs, std::map< Node, std::vector< Node > >& exp ) { + Trace("strings-subs") << "getCurrentSubstitution, effort = " << effort << std::endl; + for( unsigned i=0; i<vars.size(); i++ ){ + Node n = vars[i]; + Trace("strings-subs") << " get subs for " << n << "..." << std::endl; + if( effort>=3 ){ + //model values + Node mv = d_valuation.getModel()->getRepresentative( n ); + Trace("strings-subs") << " model val : " << mv << std::endl; + subs.push_back( mv ); + }else{ + Node nr = getRepresentative( n ); + std::map< Node, Node >::iterator itc = d_eqc_to_const.find( nr ); + if( itc!=d_eqc_to_const.end() ){ + //constant equivalence classes + Trace("strings-subs") << " constant eqc : " << d_eqc_to_const_exp[nr] << " " << d_eqc_to_const_base[nr] << " " << nr << std::endl; + subs.push_back( itc->second ); + if( !d_eqc_to_const_exp[nr].isNull() ){ + exp[n].push_back( d_eqc_to_const_exp[nr] ); + } + if( !d_eqc_to_const_base[nr].isNull() ){ + addToExplanation( n, d_eqc_to_const_base[nr], exp[n] ); + } + }else if( effort>=1 && effort<3 && n.getType().isString() ){ + //normal forms + Node ns = getNormalString( d_normal_forms_base[nr], exp[n] ); + subs.push_back( ns ); + Trace("strings-subs") << " normal eqc : " << ns << " " << d_normal_forms_base[nr] << " " << nr << std::endl; + if( !d_normal_forms_base[nr].isNull() ) { + addToExplanation( n, d_normal_forms_base[nr], exp[n] ); + } + }else{ + //representative? + //Trace("strings-subs") << " representative : " << nr << std::endl; + //addToExplanation( n, nr, exp[n] ); + //subs.push_back( nr ); + subs.push_back( n ); + } + } + } + return true; +} + ///////////////////////////////////////////////////////////////////////////// // NOTIFICATIONS ///////////////////////////////////////////////////////////////////////////// @@ -509,7 +554,7 @@ Node TheoryStrings::expandDefinition(LogicRequest &logicRequest, Node node) { void TheoryStrings::check(Effort e) { - if (done() && !fullEffort(e)) { + if (done() && e<EFFORT_FULL) { return; } @@ -528,7 +573,7 @@ void TheoryStrings::check(Effort e) { preRegisterTerm( d_emptyString ); } - // Trace("strings-process") << "Theory of strings, check : " << e << std::endl; + // Trace("strings-process") << "Theory of strings, check : " << e << std::endl; Trace("strings-check") << "Theory of strings, check : " << e << std::endl; while ( !done() && !d_conflict ) { // Get all the assertions @@ -539,11 +584,6 @@ void TheoryStrings::check(Effort e) { polarity = fact.getKind() != kind::NOT; atom = polarity ? fact : fact[0]; - //run preprocess on memberships - if( options::stringLazyPreproc() ){ - checkReduction( atom, polarity ? 1 : -1, 0 ); - doPendingLemmas(); - } //assert pending fact assertPendingFact( atom, polarity, fact ); } @@ -590,7 +630,7 @@ void TheoryStrings::check(Effort e) { checkInit(); Trace("strings-process") << "Done check init, addedFact = " << !d_pending.empty() << " " << !d_lemma_cache.empty() << ", d_conflict = " << d_conflict << std::endl; if( !hasProcessed() ){ - checkExtendedFuncsEval(); + checkExtfEval(); Trace("strings-process") << "Done check extended functions eval, addedFact = " << !d_pending.empty() << " " << !d_lemma_cache.empty() << ", d_conflict = " << d_conflict << std::endl; if( !hasProcessed() ){ checkFlatForms(); @@ -604,11 +644,17 @@ void TheoryStrings::check(Effort e) { Trace("strings-process") << "Done check lengths, addedFact = " << !d_pending.empty() << " " << !d_lemma_cache.empty() << ", d_conflict = " << d_conflict << std::endl; } if( !hasProcessed() ){ - checkExtendedFuncs(); - Trace("strings-process") << "Done check extended functions, addedFact = " << !d_pending.empty() << " " << !d_lemma_cache.empty() << ", d_conflict = " << d_conflict << std::endl; + if( options::stringExp() && !options::stringGuessModel() ){ + checkExtfReductions( 2 ); + Trace("strings-process") << "Done check extended functions reduction 2, addedFact = " << !d_pending.empty() << " " << !d_lemma_cache.empty() << ", d_conflict = " << d_conflict << std::endl; + } if( !hasProcessed() ){ - checkCardinality(); - Trace("strings-process") << "Done check cardinality, addedFact = " << !d_pending.empty() << " " << !d_lemma_cache.empty() << ", d_conflict = " << d_conflict << std::endl; + checkMemberships(); + Trace("strings-process") << "Done check memberships, addedFact = " << !d_pending.empty() << " " << !d_lemma_cache.empty() << ", d_conflict = " << d_conflict << std::endl; + if( !hasProcessed() ){ + checkCardinality(); + Trace("strings-process") << "Done check cardinality, addedFact = " << !d_pending.empty() << " " << !d_lemma_cache.empty() << ", d_conflict = " << d_conflict << std::endl; + } } } } @@ -623,19 +669,38 @@ void TheoryStrings::check(Effort e) { }while( !d_conflict && !addedLemma && addedFact ); Trace("strings-check") << "Theory of strings done full effort check " << addedLemma << " " << d_conflict << std::endl; + }else if( e==EFFORT_LAST_CALL ){ + Assert( !hasProcessed() ); + Trace("strings-check") << "Theory of strings last call effort check " << std::endl; + checkExtfEval( 3 ); + checkExtfReductions( 2 ); + doPendingFacts(); + doPendingLemmas(); + Trace("strings-process") << "Done check extended functions reduction 2, addedFact = " << !d_pending.empty() << " " << !d_lemma_cache.empty() << ", d_conflict = " << d_conflict << std::endl; } Trace("strings-check") << "Theory of strings, done check : " << e << std::endl; Assert( d_pending.empty() ); Assert( d_lemma_cache.empty() ); } -void TheoryStrings::checkExtfReduction( int effort ) { - Trace("strings-process-debug") << "Checking preprocess at effort " << effort << ", #to process=" << d_ext_func_terms.size() << "..." << std::endl; - for( NodeBoolMap::iterator it = d_ext_func_terms.begin(); it != d_ext_func_terms.end(); ++it ){ - Trace("strings-process-debug2") << (*it).first << ", active=" << !(*it).second << std::endl; - if( (*it).second ){ - Node n = (*it).first; - checkReduction( n, d_extf_pol[n], effort ); +bool TheoryStrings::needsCheckLastEffort() { + if( options::stringGuessModel() ){ + return d_has_extf.get(); + }else{ + return false; + } +} + +void TheoryStrings::checkExtfReductions( int effort ) { + std::vector< Node > extf; + d_extt->getActive( extf ); + for( unsigned i=0; i<extf.size(); i++ ){ + Node n = extf[i]; + if( d_extf_info_tmp[n].d_model_active ){ + Assert( d_extf_info_tmp.find( n )!=d_extf_info_tmp.end() ); + if( checkExtfReduction( n, d_extf_info_tmp[n].d_pol, effort ) ){ + d_extt->markReduced( n ); + } if( hasProcessed() ){ return; } @@ -643,65 +708,85 @@ void TheoryStrings::checkExtfReduction( int effort ) { } } -void TheoryStrings::checkReduction( Node atom, int pol, int effort ) { +bool TheoryStrings::checkExtfReduction( Node atom, int pol, int effort ) { //determine the effort level to process the extf at // 0 - at assertion time, 1+ - after no other reduction is applicable int r_effort = -1; - if( atom.getKind()==kind::STRING_IN_REGEXP ){ - if( pol==1 && atom[1].getKind()==kind::REGEXP_RANGE ){ - r_effort = 0; - } - }else if( atom.getKind()==kind::STRING_STRCTN ){ + if( atom.getKind()==kind::STRING_STRCTN ){ if( pol==1 ){ r_effort = 1; + }else{ + Assert( pol==-1 ); + if( effort==2 ){ + Node x = atom[0]; + Node s = atom[1]; + std::vector< Node > lexp; + Node lenx = getLength( x, lexp ); + Node lens = getLength( s, lexp ); + if( areEqual( lenx, lens ) ){ + Trace("strings-extf-debug") << " resolve extf : " << atom << " based on equal lengths disequality." << std::endl; + //we can reduce to disequality when lengths are equal + if( !areDisequal( x, s ) ){ + lexp.push_back( lenx.eqNode(lens) ); + lexp.push_back( atom.negate() ); + Node xneqs = x.eqNode(s).negate(); + sendInference( lexp, xneqs, "NEG-CTN-EQL", true ); + } + return true; + }else if( !areDisequal( lenx, lens ) ){ + //split on their lenths + sendSplit( lenx, lens, "NEG-CTN-SP" ); + }else{ + r_effort = 2; + } + } } }else{ if( options::stringLazyPreproc() ){ if( atom.getKind()==kind::STRING_SUBSTR ){ - r_effort = options::stringLazyPreproc2() ? 1 : 0; - }else{ - r_effort = options::stringLazyPreproc2() ? 2 : 0; + r_effort = 1; + }else if( atom.getKind()!=kind::STRING_IN_REGEXP ){ + r_effort = 2; } } } if( effort==r_effort ){ - if( d_preproc_cache.find( atom )==d_preproc_cache.end() ){ - d_preproc_cache[ atom ] = true; - Trace("strings-process-debug") << "Process reduction for " << atom << std::endl; - if( atom.getKind()==kind::STRING_IN_REGEXP ){ - if( atom[1].getKind()==kind::REGEXP_RANGE ){ - Node eq = d_one.eqNode(NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, atom[0])); - std::vector< Node > exp_vec; - exp_vec.push_back( atom ); - sendInference( d_empty_vec, exp_vec, eq, "RE-Range-Len", true ); - } - }else if( atom.getKind()==kind::STRING_STRCTN ){ + Node c_atom = pol==-1 ? atom.negate() : atom; + if( d_preproc_cache.find( c_atom )==d_preproc_cache.end() ){ + d_preproc_cache[ c_atom ] = true; + Trace("strings-process-debug") << "Process reduction for " << atom << ", pol = " << pol << std::endl; + if( atom.getKind()==kind::STRING_STRCTN && pol==1 ){ Node x = atom[0]; Node s = atom[1]; - //would have already reduced by now - Assert( !areEqual( s, d_emptyString ) && !areEqual( s, x ) ); + //positive contains reduces to a equality Node sk1 = mkSkolemCached( x, s, sk_id_ctn_pre, "sc1" ); Node sk2 = mkSkolemCached( x, s, sk_id_ctn_post, "sc2" ); Node eq = Rewriter::rewrite( x.eqNode( mkConcat( sk1, s, sk2 ) ) ); std::vector< Node > exp_vec; exp_vec.push_back( atom ); sendInference( d_empty_vec, exp_vec, eq, "POS-CTN", true ); + //we've reduced this atom + Trace("strings-extf-debug") << " resolve extf : " << atom << " based on positive contain reduction." << std::endl; + return true; }else{ - // for STRING_SUBSTR, + // for STRING_SUBSTR, STRING_STRCTN with pol=-1, // STRING_STRIDOF, STRING_ITOS, STRING_U16TOS, STRING_U32TOS, STRING_STOI, STRING_STOU16, STRING_STOU32, STRING_STRREPL std::vector< Node > new_nodes; - Node res = d_preproc.decompose( atom, new_nodes ); - Assert( res==atom ); - if( !new_nodes.empty() ){ - Node nnlem = new_nodes.size()==1 ? new_nodes[0] : NodeManager::currentNM()->mkNode( kind::AND, new_nodes ); - nnlem = Rewriter::rewrite( nnlem ); - Trace("strings-red-lemma") << "Reduction_" << effort << " lemma : " << nnlem << std::endl; - Trace("strings-red-lemma") << "...from " << atom << std::endl; - sendInference( d_empty_vec, nnlem, "Reduction", true ); - } + Node res = d_preproc.simplify( atom, new_nodes ); + Assert( res!=atom ); + new_nodes.push_back( NodeManager::currentNM()->mkNode( res.getType().isBoolean() ? kind::IFF : kind::EQUAL, res, atom ) ); + Node nnlem = new_nodes.size()==1 ? new_nodes[0] : NodeManager::currentNM()->mkNode( kind::AND, new_nodes ); + nnlem = Rewriter::rewrite( nnlem ); + Trace("strings-red-lemma") << "Reduction_" << effort << " lemma : " << nnlem << std::endl; + Trace("strings-red-lemma") << "...from " << atom << std::endl; + sendInference( d_empty_vec, nnlem, "Reduction", true ); + //we've reduced this atom + Trace("strings-extf-debug") << " resolve extf : " << atom << " based on reduction." << std::endl; + return true; } } } + return false; } TheoryStrings::EqcInfo::EqcInfo( context::Context* c ) : d_const_term(c), d_length_term(c), d_cardinality_lem_k(c), d_normalized_length(c) { @@ -900,13 +985,21 @@ void TheoryStrings::assertPendingFact(Node atom, bool polarity, Node exp) { d_equalityEngine.assertEquality( atom, polarity, exp ); Trace("strings-pending-debug") << " Finished assert equality" << std::endl; } else { - if( atom.getKind()==kind::STRING_IN_REGEXP ) { - if( d_ext_func_terms.find( atom )==d_ext_func_terms.end() ){ - Trace("strings-extf-debug") << "Found extended function (membership) : " << atom << std::endl; - d_ext_func_terms[atom] = true; + d_equalityEngine.assertPredicate( atom, polarity, exp ); + //process extf + if( atom.getKind()==kind::STRING_IN_REGEXP ){ + d_extt->registerTerm( atom ); + if( polarity && atom[1].getKind()==kind::REGEXP_RANGE ){ + if( d_extf_infer_cache_u.find( atom )==d_extf_infer_cache_u.end() ){ + d_extf_infer_cache_u.insert( atom ); + //length of first argument is one + Node conc = d_one.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, atom[0] ) ); + Node lem = NodeManager::currentNM()->mkNode( kind::OR, atom.negate(), conc ); + Trace("strings-lemma") << "Strings::Lemma RE-Range-Len : " << lem << std::endl; + d_out->lemma( lem ); + } } } - d_equalityEngine.assertPredicate( atom, polarity, exp ); } Trace("strings-pending-debug") << " Now collect terms" << std::endl; //collect extended function terms in the atom @@ -994,15 +1087,15 @@ void TheoryStrings::checkInit() { eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, &d_equalityEngine ); while( !eqc_i.isFinished() ) { Node n = *eqc_i; - if( tn.isInteger() ){ + if( n.isConst() ){ + d_eqc_to_const[eqc] = n; + d_eqc_to_const_base[eqc] = n; + d_eqc_to_const_exp[eqc] = Node::null(); + }else if( tn.isInteger() ){ if( n.getKind()==kind::STRING_LENGTH ){ Node nr = getRepresentative( n[0] ); d_eqc_to_len_term[nr] = n[0]; } - }else if( n.isConst() ){ - d_eqc_to_const[eqc] = n; - d_eqc_to_const_base[eqc] = n; - d_eqc_to_const_exp[eqc] = Node::null(); }else if( n.getNumChildren()>0 ){ Kind k = n.getKind(); if( k!=kind::EQUAL ){ @@ -1039,16 +1132,8 @@ void TheoryStrings::checkInit() { //infer the equality sendInference( exp, n.eqNode( nc ), "I_Norm" ); }else{ - //update the extf map : only process if neither has been reduced - NodeBoolMap::const_iterator it = d_ext_func_terms.find( n ); - if( it!=d_ext_func_terms.end() ){ - if( d_ext_func_terms.find( nc )==d_ext_func_terms.end() ){ - d_ext_func_terms[nc] = (*it).second; - }else{ - d_ext_func_terms[nc] = d_ext_func_terms[nc] && (*it).second; - } - d_ext_func_terms[n] = false; - } + //mark as congruent : only process if neither has been reduced + d_extt->markCongruent( nc, n ); } //this node is congruent to another one, we can ignore it Trace("strings-process-debug") << " congruent term : " << n << std::endl; @@ -1122,77 +1207,121 @@ void TheoryStrings::checkInit() { } } -void TheoryStrings::checkExtendedFuncsEval( int effort ) { - Trace("strings-extf-list") << "Active extended functions, effort=" << effort << " : " << std::endl; - if( effort==0 ){ - d_extf_vars.clear(); - } - d_extf_pol.clear(); - d_extf_exp.clear(); - d_extf_info.clear(); - Trace("strings-extf-debug") << "Checking " << d_ext_func_terms.size() << " extended functions." << std::endl; - for( NodeBoolMap::iterator it = d_ext_func_terms.begin(); it != d_ext_func_terms.end(); ++it ){ - if( (*it).second ){ - Node n = (*it).first; - d_extf_pol[n] = 0; - if( n.getType().isBoolean() ){ - if( areEqual( n, d_true ) ){ - d_extf_pol[n] = 1; - }else if( areEqual( n, d_false ) ){ - d_extf_pol[n] = -1; - } - } - Trace("strings-extf-debug") << "Check extf " << n << ", pol = " << d_extf_pol[n] << "..." << std::endl; - if( effort==0 ){ - std::map< Node, bool > visited; - collectVars( n, d_extf_vars[n], visited ); +void TheoryStrings::checkConstantEquivalenceClasses( TermIndex* ti, std::vector< Node >& vecc ) { + Node n = ti->d_data; + if( !n.isNull() ){ + //construct the constant + Node c = mkConcat( vecc ); + if( !areEqual( n, c ) ){ + Trace("strings-debug") << "Constant eqc : " << c << " for " << n << std::endl; + Trace("strings-debug") << " "; + for( unsigned i=0; i<vecc.size(); i++ ){ + Trace("strings-debug") << vecc[i] << " "; } - //build up a best current substitution for the variables in the term, exp is explanation for substitution - std::vector< Node > var; - std::vector< Node > sub; - for( std::map< Node, std::vector< Node > >::iterator itv = d_extf_vars[n].begin(); itv != d_extf_vars[n].end(); ++itv ){ - Node nr = itv->first; - std::map< Node, Node >::iterator itc = d_eqc_to_const.find( nr ); - Node s; - Node b; - Node e; - if( itc!=d_eqc_to_const.end() ){ - b = d_eqc_to_const_base[nr]; - s = itc->second; - e = d_eqc_to_const_exp[nr]; - }else if( effort>0 ){ - b = d_normal_forms_base[nr]; - std::vector< Node > expt; - s = getNormalString( b, expt ); - e = mkAnd( expt ); + Trace("strings-debug") << std::endl; + unsigned count = 0; + unsigned countc = 0; + std::vector< Node > exp; + while( count<n.getNumChildren() ){ + while( count<n.getNumChildren() && areEqual( n[count], d_emptyString ) ){ + addToExplanation( n[count], d_emptyString, exp ); + count++; } - if( !s.isNull() ){ - bool added = false; - for( unsigned i=0; i<itv->second.size(); i++ ){ - if( itv->second[i]!=s ){ - var.push_back( itv->second[i] ); - sub.push_back( s ); - addToExplanation( itv->second[i], b, d_extf_exp[n] ); - Trace("strings-extf-debug") << " " << itv->second[i] << " --> " << s << std::endl; - added = true; - } + if( count<n.getNumChildren() ){ + Trace("strings-debug") << "...explain " << n[count] << " " << vecc[countc] << std::endl; + if( !areEqual( n[count], vecc[countc] ) ){ + Node nrr = getRepresentative( n[count] ); + Assert( !d_eqc_to_const_exp[nrr].isNull() ); + addToExplanation( n[count], d_eqc_to_const_base[nrr], exp ); + exp.push_back( d_eqc_to_const_exp[nrr] ); + }else{ + addToExplanation( n[count], vecc[countc], exp ); } - if( added ){ - addToExplanation( e, d_extf_exp[n] ); + countc++; + count++; + } + } + //exp contains an explanation of n==c + Assert( countc==vecc.size() ); + if( hasTerm( c ) ){ + sendInference( exp, n.eqNode( c ), "I_CONST_MERGE" ); + return; + }else if( !hasProcessed() ){ + Node nr = getRepresentative( n ); + std::map< Node, Node >::iterator it = d_eqc_to_const.find( nr ); + if( it==d_eqc_to_const.end() ){ + Trace("strings-debug") << "Set eqc const " << n << " to " << c << std::endl; + d_eqc_to_const[nr] = c; + d_eqc_to_const_base[nr] = n; + d_eqc_to_const_exp[nr] = mkAnd( exp ); + }else if( c!=it->second ){ + //conflict + Trace("strings-debug") << "Conflict, other constant was " << it->second << ", this constant was " << c << std::endl; + if( d_eqc_to_const_exp[nr].isNull() ){ + // n==c ^ n == c' => false + addToExplanation( n, it->second, exp ); + }else{ + // n==c ^ n == d_eqc_to_const_base[nr] == c' => false + exp.push_back( d_eqc_to_const_exp[nr] ); + addToExplanation( n, d_eqc_to_const_base[nr], exp ); } + sendInference( exp, d_false, "I_CONST_CONFLICT" ); + return; + }else{ + Trace("strings-debug") << "Duplicate constant." << std::endl; } } - Node to_reduce; - if( !var.empty() ){ - Node nr = n.substitute( var.begin(), var.end(), sub.begin(), sub.end() ); - Node nrc = Rewriter::rewrite( nr ); - if( nrc.isConst() ){ - //mark as reduced - d_ext_func_terms[n] = false; + } + } + for( std::map< TNode, TermIndex >::iterator it = ti->d_children.begin(); it != ti->d_children.end(); ++it ){ + std::map< Node, Node >::iterator itc = d_eqc_to_const.find( it->first ); + if( itc!=d_eqc_to_const.end() ){ + vecc.push_back( itc->second ); + checkConstantEquivalenceClasses( &it->second, vecc ); + vecc.pop_back(); + if( hasProcessed() ){ + break; + } + } + } +} + +void TheoryStrings::checkExtfEval( int effort ) { + Trace("strings-extf-list") << "Active extended functions, effort=" << effort << " : " << std::endl; + d_extf_info_tmp.clear(); + bool has_nreduce = false; + std::vector< Node > terms; + std::vector< Node > sterms; + std::vector< std::vector< Node > > exp; + d_extt->getInferences( effort, terms, sterms, exp ); + for( unsigned i=0; i<terms.size(); i++ ){ + Node n = terms[i]; + Node sn = sterms[i]; + //setup information about extf + d_extf_info_tmp[n].init(); + std::map< Node, ExtfInfoTmp >::iterator itit = d_extf_info_tmp.find( n ); + if( n.getType().isBoolean() ){ + if( areEqual( n, d_true ) ){ + itit->second.d_pol = 1; + }else if( areEqual( n, d_false ) ){ + itit->second.d_pol = -1; + } + } + Trace("strings-extf-debug") << "Check extf " << n << " == " << sn << ", pol = " << itit->second.d_pol << ", effort=" << effort << "..." << std::endl; + //do the inference + Node to_reduce; + if( n!=sn ){ + itit->second.d_exp.insert( itit->second.d_exp.end(), exp[i].begin(), exp[i].end() ); + // inference is rewriting the substituted node + Node nrc = Rewriter::rewrite( sn ); + //if rewrites to a constant, then do the inference and mark as reduced + if( nrc.isConst() ){ + if( effort<3 ){ + d_extt->markReduced( n ); Trace("strings-extf-debug") << " resolvable by evaluation..." << std::endl; std::vector< Node > exps; Trace("strings-extf-debug") << " get symbolic definition..." << std::endl; - Node nrs = getSymbolicDefinition( nr, exps ); + Node nrs = getSymbolicDefinition( sn, exps ); if( !nrs.isNull() ){ Trace("strings-extf-debug") << " rewrite " << nrs << "..." << std::endl; nrs = Rewriter::rewrite( nrs ); @@ -1214,13 +1343,13 @@ void TheoryStrings::checkExtendedFuncsEval( int effort ) { }else{ conc = nrs.eqNode( nrc ); } - d_extf_exp[n].clear(); + itit->second.d_exp.clear(); } }else{ if( !areEqual( n, nrc ) ){ if( n.getType().isBoolean() ){ if( areEqual( n, nrc==d_true ? d_false : d_true ) ){ - d_extf_exp[n].push_back( nrc==d_true ? n.negate() : n ); + itit->second.d_exp.push_back( nrc==d_true ? n.negate() : n ); conc = d_false; }else{ conc = nrc==d_true ? n : n.negate(); @@ -1231,85 +1360,102 @@ 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", true ); + Trace("strings-extf") << " resolve extf : " << sn << " -> " << nrc << std::endl; + sendInference( itit->second.d_exp, conc, effort==0 ? "EXTF" : "EXTF-N", true ); if( d_conflict ){ Trace("strings-extf-debug") << " conflict, return." << std::endl; return; } } - }else if( ( nrc.getKind()==kind::OR && d_extf_pol[n]==-1 ) || ( nrc.getKind()==kind::AND && d_extf_pol[n]==1 ) ){ - //infer the consequence of each - d_ext_func_terms[n] = false; - d_extf_exp[n].push_back( d_extf_pol[n]==-1 ? n.negate() : n ); - Trace("strings-extf-debug") << " decomposable..." << std::endl; - Trace("strings-extf") << " resolve extf : " << nr << " -> " << nrc << ", pol = " << d_extf_pol[n] << std::endl; - for( unsigned i=0; i<nrc.getNumChildren(); i++ ){ - sendInference( d_extf_exp[n], d_extf_pol[n]==-1 ? nrc[i].negate() : nrc[i], effort==0 ? "EXTF_d" : "EXTF_d-N" ); - } }else{ - to_reduce = nrc; + //check if it is already equal, if so, mark as reduced. Otherwise, do nothing. + if( areEqual( n, nrc ) ){ + Trace("strings-extf") << " resolved extf, since satisfied by model: " << n << std::endl; + itit->second.d_model_active = false; + } + } + //if it reduces to a conjunction, infer each and reduce + }else if( ( nrc.getKind()==kind::OR && itit->second.d_pol==-1 ) || ( nrc.getKind()==kind::AND && itit->second.d_pol==1 ) ){ + Assert( effort<3 ); + d_extt->markReduced( n ); + itit->second.d_exp.push_back( itit->second.d_pol==-1 ? n.negate() : n ); + Trace("strings-extf-debug") << " decomposable..." << std::endl; + Trace("strings-extf") << " resolve extf : " << sn << " -> " << nrc << ", pol = " << itit->second.d_pol << std::endl; + for( unsigned i=0; i<nrc.getNumChildren(); i++ ){ + sendInference( itit->second.d_exp, itit->second.d_pol==-1 ? nrc[i].negate() : nrc[i], effort==0 ? "EXTF_d" : "EXTF_d-N" ); } }else{ - to_reduce = n; + to_reduce = nrc; + } + }else{ + to_reduce = sterms[i]; + } + //if not reduced + if( !to_reduce.isNull() ){ + Assert( effort<3 ); + if( effort==1 ){ + Trace("strings-extf") << " cannot rewrite extf : " << to_reduce << std::endl; } - if( !to_reduce.isNull() ){ - if( effort==1 ){ - Trace("strings-extf") << " cannot rewrite extf : " << to_reduce << std::endl; + checkExtfInference( n, to_reduce, itit->second, effort ); + if( Trace.isOn("strings-extf-list") ){ + Trace("strings-extf-list") << " * " << to_reduce; + if( itit->second.d_pol!=0 ){ + Trace("strings-extf-list") << ", pol = " << itit->second.d_pol; } - checkExtfInference( n, to_reduce, effort ); - if( Trace.isOn("strings-extf-list") ){ - Trace("strings-extf-list") << " * " << to_reduce; - if( d_extf_pol[n]!=0 ){ - Trace("strings-extf-list") << ", pol = " << d_extf_pol[n]; - } - if( n!=to_reduce ){ - Trace("strings-extf-list") << ", from " << n; - } - Trace("strings-extf-list") << std::endl; + if( n!=to_reduce ){ + Trace("strings-extf-list") << ", from " << n; } + Trace("strings-extf-list") << std::endl; + } + if( d_extt->isActive( n ) && itit->second.d_model_active ){ + has_nreduce = true; } - }else{ - Trace("strings-extf-debug") << " already reduced " << (*it).first << std::endl; } } + d_has_extf = has_nreduce; } -void TheoryStrings::checkExtfInference( Node n, Node nr, int effort ){ - int n_pol = d_extf_pol[n]; - if( n_pol!=0 ){ +void TheoryStrings::checkExtfInference( Node n, Node nr, ExtfInfoTmp& in, int effort ){ + //make additional inferences that do not contribute to the reduction of n, but may help show a refutation + if( in.d_pol!=0 ){ //add original to explanation - d_extf_exp[n].push_back( n_pol==1 ? n : n.negate() ); + in.d_exp.push_back( in.d_pol==1 ? n : n.negate() ); + + //d_extf_infer_cache stores whether we have made the inferences associated with a node n, + // this may need to be generalized if multiple inferences apply + if( nr.getKind()==kind::STRING_STRCTN ){ - if( ( n_pol==1 && nr[1].getKind()==kind::STRING_CONCAT ) || ( n_pol==-1 && nr[0].getKind()==kind::STRING_CONCAT ) ){ + if( ( in.d_pol==1 && nr[1].getKind()==kind::STRING_CONCAT ) || ( in.d_pol==-1 && nr[0].getKind()==kind::STRING_CONCAT ) ){ if( d_extf_infer_cache.find( nr )==d_extf_infer_cache.end() ){ d_extf_infer_cache.insert( nr ); + //one argument does (not) contain each of the components of the other argument - int index = n_pol==1 ? 1 : 0; + int index = in.d_pol==1 ? 1 : 0; std::vector< Node > children; children.push_back( nr[0] ); children.push_back( nr[1] ); - //Node exp_n = mkAnd( d_extf_exp[n] ); + //Node exp_n = mkAnd( exp ); for( unsigned i=0; i<nr[index].getNumChildren(); i++ ){ children[index] = nr[index][i]; Node conc = NodeManager::currentNM()->mkNode( kind::STRING_STRCTN, children ); //can mark as reduced, since model for n => model for conc - d_ext_func_terms[conc] = false; - sendInference( d_extf_exp[n], n_pol==1 ? conc : conc.negate(), "CTN_Decompose" ); + d_extt->markReduced( conc ); + sendInference( in.d_exp, in.d_pol==1 ? conc : conc.negate(), "CTN_Decompose" ); } + } }else{ //store this (reduced) assertion //Assert( effort==0 || nr[0]==getRepresentative( nr[0] ) ); - bool pol = n_pol==1; - if( std::find( d_extf_info[nr[0]].d_ctn[pol].begin(), d_extf_info[nr[0]].d_ctn[pol].end(), nr[1] )==d_extf_info[nr[0]].d_ctn[pol].end() ){ + bool pol = in.d_pol==1; + if( std::find( d_extf_info_tmp[nr[0]].d_ctn[pol].begin(), d_extf_info_tmp[nr[0]].d_ctn[pol].end(), nr[1] )==d_extf_info_tmp[nr[0]].d_ctn[pol].end() ){ Trace("strings-extf-debug") << " store contains info : " << nr[0] << " " << pol << " " << nr[1] << std::endl; - d_extf_info[nr[0]].d_ctn[pol].push_back( nr[1] ); - d_extf_info[nr[0]].d_ctn_from[pol].push_back( n ); + d_extf_info_tmp[nr[0]].d_ctn[pol].push_back( nr[1] ); + d_extf_info_tmp[nr[0]].d_ctn_from[pol].push_back( n ); //transitive closure for contains bool opol = !pol; - for( unsigned i=0; i<d_extf_info[nr[0]].d_ctn[opol].size(); i++ ){ - Node onr = d_extf_info[nr[0]].d_ctn[opol][i]; + for( unsigned i=0; i<d_extf_info_tmp[nr[0]].d_ctn[opol].size(); i++ ){ + Node onr = d_extf_info_tmp[nr[0]].d_ctn[opol][i]; Node conc = NodeManager::currentNM()->mkNode( kind::STRING_STRCTN, pol ? nr[1] : onr, pol ? onr : nr[1] ); conc = Rewriter::rewrite( conc ); bool do_infer = false; @@ -1320,24 +1466,24 @@ void TheoryStrings::checkExtfInference( Node n, Node nr, int effort ){ } if( do_infer ){ conc = conc.negate(); - std::vector< Node > exp; - exp.insert( exp.end(), d_extf_exp[n].begin(), d_extf_exp[n].end() ); - Node ofrom = d_extf_info[nr[0]].d_ctn_from[opol][i]; - Assert( d_extf_exp.find( ofrom )!=d_extf_exp.end() ); - exp.insert( exp.end(), d_extf_exp[ofrom].begin(), d_extf_exp[ofrom].end() ); - sendInference( exp, conc, "CTN_Trans" ); + std::vector< Node > exp_c; + exp_c.insert( exp_c.end(), in.d_exp.begin(), in.d_exp.end() ); + Node ofrom = d_extf_info_tmp[nr[0]].d_ctn_from[opol][i]; + Assert( d_extf_info_tmp.find( ofrom )!=d_extf_info_tmp.end() ); + exp_c.insert( exp_c.end(), d_extf_info_tmp[ofrom].d_exp.begin(), d_extf_info_tmp[ofrom].d_exp.end() ); + sendInference( exp_c, conc, "CTN_Trans" ); } } }else{ Trace("strings-extf-debug") << " redundant." << std::endl; - d_ext_func_terms[n] = false; + d_extt->markReduced( n ); } } } } } -void TheoryStrings::collectVars( Node n, std::map< Node, std::vector< Node > >& vars, std::map< Node, bool >& visited ) { +void TheoryStrings::collectVars( Node n, std::vector< Node >& vars, std::map< Node, bool >& visited ) { if( !n.isConst() ){ if( visited.find( n )==visited.end() ){ visited[n] = true; @@ -1346,8 +1492,9 @@ void TheoryStrings::collectVars( Node n, std::map< Node, std::vector< Node > >& collectVars( n[i], vars, visited ); } }else{ - Node nr = getRepresentative( n ); - vars[nr].push_back( n ); + //Node nr = getRepresentative( n ); + //vars[nr].push_back( n ); + vars.push_back( n ); } } } @@ -1387,6 +1534,14 @@ Node TheoryStrings::getSymbolicDefinition( Node n, std::vector< Node >& exp ) { } } +Node TheoryStrings::getConstantEqc( Node eqc ) { + std::map< Node, Node >::iterator it = d_eqc_to_const.find( eqc ); + if( it!=d_eqc_to_const.end() ){ + return it->second; + }else{ + return Node::null(); + } +} void TheoryStrings::debugPrintFlatForms( const char * tc ){ for( unsigned k=0; k<d_strings_eqc.size(); k++ ){ @@ -1429,6 +1584,31 @@ void TheoryStrings::debugPrintFlatForms( const char * tc ){ Trace( tc ) << std::endl; } +void TheoryStrings::debugPrintNormalForms( const char * tc ) { +} + +struct sortConstLength { + std::map< Node, unsigned > d_const_length; + bool operator() (Node i, Node j) { + std::map< Node, unsigned >::iterator it_i = d_const_length.find( i ); + std::map< Node, unsigned >::iterator it_j = d_const_length.find( j ); + if( it_i==d_const_length.end() ){ + if( it_j==d_const_length.end() ){ + return i<j; + }else{ + return false; + } + }else{ + if( it_j==d_const_length.end() ){ + return true; + }else{ + return it_i->second<it_j->second; + } + } + } +}; + + void TheoryStrings::checkFlatForms() { //first check for cycles, while building ordering of equivalence classes d_eqc.clear(); @@ -1439,6 +1619,17 @@ void TheoryStrings::checkFlatForms() { std::vector< Node > eqc; eqc.insert( eqc.end(), d_strings_eqc.begin(), d_strings_eqc.end() ); d_strings_eqc.clear(); + if( options::stringBinaryCsp() ){ + //sort: process smallest constants first (necessary if doing binary splits) + sortConstLength scl; + for( unsigned i=0; i<eqc.size(); i++ ){ + std::map< Node, Node >::iterator itc = d_eqc_to_const.find( eqc[i] ); + if( itc!=d_eqc_to_const.end() ){ + scl.d_const_length[eqc[i]] = itc->second.getConst<String>().size(); + } + } + std::sort( eqc.begin(), eqc.end(), scl ); + } for( unsigned i=0; i<eqc.size(); i++ ){ std::vector< Node > curr; std::vector< Node > exp; @@ -1454,14 +1645,50 @@ void TheoryStrings::checkFlatForms() { Trace("strings-ff") << "Flat forms : " << std::endl; debugPrintFlatForms( "strings-ff" ); } + //inferences without recursively expanding flat forms + + //(1) approximate equality by containment, infer conflicts for( unsigned k=0; k<d_strings_eqc.size(); k++ ){ Node eqc = d_strings_eqc[k]; - Node c; - std::map< Node, Node >::iterator itc = d_eqc_to_const.find( eqc ); - if( itc!=d_eqc_to_const.end() ){ - c = itc->second; //use? + Node c = getConstantEqc( eqc ); + if( !c.isNull() ){ + //if equivalence class is constant, all component constants in flat forms must be contained in it, in order + std::map< Node, std::vector< Node > >::iterator it = d_eqc.find( eqc ); + if( it!=d_eqc.end() ){ + for( unsigned i=0; i<it->second.size(); i++ ){ + Node n = it->second[i]; + int firstc, lastc; + if( !TheoryStringsRewriter::canConstantContainList( c, d_flat_form[n], firstc, lastc ) ){ + Trace("strings-ff-debug") << "Flat form for " << n << " cannot be contained in constant " << c << std::endl; + Trace("strings-ff-debug") << " indices = " << firstc << "/" << lastc << std::endl; + //conflict, explanation is n = base ^ base = c ^ relevant porition of ( n = f[n] ) + std::vector< Node > exp; + Assert( d_eqc_to_const_base.find( eqc )!=d_eqc_to_const_base.end() ); + addToExplanation( n, d_eqc_to_const_base[eqc], exp ); + Assert( d_eqc_to_const_exp.find( eqc )!=d_eqc_to_const_exp.end() ); + if( !d_eqc_to_const_exp[eqc].isNull() ){ + exp.push_back( d_eqc_to_const_exp[eqc] ); + } + for( int e=firstc; e<=lastc; e++ ){ + if( d_flat_form[n][e].isConst() ){ + Assert( e>=0 && e<(int)d_flat_form_index[n].size() ); + Assert( d_flat_form_index[n][e]>=0 && d_flat_form_index[n][e]<(int)n.getNumChildren() ); + addToExplanation( d_flat_form[n][e], n[d_flat_form_index[n][e]], exp ); + } + } + Node conc = d_false; + sendInference( exp, conc, "F_NCTN" ); + return; + } + } + } } + } + + //(2) scan lists, unification to infer conflicts and equalities + for( unsigned k=0; k<d_strings_eqc.size(); k++ ){ + Node eqc = d_strings_eqc[k]; std::map< Node, std::vector< Node > >::iterator it = d_eqc.find( eqc ); if( it!=d_eqc.end() && it->second.size()>1 ){ //iterate over start index @@ -1503,7 +1730,7 @@ void TheoryStrings::checkFlatForms() { } }else{ Node curr = d_flat_form[a][count]; - Node curr_c = d_eqc_to_const[curr]; + Node curr_c = getConstantEqc( curr ); Node ac = a[d_flat_form_index[a][count]]; std::vector< Node > lexp; Node lcurr = getLength( ac, lexp ); @@ -1529,7 +1756,7 @@ void TheoryStrings::checkFlatForms() { Node bc = b[d_flat_form_index[b][count]]; inelig.push_back( b ); Assert( !areEqual( curr, cc ) ); - Node cc_c = d_eqc_to_const[cc]; + Node cc_c = getConstantEqc( cc ); if( !curr_c.isNull() && !cc_c.isNull() ){ //check for constant conflict int index; @@ -1582,7 +1809,13 @@ void TheoryStrings::checkFlatForms() { //explain why other components up to now are empty for( unsigned t=0; t<2; t++ ){ Node c = t==0 ? a : b; - int jj = t==0 ? d_flat_form_index[a][count] : ( inf_type==2 ? ( r==0 ? c.getNumChildren() : -1 ) : d_flat_form_index[b][count] ); + int jj; + if( inf_type==3 || ( t==1 && inf_type==2 ) ){ + //explain all the empty components for F_EndpointEq, all for the short end for F_EndpointEmp + jj = r==0 ? c.getNumChildren() : -1; + }else{ + jj = t==0 ? d_flat_form_index[a][count] : d_flat_form_index[b][count]; + } if( r==0 ){ for( int j=0; j<jj; j++ ){ if( areEqual( c[j], d_emptyString ) ){ @@ -1597,10 +1830,9 @@ void TheoryStrings::checkFlatForms() { } } } - //if( exp_n.empty() ){ - sendInference( exp, conc, inf_type==0? "F_Const" : ( inf_type==1 ? "F_LengthEq" : ( inf_type==2 ? "F_Endpoint" : "F_EndpointEq" ) ) ); - //}else{ - //} + //notice that F_EndpointEmp is not typically applied, since strict prefix equality ( a.b = a ) where a,b non-empty + // is conflicting by arithmetic len(a.b)=len(a)+len(b)!=len(a) when len(b)!=0. + sendInference( exp, conc, inf_type==0 ? "F_Const" : ( inf_type==1 ? "F_Unify" : ( inf_type==2 ? "F_EndpointEmp" : "F_EndpointEq" ) ) ); if( d_conflict ){ return; }else{ @@ -1621,7 +1853,7 @@ void TheoryStrings::checkFlatForms() { if( !hasProcessed() ){ // simple extended func reduction Trace("strings-process") << "Check extended function reduction effort=1..." << std::endl; - checkExtfReduction( 1 ); + checkExtfReductions( 1 ); Trace("strings-process") << "Done check extended function reduction" << std::endl; } } @@ -1720,41 +1952,43 @@ void TheoryStrings::checkNormalForms(){ d_normal_forms.clear(); d_normal_forms_exp.clear(); std::map< Node, Node > nf_to_eqc; + std::map< Node, Node > eqc_to_nf; std::map< Node, Node > eqc_to_exp; for( unsigned i=0; i<d_strings_eqc.size(); i++ ) { Node eqc = d_strings_eqc[i]; Trace("strings-process-debug") << "- Verify normal forms are the same for " << eqc << std::endl; - std::vector< Node > nf; - std::vector< Node > nf_exp; - normalizeEquivalenceClass( eqc, nf, nf_exp ); + normalizeEquivalenceClass( eqc ); Trace("strings-debug") << "Finished normalizing eqc..." << std::endl; if( hasProcessed() ){ return; }else{ - Node nf_term = mkConcat( nf ); - if( nf_to_eqc.find( nf_term )!=nf_to_eqc.end() ) { - //Trace("strings-debug") << "Merge because of normal form : " << eqc << " and " << nf_to_eqc[nf_term] << " both have normal form " << nf_term << std::endl; + Node nf_term = mkConcat( d_normal_forms[eqc] ); + std::map< Node, Node >::iterator itn = nf_to_eqc.find( nf_term ); + if( itn!=nf_to_eqc.end() ){ //two equivalence classes have same normal form, merge - nf_exp.push_back( eqc_to_exp[nf_to_eqc[nf_term]] ); - Node eq = eqc.eqNode( nf_to_eqc[nf_term] ); + std::vector< Node > nf_exp; + nf_exp.push_back( mkAnd( d_normal_forms_exp[eqc] ) ); + nf_exp.push_back( eqc_to_exp[itn->second] ); + Node eq = d_normal_forms_base[eqc].eqNode( d_normal_forms_base[itn->second] ); sendInference( nf_exp, eq, "Normal_Form" ); } else { nf_to_eqc[nf_term] = eqc; - eqc_to_exp[eqc] = mkAnd( nf_exp ); + eqc_to_nf[eqc] = nf_term; + eqc_to_exp[eqc] = mkAnd( d_normal_forms_exp[eqc] ); } } Trace("strings-process-debug") << "Done verifying normal forms are the same for " << eqc << std::endl; } - - if(Trace.isOn("strings-nf")) { - Trace("strings-nf") << "**** Normal forms are : " << std::endl; - for( std::map< Node, Node >::iterator it = nf_to_eqc.begin(); it != nf_to_eqc.end(); ++it ){ - Trace("strings-nf") << " N[" << it->second << "] = " << it->first << std::endl; - } - Trace("strings-nf") << std::endl; - } if( !hasProcessed() ){ - checkExtendedFuncsEval( 1 ); + if(Trace.isOn("strings-nf")) { + Trace("strings-nf") << "**** Normal forms are : " << std::endl; + for( std::map< Node, Node >::iterator it = eqc_to_exp.begin(); it != eqc_to_exp.end(); ++it ){ + Trace("strings-nf") << " N[" << it->first << "] (base " << d_normal_forms_base[it->first] << ") = " << eqc_to_nf[it->first] << std::endl; + Trace("strings-nf") << " exp: " << it->second << std::endl; + } + Trace("strings-nf") << std::endl; + } + checkExtfEval( 1 ); Trace("strings-process-debug") << "Done check extended functions re-eval, addedFact = " << !d_pending.empty() << " " << !d_lemma_cache.empty() << ", d_conflict = " << d_conflict << std::endl; if( !hasProcessed() ){ if( !options::stringEagerLen() ){ @@ -1772,97 +2006,78 @@ void TheoryStrings::checkNormalForms(){ } } -//nf_exp is conjunction -bool TheoryStrings::normalizeEquivalenceClass( Node eqc, std::vector< Node > & nf, std::vector< Node > & nf_exp ) { +//compute d_normal_forms_(base,exp,exp_depend)[eqc] +void TheoryStrings::normalizeEquivalenceClass( Node eqc ) { Trace("strings-process-debug") << "Process equivalence class " << eqc << std::endl; if( areEqual( eqc, d_emptyString ) ) { +#ifdef CVC4_ASSERTIONS for( unsigned j=0; j<d_eqc[eqc].size(); j++ ){ Node n = d_eqc[eqc][j]; for( unsigned i=0; i<n.getNumChildren(); i++ ){ Assert( areEqual( n[i], d_emptyString ) ); } } +#endif //do nothing Trace("strings-process-debug") << "Return process equivalence class " << eqc << " : empty." << std::endl; d_normal_forms_base[eqc] = d_emptyString; d_normal_forms[eqc].clear(); d_normal_forms_exp[eqc].clear(); - return true; } else { - bool result; - if( d_normal_forms.find(eqc)==d_normal_forms.end() ){ - //phi => t = s1 * ... * sn - // normal form for each non-variable term in this eqc (s1...sn) - std::vector< std::vector< Node > > normal_forms; - // explanation for each normal form (phi) - std::vector< std::vector< Node > > normal_forms_exp; - // dependency information - std::vector< std::map< Node, std::map< bool, int > > > normal_forms_exp_depend; - // record terms for each normal form (t) - std::vector< Node > normal_form_src; - //Get Normal Forms - result = getNormalForms(eqc, normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend); - if( hasProcessed() ){ - return true; - }else if( result ){ - if( processNEqc(normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend) ){ - return true; - } - } - //construct the normal form - if( normal_forms.empty() ){ - Trace("strings-solve-debug2") << "construct the normal form" << std::endl; - getConcatVec( eqc, nf ); - d_normal_forms_base[eqc] = eqc; - }else{ - int nf_index = 0; - //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() ); - //Trace("strings-solve-debug2") << "take normal form ... done" << std::endl; - //d_normal_forms_base[eqc] = normal_form_src[nf_index]; - ///* - std::vector< Node >::iterator itn = std::find( normal_form_src.begin(), normal_form_src.end(), eqc ); - if( itn!=normal_form_src.end() ){ - nf_index = itn - normal_form_src.begin(); - Trace("strings-solve-debug2") << "take normal form " << nf_index << std::endl; - Assert( normal_form_src[nf_index]==eqc ); - }else{ - //just take the first normal form - Trace("strings-solve-debug2") << "take the first normal form" << std::endl; - } - 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] ) ); - } - Trace("strings-solve-debug2") << "take normal form ... done" << std::endl; - d_normal_forms_base[eqc] = normal_form_src[nf_index]; - //*/ - //track dependencies - for( unsigned i=0; i<normal_forms_exp[nf_index].size(); i++ ){ - Node exp = normal_forms_exp[nf_index][i]; - for( unsigned r=0; r<2; r++ ){ - d_normal_forms_exp_depend[eqc][exp][r==0] = normal_forms_exp_depend[nf_index][exp][r==0]; - } - } - } - - d_normal_forms[eqc].insert( d_normal_forms[eqc].end(), nf.begin(), nf.end() ); - d_normal_forms_exp[eqc].insert( d_normal_forms_exp[eqc].end(), nf_exp.begin(), nf_exp.end() ); - - Trace("strings-process-debug") << "Return process equivalence class " << eqc << " : returned, size = " << nf.size() << std::endl; + Assert( d_normal_forms.find(eqc)==d_normal_forms.end() ); + //phi => t = s1 * ... * sn + // normal form for each non-variable term in this eqc (s1...sn) + std::vector< std::vector< Node > > normal_forms; + // explanation for each normal form (phi) + std::vector< std::vector< Node > > normal_forms_exp; + // dependency information + std::vector< std::map< Node, std::map< bool, int > > > normal_forms_exp_depend; + // record terms for each normal form (t) + std::vector< Node > normal_form_src; + // get normal forms + getNormalForms(eqc, normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend); + if( hasProcessed() ){ + return; + } + // process the normal forms + processNEqc( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend ); + if( hasProcessed() ){ + return; + } + //debugPrintNormalForms( "strings-solve", eqc, normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend ); + + //construct the normal form + Assert( !normal_forms.empty() ); + + int nf_index = 0; + std::vector< Node >::iterator itn = std::find( normal_form_src.begin(), normal_form_src.end(), eqc ); + if( itn!=normal_form_src.end() ){ + nf_index = itn - normal_form_src.begin(); + Trace("strings-solve-debug2") << "take normal form " << nf_index << std::endl; + Assert( normal_form_src[nf_index]==eqc ); }else{ - Trace("strings-process-debug") << "Return process equivalence class " << eqc << " : already computed, size = " << d_normal_forms[eqc].size() << std::endl; - nf.insert( nf.end(), d_normal_forms[eqc].begin(), d_normal_forms[eqc].end() ); - nf_exp.insert( nf_exp.end(), d_normal_forms_exp[eqc].begin(), d_normal_forms_exp[eqc].end() ); - result = true; + //just take the first normal form + Trace("strings-solve-debug2") << "take the first normal form" << std::endl; + } + d_normal_forms[eqc].insert( d_normal_forms[eqc].end(), normal_forms[nf_index].begin(), normal_forms[nf_index].end() ); + d_normal_forms_exp[eqc].insert( d_normal_forms_exp[eqc].end(), normal_forms_exp[nf_index].begin(), normal_forms_exp[nf_index].end() ); + Trace("strings-solve-debug2") << "take normal form ... done" << std::endl; + d_normal_forms_base[eqc] = normal_form_src[nf_index]; + //track dependencies + for( unsigned i=0; i<normal_forms_exp[nf_index].size(); i++ ){ + Node exp = normal_forms_exp[nf_index][i]; + for( unsigned r=0; r<2; r++ ){ + d_normal_forms_exp_depend[eqc][exp][r==0] = normal_forms_exp_depend[nf_index][exp][r==0]; + } } - return result; + Trace("strings-process-debug") << "Return process equivalence class " << eqc << " : returned, size = " << d_normal_forms[eqc].size() << std::endl; } } -bool TheoryStrings::getNormalForms( Node &eqc, std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, +void TheoryStrings::getNormalForms( Node &eqc, std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend ) { + //constant for equivalence class + Node eqc_non_c = eqc; Trace("strings-process-debug") << "Get normal forms " << eqc << std::endl; eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, &d_equalityEngine ); while( !eqc_i.isFinished() ){ @@ -1915,8 +2130,9 @@ bool TheoryStrings::getNormalForms( Node &eqc, std::vector< std::vector< Node > } } } - if( nr!=n[i] ){ - Node eq = n[i].eqNode( nr ); + if( d_normal_forms_base[nr]!=n[i] ){ + Assert( d_normal_forms_base.find( nr )!=d_normal_forms_base.end() ); + Node eq = n[i].eqNode( d_normal_forms_base[nr] ); nf_exp_n.push_back( eq ); //track depends nf_exp_depend_n[eq][false] = orig_size; @@ -1950,25 +2166,25 @@ bool TheoryStrings::getNormalForms( Node &eqc, std::vector< std::vector< Node > //this was redundant: combination of self + empty string(s) Node nn = nf_n.size()==0 ? d_emptyString : nf_n[0]; Assert( areEqual( nn, eqc ) ); - //Assert( areEqual( nf_n[0], eqc ) ); - /* - if( !areEqual( nn, eqc ) ){ - std::vector< Node > ant; - ant.insert( ant.end(), nf_exp_n.begin(), nf_exp_n.end() ); - ant.push_back( n.eqNode( eqc ) ); - Node conc = Rewriter::rewrite( nn.eqNode( eqc ) ); - sendInference( ant, conc, "CYCLE-T" ); - return true; - } - */ } + }else{ + eqc_non_c = n; } } ++eqc_i; } - if(Trace.isOn("strings-solve")) { - if( !normal_forms.empty() ) { + if( normal_forms.empty() ) { + Trace("strings-solve-debug2") << "construct the normal form" << std::endl; + //do not choose a concat here use "eqc_non_c" (in this case they have non-trivial explanation why they normalize to self) + std::vector< Node > eqc_non_c_nf; + getConcatVec( eqc_non_c, eqc_non_c_nf ); + normal_forms.push_back( eqc_non_c_nf ); + normal_form_src.push_back( eqc_non_c ); + normal_forms_exp.push_back( std::vector< Node >() ); + normal_forms_exp_depend.push_back( std::map< Node, std::map< bool, int > >() ); + }else{ + if(Trace.isOn("strings-solve")) { Trace("strings-solve") << "--- Normal forms for equivlance class " << eqc << " : " << std::endl; for( unsigned i=0; i<normal_forms.size(); i++ ) { Trace("strings-solve") << "#" << i << " (from " << normal_form_src[i] << ") : "; @@ -2003,263 +2219,173 @@ bool TheoryStrings::getNormalForms( Node &eqc, std::vector< std::vector< Node > } else { Trace("strings-solve") << "--- Single normal form for equivalence class " << eqc << std::endl; } + + //if equivalence class is constant, approximate as containment, infer conflicts + Node c = getConstantEqc( eqc ); + if( !c.isNull() ){ + Trace("strings-solve") << "Eqc is constant " << c << std::endl; + for( unsigned i=0; i<normal_forms.size(); i++ ) { + int firstc, lastc; + if( !TheoryStringsRewriter::canConstantContainList( c, normal_forms[i], firstc, lastc ) ){ + Node n = normal_form_src[i]; + //conflict + Trace("strings-solve") << "Normal form for " << n << " cannot be contained in constant " << c << std::endl; + //conflict, explanation is n = base ^ base = c ^ relevant porition of ( n = N[n] ) + std::vector< Node > exp; + Assert( d_eqc_to_const_base.find( eqc )!=d_eqc_to_const_base.end() ); + addToExplanation( n, d_eqc_to_const_base[eqc], exp ); + Assert( d_eqc_to_const_exp.find( eqc )!=d_eqc_to_const_exp.end() ); + if( !d_eqc_to_const_exp[eqc].isNull() ){ + exp.push_back( d_eqc_to_const_exp[eqc] ); + } + //TODO: this can be minimized based on firstc/lastc, normal_forms_exp_depend + exp.insert( exp.end(), normal_forms_exp[i].begin(), normal_forms_exp[i].end() ); + Node conc = d_false; + sendInference( exp, conc, "N_NCTN" ); + } + } + } } - return true; } -void TheoryStrings::getExplanationVectorForPrefix( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, - std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend, - unsigned i, unsigned j, int index, bool isRev, std::vector< Node >& curr_exp ) { +void TheoryStrings::getExplanationVectorForPrefix( std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend, + unsigned i, int index, bool isRev, std::vector< Node >& curr_exp ) { if( index==-1 || !options::stringMinPrefixExplain() ){ curr_exp.insert(curr_exp.end(), normal_forms_exp[i].begin(), normal_forms_exp[i].end() ); - curr_exp.insert(curr_exp.end(), normal_forms_exp[j].begin(), normal_forms_exp[j].end() ); }else{ - Trace("strings-explain-prefix") << "Get explanation for prefix " << index << " of normal forms " << i << " and " << j << ", reverse = " << isRev << std::endl; - for( unsigned r=0; r<2; r++ ){ - int tindex = r==0 ? i : j; - for( unsigned k=0; k<normal_forms_exp[tindex].size(); k++ ){ - Node exp = normal_forms_exp[tindex][k]; - int dep = normal_forms_exp_depend[tindex][exp][isRev]; - if( dep<=index ){ - curr_exp.push_back( exp ); - Trace("strings-explain-prefix-debug") << " include : " << exp << std::endl; - }else{ - Trace("strings-explain-prefix-debug") << " exclude : " << exp << std::endl; - } + for( unsigned k=0; k<normal_forms_exp[i].size(); k++ ){ + Node exp = normal_forms_exp[i][k]; + int dep = normal_forms_exp_depend[i][exp][isRev]; + if( dep<=index ){ + curr_exp.push_back( exp ); + Trace("strings-explain-prefix-debug") << " include : " << exp << std::endl; + }else{ + Trace("strings-explain-prefix-debug") << " exclude : " << exp << std::endl; } } - Trace("strings-explain-prefix") << "Included " << curr_exp.size() << " / " << ( normal_forms_exp[i].size() + normal_forms_exp[j].size() ) << std::endl; } - if( normal_form_src[i]!=normal_form_src[j] ){ - curr_exp.push_back( normal_form_src[i].eqNode( normal_form_src[j] ) ); +} + +void TheoryStrings::getExplanationVectorForPrefixEq( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, + std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend, + unsigned i, unsigned j, int index_i, int index_j, bool isRev, std::vector< Node >& curr_exp ) { + Trace("strings-explain-prefix") << "Get explanation for prefix " << index_i << ", " << index_j << " of normal forms " << i << " and " << j << ", reverse = " << isRev << std::endl; + for( unsigned r=0; r<2; r++ ){ + getExplanationVectorForPrefix( normal_forms_exp, normal_forms_exp_depend, r==0 ? i : j, r==0 ? index_i : index_j, isRev, curr_exp ); } + Trace("strings-explain-prefix") << "Included " << curr_exp.size() << " / " << ( normal_forms_exp[i].size() + normal_forms_exp[j].size() ) << std::endl; + addToExplanation( normal_form_src[i], normal_form_src[j], curr_exp ); } -bool TheoryStrings::processNEqc( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, - std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend ) { - bool flag_lb = false; - std::vector< Node > c_lb_exp; - int c_i, c_j, c_loop_n_index, c_other_n_index, c_loop_index, c_index; + +void TheoryStrings::processNEqc( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, + std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend ){ + //the possible inferences + std::vector< InferInfo > pinfer; + // loop over all pairs for(unsigned i=0; i<normal_forms.size()-1; i++) { //unify each normalform[j] with normal_forms[i] for(unsigned j=i+1; j<normal_forms.size(); j++ ) { + //ensure that normal_forms[i] and normal_forms[j] are the same modulo equality, add to pinfer if not Trace("strings-solve") << "Strings: Process normal form #" << i << " against #" << j << "..." << std::endl; if( isNormalFormPair( normal_form_src[i], normal_form_src[j] ) ) { Trace("strings-solve") << "Strings: Already cached." << std::endl; }else{ //process the reverse direction first (check for easy conflicts and inferences) - if( processReverseNEq( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j ) ){ - return true; + unsigned rindex = 0; + processReverseNEq( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, rindex, 0, pinfer ); + if( hasProcessed() ){ + return; + }else if( !pinfer.empty() && pinfer.back().d_id==1 ){ + break; } + //AJR: for less aggressive endpoint inference + //rindex = 0; - //ensure that normal_forms[i] and normal_forms[j] are the same modulo equality unsigned index = 0; - bool success; - do{ - //simple check - if( processSimpleNEq( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, index, false ) ){ - //added a lemma, return - return true; - } - - success = false; - //if we are at the end - if(index==normal_forms[i].size() || index==normal_forms[j].size() ) { - Assert( index==normal_forms[i].size() && index==normal_forms[j].size() ); - //we're done - //addNormalFormPair( normal_form_src[i], normal_form_src[j] ); - } else { - std::vector< Node > lexp; - Node length_term_i = getLength( normal_forms[i][index], lexp ); - Node length_term_j = getLength( normal_forms[j][index], lexp ); - //check length(normal_forms[i][index]) == length(normal_forms[j][index]) - if( !areDisequal(length_term_i, length_term_j) && !areEqual(length_term_i, length_term_j) && - normal_forms[i][index].getKind()!=kind::CONST_STRING && normal_forms[j][index].getKind()!=kind::CONST_STRING ) { - //length terms are equal, merge equivalence classes if not already done so - Node length_eq = NodeManager::currentNM()->mkNode( kind::EQUAL, length_term_i, length_term_j ); - Trace("strings-solve-debug") << "Non-simple Case 1 : string lengths neither equal nor disequal" << std::endl; - //try to make the lengths equal via splitting on demand - sendSplit( length_term_i, length_term_j, "Len-Split(Diseq)" ); - length_eq = Rewriter::rewrite( length_eq ); - d_pending_req_phase[ length_eq ] = true; - return true; - } else { - Trace("strings-solve-debug") << "Non-simple Case 2 : must compare strings" << std::endl; - int loop_in_i = -1; - int loop_in_j = -1; - if( detectLoop(normal_forms, i, j, index, loop_in_i, loop_in_j) ){ - if( !flag_lb ){ - c_i = i; - c_j = j; - c_loop_n_index = loop_in_i!=-1 ? i : j; - c_other_n_index = loop_in_i!=-1 ? j : i; - c_loop_index = loop_in_i!=-1 ? loop_in_i : loop_in_j; - c_index = index; - - getExplanationVectorForPrefix( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, -1, false, c_lb_exp ); - - if(options::stringLB() == 0) { - flag_lb = true; - } else { - if(processLoop(c_lb_exp, normal_forms, normal_form_src, c_i, c_j, c_loop_n_index, c_other_n_index, c_loop_index, c_index)) { - return true; - } - } - } - } else { - Node conc; - std::vector< Node > antec; - Trace("strings-solve-debug") << "No loops detected." << std::endl; - if( normal_forms[i][index].getKind() == kind::CONST_STRING || normal_forms[j][index].getKind() == kind::CONST_STRING) { - unsigned const_k = normal_forms[i][index].getKind() == kind::CONST_STRING ? i : j; - unsigned nconst_k = normal_forms[i][index].getKind() == kind::CONST_STRING ? j : i; - Node const_str = normal_forms[const_k][index]; - Node other_str = normal_forms[nconst_k][index]; - Assert( other_str.getKind()!=kind::CONST_STRING, "Other string is not constant." ); - Assert( other_str.getKind()!=kind::STRING_CONCAT, "Other string is not CONCAT." ); - if( !d_equalityEngine.areDisequal(other_str, d_emptyString, true) ) { - sendSplit( other_str, d_emptyString, "Len-Split(CST)" ); - } else { - Assert(areDisequal(other_str, d_emptyString), "CST Split on empty Var"); - getExplanationVectorForPrefix( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, index, false, antec ); - Node xnz = other_str.eqNode(d_emptyString).negate(); - antec.push_back( xnz ); - Node conc; - if( normal_forms[nconst_k].size() > index + 1 && normal_forms[nconst_k][index + 1].isConst() ) { - CVC4::String stra = const_str.getConst<String>(); - CVC4::String strb = normal_forms[nconst_k][index + 1].getConst<String>(); - CVC4::String stra1 = stra.substr(1); - size_t p = stra.size() - stra1.overlap(strb); - size_t p2 = stra1.find(strb); - p = p2==std::string::npos? p : ( p>p2+1? p2+1 : p ); - Node prea = p==stra.size()? const_str : NodeManager::currentNM()->mkConst(stra.substr(0, p)); - Node sk = mkSkolemCached( other_str, prea, sk_id_c_spt, "c_spt" ); - conc = other_str.eqNode( mkConcat(prea, sk) ); - Trace("strings-csp") << "Const Split: " << prea << " is removed from " << stra << " due to " << strb << std::endl; - } else { - // normal v/c split - Node firstChar = const_str.getConst<String>().size() == 1 ? const_str : - NodeManager::currentNM()->mkConst( const_str.getConst<String>().substr(0, 1) ); - Node sk = mkSkolemCached( other_str, firstChar, sk_id_vc_spt, "c_spt" ); - conc = other_str.eqNode( mkConcat(firstChar, sk) ); - Trace("strings-csp") << "Const Split: " << firstChar << " is removed from " << const_str << " (normal) " << std::endl; - } - - conc = Rewriter::rewrite( conc ); - sendInference( antec, conc, "S-Split(CST-P)", true ); - } - return true; - } else { - std::vector< Node > antec_new_lits; - getExplanationVectorForPrefix( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, index, false, antec ); - - Node ldeq = NodeManager::currentNM()->mkNode( kind::EQUAL, length_term_i, length_term_j ).negate(); - if( d_equalityEngine.areDisequal( length_term_i, length_term_j, true ) ){ - antec.push_back( ldeq ); - }else{ - antec_new_lits.push_back(ldeq); - } - - //x!=e /\ y!=e - for(unsigned xory=0; xory<2; xory++) { - Node x = xory==0 ? normal_forms[i][index] : normal_forms[j][index]; - Node xgtz = x.eqNode( d_emptyString ).negate(); - if( d_equalityEngine.areDisequal( x, d_emptyString, true ) ) { - antec.push_back( xgtz ); - } else { - antec_new_lits.push_back( xgtz ); - } - } - Node sk = mkSkolemCached( normal_forms[i][index], normal_forms[j][index], sk_id_v_spt, "v_spt", 1 ); - Node eq1 = normal_forms[i][index].eqNode( mkConcat(normal_forms[j][index], sk) ); - Node eq2 = normal_forms[j][index].eqNode( mkConcat(normal_forms[i][index], sk) ); - if( options::stringCheckEntailLen() ){ - //check entailment - for( unsigned e=0; e<2; e++ ){ - Node lt1 = e==0 ? length_term_i : length_term_j; - Node lt2 = e==0 ? length_term_j : length_term_i; - Node ent_lit = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::GT, lt1, lt2 ) ); - std::pair<bool, Node> et = d_valuation.entailmentCheck(THEORY_OF_TYPE_BASED, ent_lit ); - if( et.first ){ - Trace("strings-entail") << "Strings entailment : " << ent_lit << " is entailed in the current context." << std::endl; - Trace("strings-entail") << " explanation was : " << et.second << std::endl; - conc = e==0 ? eq1 : eq2; - antec_new_lits.push_back( et.second ); - break; - } - } - } - if( conc.isNull() ){ - conc = Rewriter::rewrite(NodeManager::currentNM()->mkNode( kind::OR, eq1, eq2 )); - } - - - sendInference( antec, antec_new_lits, conc, "S-Split(VAR)", true ); - //++(d_statistics.d_eq_splits); - return true; - } - } - } - } - } while(success); + processSimpleNEq( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, index, false, rindex, pinfer ); + if( hasProcessed() ){ + return; + }else if( !pinfer.empty() && pinfer.back().d_id==1 ){ + break; + } } } - if(!flag_lb) { - return false; - } } - if(flag_lb) { - if(processLoop(c_lb_exp, normal_forms, normal_form_src, c_i, c_j, c_loop_n_index, c_other_n_index, c_loop_index, c_index)) { - return true; + if( !pinfer.empty() ){ + //now, determine which of the possible inferences we want to add + int use_index = -1; + Trace("strings-solve") << "Possible inferences (" << pinfer.size() << ") : " << std::endl; + unsigned min_id = 9; + unsigned max_index = 0; + for( unsigned i=0; i<pinfer.size(); i++ ){ + Trace("strings-solve") << "From " << pinfer[i].d_i << " / " << pinfer[i].d_j << " (rev=" << pinfer[i].d_rev << ") : "; + Trace("strings-solve") << pinfer[i].d_conc << " by " << pinfer[i].getId() << std::endl; + if( use_index==-1 || pinfer[i].d_id<min_id || ( pinfer[i].d_id==min_id && pinfer[i].d_index>max_index ) ){ + min_id = pinfer[i].d_id; + max_index = pinfer[i].d_index; + use_index = i; + } + } + //send the inference + sendInference( pinfer[use_index].d_ant, pinfer[use_index].d_antn, pinfer[use_index].d_conc, pinfer[use_index].getId(), pinfer[use_index].sendAsLemma() ); + for( std::map< int, std::vector< Node > >::iterator it = pinfer[use_index].d_new_skolem.begin(); it != pinfer[use_index].d_new_skolem.end(); ++it ){ + for( unsigned i=0; i<it->second.size(); i++ ){ + if( it->first==0 ){ + sendLengthLemma( it->second[i] ); + }else if( it->first==1 ){ + registerNonEmptySkolem( it->second[i] ); + } + } } } +} - return false; +bool TheoryStrings::InferInfo::sendAsLemma() { + return true; } -bool TheoryStrings::processReverseNEq( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, +void TheoryStrings::processReverseNEq( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend, - unsigned i, unsigned j ) { + unsigned i, unsigned j, unsigned& index, unsigned rproc, std::vector< InferInfo >& pinfer ) { //reverse normal form of i, j std::reverse( normal_forms[i].begin(), normal_forms[i].end() ); std::reverse( normal_forms[j].begin(), normal_forms[j].end() ); - unsigned index = 0; - bool ret = processSimpleNEq( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, index, true ); + processSimpleNEq( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, index, true, rproc, pinfer ); //reverse normal form of i, j std::reverse( normal_forms[i].begin(), normal_forms[i].end() ); std::reverse( normal_forms[j].begin(), normal_forms[j].end() ); - - return ret; } -bool TheoryStrings::processSimpleNEq( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, +//rproc is the # is the size of suffix that is identical +void TheoryStrings::processSimpleNEq( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend, - unsigned i, unsigned j, unsigned& index, bool isRev ) { + unsigned i, unsigned j, unsigned& index, bool isRev, unsigned rproc, std::vector< InferInfo >& pinfer ) { + Assert( rproc<=normal_forms[i].size() && rproc<=normal_forms[j].size() ); bool success; do { success = false; //if we are at the end - if(index==normal_forms[i].size() || index==normal_forms[j].size() ) { - if( index==normal_forms[i].size() && index==normal_forms[j].size() ) { + if( index==(normal_forms[i].size()-rproc) || index==(normal_forms[j].size()-rproc) ){ + if( index==(normal_forms[i].size()-rproc) && index==(normal_forms[j].size()-rproc) ){ //we're done - } else { + }else{ //the remainder must be empty - unsigned k = index==normal_forms[i].size() ? j : i; + unsigned k = index==(normal_forms[i].size()-rproc) ? j : i; unsigned index_k = index; //Node eq_exp = mkAnd( curr_exp ); std::vector< Node > curr_exp; - getExplanationVectorForPrefix( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, -1, isRev, curr_exp ); - while(!d_conflict && index_k<normal_forms[k].size()) { + getExplanationVectorForPrefixEq( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, -1, -1, isRev, curr_exp ); + while( !d_conflict && index_k<(normal_forms[k].size()-rproc) ){ //can infer that this string must be empty Node eq = normal_forms[k][index_k].eqNode( d_emptyString ); //Trace("strings-lemma") << "Strings: Infer " << eq << " from " << eq_exp << std::endl; Assert( !areEqual( d_emptyString, normal_forms[k][index_k] ) ); - sendInference( curr_exp, eq, "EQ_Endpoint" ); + sendInference( curr_exp, eq, "N_EndpointEmp" ); index_k++; } - return true; } }else{ Trace("strings-solve-debug") << "Process " << normal_forms[i][index] << " ... " << normal_forms[j][index] << std::endl; @@ -2279,23 +2405,22 @@ bool TheoryStrings::processSimpleNEq( std::vector< std::vector< Node > > &normal //eq = Rewriter::rewrite( eq ); Node length_eq = length_term_i.eqNode( length_term_j ); //temp_exp.insert(temp_exp.end(), curr_exp.begin(), curr_exp.end() ); - getExplanationVectorForPrefix( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, index, isRev, temp_exp ); + getExplanationVectorForPrefixEq( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, index, index, isRev, temp_exp ); temp_exp.push_back(length_eq); - sendInference( temp_exp, eq, "LengthEq" ); - return true; - }else if( ( normal_forms[i][index].getKind()!=kind::CONST_STRING && index==normal_forms[i].size()-1 ) || - ( normal_forms[j][index].getKind()!=kind::CONST_STRING && index==normal_forms[j].size()-1 ) ){ + sendInference( temp_exp, eq, "N_Unify" ); + return; + }else if( ( normal_forms[i][index].getKind()!=kind::CONST_STRING && index==normal_forms[i].size()-rproc-1 ) || + ( normal_forms[j][index].getKind()!=kind::CONST_STRING && index==normal_forms[j].size()-rproc-1 ) ){ Trace("strings-solve-debug") << "Simple Case 3 : at endpoint" << std::endl; - Node conc; std::vector< Node > antec; //antec.insert(antec.end(), curr_exp.begin(), curr_exp.end() ); - getExplanationVectorForPrefix( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, -1, isRev, antec ); + getExplanationVectorForPrefixEq( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, -1, -1, isRev, antec ); std::vector< Node > eqn; for( unsigned r=0; r<2; r++ ) { int index_k = index; int k = r==0 ? i : j; std::vector< Node > eqnc; - for( unsigned index_l=index_k; index_l<normal_forms[k].size(); index_l++ ) { + for( unsigned index_l=index_k; index_l<(normal_forms[k].size()-rproc); index_l++ ) { if(isRev) { eqnc.insert(eqnc.begin(), normal_forms[k][index_l] ); } else { @@ -2304,18 +2429,17 @@ bool TheoryStrings::processSimpleNEq( std::vector< std::vector< Node > > &normal } eqn.push_back( mkConcat( eqnc ) ); } - if( !areEqual( eqn[0], eqn[1] ) ) { - conc = eqn[0].eqNode( eqn[1] ); - sendInference( antec, conc, "ENDPOINT", true ); - return true; + if( !areEqual( eqn[0], eqn[1] ) ){ + sendInference( antec, eqn[0].eqNode( eqn[1] ), "N_EndpointEq", true ); + return; }else{ Assert( normal_forms[i].size()==normal_forms[j].size() ); - index = normal_forms[i].size(); + index = normal_forms[i].size()-rproc; } - } else if( normal_forms[i][index].isConst() && normal_forms[j][index].isConst() ){ + }else if( normal_forms[i][index].isConst() && normal_forms[j][index].isConst() ){ Node const_str = normal_forms[i][index]; Node other_str = normal_forms[j][index]; - Trace("strings-solve-debug") << "Simple Case 3 : Const Split : " << const_str << " vs " << other_str << std::endl; + Trace("strings-solve-debug") << "Simple Case 3 : Const Split : " << const_str << " vs " << other_str << " at index " << index << ", isRev = " << isRev << std::endl; unsigned len_short = const_str.getConst<String>().size() <= other_str.getConst<String>().size() ? const_str.getConst<String>().size() : other_str.getConst<String>().size(); bool isSameFix = isRev ? const_str.getConst<String>().rstrncmp(other_str.getConst<String>(), len_short): const_str.getConst<String>().strncmp(other_str.getConst<String>(), len_short); if( isSameFix ) { @@ -2323,12 +2447,24 @@ bool TheoryStrings::processSimpleNEq( std::vector< std::vector< Node > > &normal //k is the index of the string that is shorter int k = const_str.getConst<String>().size()<other_str.getConst<String>().size() ? i : j; int l = const_str.getConst<String>().size()<other_str.getConst<String>().size() ? j : i; - if(isRev) { + //update the nf exp dependencies + //notice this is not critical for soundness: not doing the below incrementing will only lead to overapproximating when antecedants are required in explanations + for( std::map< Node, std::map< bool, int > >::iterator itnd = normal_forms_exp_depend[l].begin(); itnd != normal_forms_exp_depend[l].end(); ++itnd ){ + for( std::map< bool, int >::iterator itnd2 = itnd->second.begin(); itnd2 != itnd->second.end(); ++itnd2 ){ + //see if this can be incremented: it can if it is not relevant to the current index + Assert( itnd2->second>=0 && itnd2->second<=(int)normal_forms[l].size() ); + bool increment = (itnd2->first==isRev) ? itnd2->second>(int)index : ( (int)normal_forms[l].size()-1-itnd2->second )<(int)index; + if( increment ){ + normal_forms_exp_depend[l][itnd->first][itnd2->first] = itnd2->second + 1; + } + } + } + if( isRev ){ int new_len = normal_forms[l][index].getConst<String>().size() - len_short; Node remainderStr = NodeManager::currentNM()->mkConst( normal_forms[l][index].getConst<String>().substr(0, new_len) ); Trace("strings-solve-debug-test") << "Break normal form of " << normal_forms[l][index] << " into " << normal_forms[k][index] << ", " << remainderStr << std::endl; normal_forms[l].insert( normal_forms[l].begin()+index + 1, remainderStr ); - } else { + }else{ Node remainderStr = NodeManager::currentNM()->mkConst(normal_forms[l][index].getConst<String>().substr(len_short)); Trace("strings-solve-debug-test") << "Break normal form of " << normal_forms[l][index] << " into " << normal_forms[k][index] << ", " << remainderStr << std::endl; normal_forms[l].insert( normal_forms[l].begin()+index + 1, remainderStr ); @@ -2336,29 +2472,231 @@ bool TheoryStrings::processSimpleNEq( std::vector< std::vector< Node > > &normal normal_forms[l][index] = normal_forms[k][index]; index++; success = true; - } else { + }else{ + //conflict std::vector< Node > antec; - //curr_exp is conflict - //antec.insert(antec.end(), curr_exp.begin(), curr_exp.end() ); - getExplanationVectorForPrefix( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, index, isRev, antec ); - sendInference( antec, d_false, "Const Conflict", true ); - return true; + getExplanationVectorForPrefixEq( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, index, index, isRev, antec ); + sendInference( antec, d_false, "N_Const", true ); + return; + } + }else{ + //construct the candidate inference "info" + InferInfo info; + info.d_index = index; + //for debugging + info.d_i = i; + info.d_j = j; + info.d_rev = isRev; + bool info_valid = false; + Assert( index<normal_forms[i].size()-rproc && index<normal_forms[j].size()-rproc ); + std::vector< Node > lexp; + Node length_term_i = getLength( normal_forms[i][index], lexp ); + Node length_term_j = getLength( normal_forms[j][index], lexp ); + //split on equality between string lengths (note that splitting on equality between strings is worse since it is harder to process) + if( !areDisequal( length_term_i, length_term_j ) && !areEqual( length_term_i, length_term_j ) && + normal_forms[i][index].getKind()!=kind::CONST_STRING && normal_forms[j][index].getKind()!=kind::CONST_STRING ){ //AJR: remove the latter 2 conditions? + Trace("strings-solve-debug") << "Non-simple Case 1 : string lengths neither equal nor disequal" << std::endl; + //try to make the lengths equal via splitting on demand + Node length_eq = NodeManager::currentNM()->mkNode( kind::EQUAL, length_term_i, length_term_j ); + length_eq = Rewriter::rewrite( length_eq ); + //set info + info.d_conc = NodeManager::currentNM()->mkNode( kind::OR, length_eq, length_eq.negate() ); + info.d_pending_phase[ length_eq ] = true; + info.d_id = 3; + info_valid = true; + }else{ + Trace("strings-solve-debug") << "Non-simple Case 2 : must compare strings" << std::endl; + int loop_in_i = -1; + int loop_in_j = -1; + if( detectLoop( normal_forms, i, j, index, loop_in_i, loop_in_j, rproc ) ){ + if( !isRev ){ //FIXME + getExplanationVectorForPrefixEq( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, -1, -1, isRev, info.d_ant ); + //set info + if( processLoop( normal_forms, normal_form_src, i, j, loop_in_i!=-1 ? i : j, loop_in_i!=-1 ? j : i, loop_in_i!=-1 ? loop_in_i : loop_in_j, index, info ) ){ + info_valid = true; + } + } + }else{ + //AJR: length entailment here? + if( normal_forms[i][index].getKind() == kind::CONST_STRING || normal_forms[j][index].getKind() == kind::CONST_STRING ){ + unsigned const_k = normal_forms[i][index].getKind() == kind::CONST_STRING ? i : j; + unsigned nconst_k = normal_forms[i][index].getKind() == kind::CONST_STRING ? j : i; + Node other_str = normal_forms[nconst_k][index]; + Assert( other_str.getKind()!=kind::CONST_STRING, "Other string is not constant." ); + Assert( other_str.getKind()!=kind::STRING_CONCAT, "Other string is not CONCAT." ); + if( !d_equalityEngine.areDisequal( other_str, d_emptyString, true ) ){ + Node eq = other_str.eqNode( d_emptyString ); + //set info + info.d_conc = NodeManager::currentNM()->mkNode( kind::OR, eq, eq.negate() ); + info.d_id = 4; + info_valid = true; + }else{ + if( !isRev ){ //FIXME + Node xnz = other_str.eqNode( d_emptyString ).negate(); + unsigned index_nc_k = index+1; + //Node next_const_str = TheoryStringsRewriter::collectConstantStringAt( normal_forms[nconst_k], index_nc_k, false ); + unsigned start_index_nc_k = index+1; + Node next_const_str = TheoryStringsRewriter::getNextConstantAt( normal_forms[nconst_k], start_index_nc_k, index_nc_k, false ); + if( !next_const_str.isNull() ) { + unsigned index_c_k = index; + Node const_str = TheoryStringsRewriter::collectConstantStringAt( normal_forms[const_k], index_c_k, false ); + Assert( !const_str.isNull() ); + CVC4::String stra = const_str.getConst<String>(); + CVC4::String strb = next_const_str.getConst<String>(); + //since non-empty, we start with charecter #1 + size_t p; + if( isRev ){ + CVC4::String stra1 = stra.prefix( stra.size()-1 ); + p = stra.size() - stra1.roverlap(strb); + Trace("strings-csp-debug") << "Compute roverlap : " << const_str << " " << next_const_str << std::endl; + size_t p2 = stra1.rfind(strb); + p = p2==std::string::npos ? p : ( p>p2+1? p2+1 : p ); + Trace("strings-csp-debug") << "overlap : " << stra1 << " " << strb << " returned " << p << " " << p2 << " " << (p2==std::string::npos) << std::endl; + }else{ + CVC4::String stra1 = stra.substr( 1 ); + p = stra.size() - stra1.overlap(strb); + Trace("strings-csp-debug") << "Compute overlap : " << const_str << " " << next_const_str << std::endl; + size_t p2 = stra1.find(strb); + p = p2==std::string::npos ? p : ( p>p2+1? p2+1 : p ); + Trace("strings-csp-debug") << "overlap : " << stra1 << " " << strb << " returned " << p << " " << p2 << " " << (p2==std::string::npos) << std::endl; + } + if( p>1 ){ + if( start_index_nc_k==index+1 ){ + info.d_ant.push_back( xnz ); + getExplanationVectorForPrefixEq( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, + const_k, nconst_k, index_c_k, index_nc_k, isRev, info.d_ant ); + Node prea = p==stra.size() ? const_str : NodeManager::currentNM()->mkConst( isRev ? stra.suffix( p ) : stra.prefix( p ) ); + Node sk = mkSkolemCached( other_str, prea, isRev ? sk_id_c_spt_rev : sk_id_c_spt, "c_spt", -1 ); + Trace("strings-csp") << "Const Split: " << prea << " is removed from " << stra << " due to " << strb << ", p=" << p << std::endl; + //set info + info.d_conc = other_str.eqNode( isRev ? mkConcat( sk, prea ) : mkConcat(prea, sk) ); + info.d_new_skolem[0].push_back( sk ); + info.d_id = 1; + info_valid = true; + } + /* FIXME for isRev, speculative + else if( options::stringLenPropCsp() ){ + //propagate length constraint + std::vector< Node > cc; + for( unsigned i=index; i<start_index_nc_k; i++ ){ + cc.push_back( normal_forms[nconst_k][i] ); + } + Node lt = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, mkConcat( cc ) ); + conc = NodeManager::currentNM()->mkNode( kind::GEQ, lt, NodeManager::currentNM()->mkConst( Rational(p) ) ); + sendInference( ant, conc, "S-Split(CSP-P)-lprop", true ); + } + */ + } + } + if( !info_valid ){ + info.d_ant.push_back( xnz ); + Node const_str = normal_forms[const_k][index]; + getExplanationVectorForPrefixEq( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, index, index, isRev, info.d_ant ); + CVC4::String stra = const_str.getConst<String>(); + if( options::stringBinaryCsp() && stra.size()>3 ){ + //split string in half + Node c_firstHalf = NodeManager::currentNM()->mkConst( isRev ? stra.substr( stra.size()/2 ) : stra.substr(0, stra.size()/2 ) ); + Node sk = mkSkolemCached( other_str, c_firstHalf , isRev ? sk_id_vc_bin_spt_rev : sk_id_vc_bin_spt, "cb_spt", -1 ); + Trace("strings-csp") << "Const Split: " << c_firstHalf << " is removed from " << const_str << " (binary) " << std::endl; + info.d_conc = NodeManager::currentNM()->mkNode( kind::OR, other_str.eqNode( isRev ? mkConcat( sk, c_firstHalf ) : mkConcat( c_firstHalf, sk ) ), + NodeManager::currentNM()->mkNode( kind::AND, + sk.eqNode( d_emptyString ).negate(), + c_firstHalf.eqNode( isRev ? mkConcat( sk, other_str ) : mkConcat( other_str, sk ) ) ) ); + info.d_new_skolem[0].push_back( sk ); + info.d_id = 5; + info_valid = true; + }else{ + // normal v/c split + Node firstChar = stra.size() == 1 ? const_str : NodeManager::currentNM()->mkConst( isRev ? stra.suffix( 1 ) : stra.prefix( 1 ) ); + Node sk = mkSkolemCached( other_str, firstChar, isRev ? sk_id_vc_spt_rev : sk_id_vc_spt, "c_spt", -1 ); + Trace("strings-csp") << "Const Split: " << firstChar << " is removed from " << const_str << " (serial) " << std::endl; + info.d_conc = other_str.eqNode( isRev ? mkConcat( sk, firstChar ) : mkConcat(firstChar, sk) ); + info.d_new_skolem[0].push_back( sk ); + info.d_id = 6; + info_valid = true; + } + } + } + } + }else{ + int lentTestSuccess = -1; + Node lentTestExp; + if( options::stringCheckEntailLen() ){ + //check entailment + for( unsigned e=0; e<2; e++ ){ + Node t = e==0 ? normal_forms[i][index] : normal_forms[j][index]; + //do not infer constants are larger than variables + if( t.getKind()!=kind::CONST_STRING ){ + Node lt1 = e==0 ? length_term_i : length_term_j; + Node lt2 = e==0 ? length_term_j : length_term_i; + Node ent_lit = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::GT, lt1, lt2 ) ); + std::pair<bool, Node> et = d_valuation.entailmentCheck( THEORY_OF_TYPE_BASED, ent_lit ); + if( et.first ){ + Trace("strings-entail") << "Strings entailment : " << ent_lit << " is entailed in the current context." << std::endl; + Trace("strings-entail") << " explanation was : " << et.second << std::endl; + lentTestSuccess = e; + lentTestExp = et.second; + break; + } + } + } + } + + getExplanationVectorForPrefixEq( normal_forms, normal_form_src, normal_forms_exp, normal_forms_exp_depend, i, j, index, index, isRev, info.d_ant ); + //x!=e /\ y!=e + for(unsigned xory=0; xory<2; xory++) { + Node x = xory==0 ? normal_forms[i][index] : normal_forms[j][index]; + Node xgtz = x.eqNode( d_emptyString ).negate(); + if( d_equalityEngine.areDisequal( x, d_emptyString, true ) ) { + info.d_ant.push_back( xgtz ); + } else { + info.d_antn.push_back( xgtz ); + } + } + Node sk = mkSkolemCached( normal_forms[i][index], normal_forms[j][index], isRev ? sk_id_v_spt_rev : sk_id_v_spt, "v_spt", -1 ); + //must add length requirement + info.d_new_skolem[1].push_back( sk ); + Node eq1 = normal_forms[i][index].eqNode( isRev ? mkConcat(sk, normal_forms[j][index]) : mkConcat(normal_forms[j][index], sk) ); + Node eq2 = normal_forms[j][index].eqNode( isRev ? mkConcat(sk, normal_forms[i][index]) : mkConcat(normal_forms[i][index], sk) ); + + if( lentTestSuccess!=-1 ){ + info.d_antn.push_back( lentTestExp ); + info.d_conc = lentTestSuccess==0 ? eq1 : eq2; + info.d_id = 2; + info_valid = true; + }else{ + Node ldeq = NodeManager::currentNM()->mkNode( kind::EQUAL, length_term_i, length_term_j ).negate(); + if( d_equalityEngine.areDisequal( length_term_i, length_term_j, true ) ){ + info.d_ant.push_back( ldeq ); + }else{ + info.d_antn.push_back(ldeq); + } + //set info + info.d_conc = NodeManager::currentNM()->mkNode( kind::OR, eq1, eq2 ); + info.d_id = 7; + info_valid = true; + } + } + } + } + if( info_valid ){ + pinfer.push_back( info ); + Assert( !success ); } } } } }while( success ); - return false; } -bool TheoryStrings::detectLoop( std::vector< std::vector< Node > > &normal_forms, int i, int j, int index, int &loop_in_i, int &loop_in_j) { +bool TheoryStrings::detectLoop( std::vector< std::vector< Node > > &normal_forms, int i, int j, int index, int &loop_in_i, int &loop_in_j, unsigned rproc ){ int has_loop[2] = { -1, -1 }; if( options::stringLB() != 2 ) { for( unsigned r=0; r<2; r++ ) { int n_index = (r==0 ? i : j); int other_n_index = (r==0 ? j : i); if( normal_forms[other_n_index][index].getKind() != kind::CONST_STRING ) { - for( unsigned lp = index+1; lp<normal_forms[n_index].size(); lp++ ){ + for( unsigned lp = index+1; lp<normal_forms[n_index].size()-rproc; lp++ ){ if( normal_forms[n_index][lp]==normal_forms[other_n_index][index] ){ has_loop[r] = lp; break; @@ -2372,13 +2710,14 @@ bool TheoryStrings::detectLoop( std::vector< std::vector< Node > > &normal_forms loop_in_j = has_loop[1]; return true; } else { + Trace("strings-solve-debug") << "No loops detected." << std::endl; return false; } } //xs(zy)=t(yz)xr -bool TheoryStrings::processLoop( std::vector< Node > &antec, std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, - int i, int j, int loop_n_index, int other_n_index, int loop_index, int index) { +bool TheoryStrings::processLoop( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, + int i, int j, int loop_n_index, int other_n_index, int loop_index, int index, InferInfo& info ){ if( options::stringAbortLoop() ){ Message() << "Looping word equation encountered." << std::endl; exit( 1 ); @@ -2417,11 +2756,11 @@ bool TheoryStrings::processLoop( std::vector< Node > &antec, std::vector< std::v //Trace("strings-loop") << "Lemma Cache: " << normal_form_src[i] << " vs " << normal_form_src[j] << std::endl; //TODO: can be more general - if( s_zy.isConst() && r.isConst() && r != d_emptyString) { + if( s_zy.isConst() && r.isConst() && r!=d_emptyString) { int c; bool flag = true; if(s_zy.getConst<String>().tailcmp( r.getConst<String>(), c ) ) { - if(c >= 0) { + if( c>=0) { s_zy = NodeManager::currentNM()->mkConst( s_zy.getConst<String>().substr(0, c) ); r = d_emptyString; vec_r.clear(); @@ -2429,29 +2768,37 @@ bool TheoryStrings::processLoop( std::vector< Node > &antec, std::vector< std::v flag = false; } } - if(flag) { + if( flag ){ Trace("strings-loop") << "Strings::Loop: tails are different." << std::endl; - sendInference( antec, conc, "Loop Conflict", true ); - return true; + sendInference( info.d_ant, conc, "Loop Conflict", true ); + return false; } } //require that x is non-empty + Node split_eq; if( !areDisequal( normal_forms[loop_n_index][loop_index], d_emptyString ) ){ //try to make normal_forms[loop_n_index][loop_index] equal to empty to avoid loop - sendSplit( normal_forms[loop_n_index][loop_index], d_emptyString, "Len-Split(Loop-X)" ); - } else if( !areDisequal( t_yz, d_emptyString ) && t_yz.getKind()!=kind::CONST_STRING ) { + split_eq = normal_forms[loop_n_index][loop_index].eqNode( d_emptyString ); + }else if( !areDisequal( t_yz, d_emptyString ) && t_yz.getKind()!=kind::CONST_STRING ) { //try to make normal_forms[loop_n_index][loop_index] equal to empty to avoid loop - sendSplit( t_yz, d_emptyString, "Len-Split(Loop-YZ)" ); - } else { + split_eq = t_yz.eqNode( d_emptyString ); + } + if( !split_eq.isNull() ){ + info.d_conc = NodeManager::currentNM()->mkNode( kind::OR, split_eq, split_eq.negate() ); + info.d_id = 4; + return true; + }else{ //need to break - antec.push_back( normal_forms[loop_n_index][loop_index].eqNode( d_emptyString ).negate() ); + info.d_ant.push_back( normal_forms[loop_n_index][loop_index].eqNode( d_emptyString ).negate() ); if( t_yz.getKind()!=kind::CONST_STRING ) { - antec.push_back( t_yz.eqNode( d_emptyString ).negate() ); + info.d_ant.push_back( t_yz.eqNode( d_emptyString ).negate() ); } - Node ant = mkExplain( antec ); - if(d_loop_antec.find(ant) == d_loop_antec.end()) { - d_loop_antec.insert(ant); + Node ant = mkExplain( info.d_ant ); + if( d_loop_antec.find( ant ) == d_loop_antec.end() ){ + d_loop_antec.insert( ant ); + info.d_ant.clear(); + info.d_antn.push_back( ant ); Node str_in_re; if( s_zy == t_yz && @@ -2531,26 +2878,23 @@ bool TheoryStrings::processLoop( std::vector< Node > &antec, std::vector< std::v //we will be done addNormalFormPair( normal_form_src[i], normal_form_src[j] ); if( options::stringProcessLoop() ){ - sendLemma( ant, conc, "F-LOOP" ); - ++(d_statistics.d_loop_lemmas); + info.d_conc = conc; + info.d_id = 8; + return true; }else{ d_out->setIncomplete(); - return false; } - - } else { + }else{ Trace("strings-loop") << "Strings::Loop: loop lemma for " << ant << " has already added." << std::endl; addNormalFormPair( normal_form_src[i], normal_form_src[j] ); - return false; } } - return true; } - return true; + return false; } //return true for lemma, false if we succeed -bool TheoryStrings::processDeq( Node ni, Node nj ) { +void TheoryStrings::processDeq( Node ni, Node nj ) { //Assert( areDisequal( ni, nj ) ); if( d_normal_forms[ni].size()>1 || d_normal_forms[nj].size()>1 ){ std::vector< Node > nfi; @@ -2560,7 +2904,7 @@ bool TheoryStrings::processDeq( Node ni, Node nj ) { int revRet = processReverseDeq( nfi, nfj, ni, nj ); if( revRet!=0 ){ - return revRet==-1; + return; } nfi.clear(); @@ -2572,47 +2916,88 @@ bool TheoryStrings::processDeq( Node ni, Node nj ) { while( index<nfi.size() || index<nfj.size() ){ int ret = processSimpleDeq( nfi, nfj, ni, nj, index, false ); if( ret!=0 ) { - return ret==-1; - } else { + return; + }else{ Assert( index<nfi.size() && index<nfj.size() ); Node i = nfi[index]; Node j = nfj[index]; Trace("strings-solve-debug") << "...Processing(DEQ) " << i << " " << j << std::endl; - if( !areEqual( i, j ) ) { + if( !areEqual( i, j ) ){ Assert( i.getKind()!=kind::CONST_STRING || j.getKind()!=kind::CONST_STRING ); std::vector< Node > lexp; Node li = getLength( i, lexp ); Node lj = getLength( j, lexp ); - if( areDisequal(li, lj) ){ - //if( i.getKind()==kind::CONST_STRING || j.getKind()==kind::CONST_STRING ){ - - Trace("strings-solve") << "Non-Simple Case 1 : add lemma " << std::endl; - //must add lemma - std::vector< Node > antec; - std::vector< Node > antec_new_lits; - antec.insert( antec.end(), d_normal_forms_exp[ni].begin(), d_normal_forms_exp[ni].end() ); - antec.insert( antec.end(), d_normal_forms_exp[nj].begin(), d_normal_forms_exp[nj].end() ); - //check disequal - if( areDisequal( ni, nj ) ){ - antec.push_back( ni.eqNode( nj ).negate() ); + if( areDisequal( li, lj ) ){ + if( i.getKind()==kind::CONST_STRING || j.getKind()==kind::CONST_STRING ){ + //check if empty + Node const_k = i.getKind() == kind::CONST_STRING ? i : j; + Node nconst_k = i.getKind() == kind::CONST_STRING ? j : i; + Node lnck = i.getKind() == kind::CONST_STRING ? lj : li; + if( !d_equalityEngine.areDisequal( nconst_k, d_emptyString, true ) ){ + Node eq = nconst_k.eqNode( d_emptyString ); + Node conc = NodeManager::currentNM()->mkNode( kind::OR, eq, eq.negate() ); + sendInference( d_empty_vec, conc, "D-DISL-Emp-Split" ); + return; + }else{ + //split on first character + CVC4::String str = const_k.getConst<String>(); + Node firstChar = str.size() == 1 ? const_k : NodeManager::currentNM()->mkConst( str.prefix( 1 ) ); + if( areEqual( lnck, d_one ) ){ + if( areDisequal( firstChar, nconst_k ) ){ + return; + }else if( !areEqual( firstChar, nconst_k ) ){ + //splitting on demand : try to make them disequal + Node eq = firstChar.eqNode( nconst_k ); + sendSplit( firstChar, nconst_k, "S-Split(DEQL-Const)" ); + eq = Rewriter::rewrite( eq ); + d_pending_req_phase[ eq ] = false; + return; + } + }else{ + Node sk = mkSkolemCached( nconst_k, firstChar, sk_id_dc_spt, "dc_spt", 2 ); + Node skr = mkSkolemCached( nconst_k, firstChar, sk_id_dc_spt_rem, "dc_spt_rem", -1 ); + Node eq1 = nconst_k.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk, skr ) ); + eq1 = Rewriter::rewrite( eq1 ); + Node eq2 = nconst_k.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, firstChar, skr ) ); + std::vector< Node > antec; + antec.insert( antec.end(), d_normal_forms_exp[ni].begin(), d_normal_forms_exp[ni].end() ); + antec.insert( antec.end(), d_normal_forms_exp[nj].begin(), d_normal_forms_exp[nj].end() ); + antec.push_back( nconst_k.eqNode( d_emptyString ).negate() ); + sendInference( antec, NodeManager::currentNM()->mkNode( kind::OR, + NodeManager::currentNM()->mkNode( kind::AND, eq1, sk.eqNode( firstChar ).negate() ), eq2 ), "D-DISL-CSplit" ); + d_pending_req_phase[ eq1 ] = true; + return; + } + } }else{ - antec_new_lits.push_back( ni.eqNode( nj ).negate() ); + Trace("strings-solve") << "Non-Simple Case 1 : add lemma " << std::endl; + //must add lemma + std::vector< Node > antec; + std::vector< Node > antec_new_lits; + antec.insert( antec.end(), d_normal_forms_exp[ni].begin(), d_normal_forms_exp[ni].end() ); + antec.insert( antec.end(), d_normal_forms_exp[nj].begin(), d_normal_forms_exp[nj].end() ); + //check disequal + if( areDisequal( ni, nj ) ){ + antec.push_back( ni.eqNode( nj ).negate() ); + }else{ + antec_new_lits.push_back( ni.eqNode( nj ).negate() ); + } + antec_new_lits.push_back( li.eqNode( lj ).negate() ); + std::vector< Node > conc; + Node sk1 = mkSkolemCached( i, j, sk_id_deq_x, "x_dsplit" ); + Node sk2 = mkSkolemCached( i, j, sk_id_deq_y, "y_dsplit" ); + Node sk3 = mkSkolemCached( i, j, sk_id_deq_z, "z_dsplit", 1 ); + //Node nemp = sk3.eqNode(d_emptyString).negate(); + //conc.push_back(nemp); + Node lsk1 = mkLength( sk1 ); + conc.push_back( lsk1.eqNode( li ) ); + Node lsk2 = mkLength( sk2 ); + conc.push_back( lsk2.eqNode( lj ) ); + conc.push_back( NodeManager::currentNM()->mkNode( kind::OR, j.eqNode( mkConcat( sk1, sk3 ) ), i.eqNode( mkConcat( sk2, sk3 ) ) ) ); + sendInference( antec, antec_new_lits, NodeManager::currentNM()->mkNode( kind::AND, conc ), "D-DISL-Split" ); + ++(d_statistics.d_deq_splits); + return; } - antec_new_lits.push_back( li.eqNode( lj ).negate() ); - std::vector< Node > conc; - Node sk1 = mkSkolemCached( i, j, sk_id_deq_x, "x_dsplit" ); - Node sk2 = mkSkolemCached( i, j, sk_id_deq_y, "y_dsplit" ); - Node sk3 = mkSkolemCached( i, j, sk_id_deq_z, "z_dsplit", 1 ); - //Node nemp = sk3.eqNode(d_emptyString).negate(); - //conc.push_back(nemp); - Node lsk1 = mkLength( sk1 ); - conc.push_back( lsk1.eqNode( li ) ); - Node lsk2 = mkLength( sk2 ); - conc.push_back( lsk2.eqNode( lj ) ); - conc.push_back( NodeManager::currentNM()->mkNode( kind::OR, j.eqNode( mkConcat( sk1, sk3 ) ), i.eqNode( mkConcat( sk2, sk3 ) ) ) ); - sendInference( antec, antec_new_lits, NodeManager::currentNM()->mkNode( kind::AND, conc ), "D-DISL-Split" ); - ++(d_statistics.d_deq_splits); - return true; }else if( areEqual( li, lj ) ){ Assert( !areDisequal( i, j ) ); //splitting on demand : try to make them disequal @@ -2620,14 +3005,14 @@ bool TheoryStrings::processDeq( Node ni, Node nj ) { sendSplit( i, j, "S-Split(DEQL)" ); eq = Rewriter::rewrite( eq ); d_pending_req_phase[ eq ] = false; - return true; + return; }else{ //splitting on demand : try to make lengths equal Node eq = li.eqNode( lj ); sendSplit( li, lj, "D-Split" ); eq = Rewriter::rewrite( eq ); d_pending_req_phase[ eq ] = true; - return true; + return; } } index++; @@ -2635,7 +3020,6 @@ bool TheoryStrings::processDeq( Node ni, Node nj ) { } Assert( false ); } - return false; } int TheoryStrings::processReverseDeq( std::vector< Node >& nfi, std::vector< Node >& nfj, Node ni, Node nj ) { @@ -2653,9 +3037,19 @@ int TheoryStrings::processReverseDeq( std::vector< Node >& nfi, std::vector< Nod return ret; } -int TheoryStrings::processSimpleDeq( std::vector< Node >& nfi, std::vector< Node >& nfj, Node ni, Node nj, unsigned& index, bool isRev ) { +int TheoryStrings::processSimpleDeq( std::vector< Node >& nfi, std::vector< Node >& nfj, Node ni, Node nj, unsigned& index, bool isRev ){ + //see if one side is constant, if so, we can approximate as containment + for( unsigned i=0; i<2; i++ ){ + Node c = getConstantEqc( i==0 ? ni : nj ); + if( !c.isNull() ){ + int findex, lindex; + if( !TheoryStringsRewriter::canConstantContainList( c, i==0 ? nfj : nfi, findex, lindex ) ){ + return 1; + } + } + } while( index<nfi.size() || index<nfj.size() ) { - if( index>=nfi.size() || index>=nfj.size() ) { + if( index>=nfi.size() || index>=nfj.size() ){ Trace("strings-solve-debug") << "Disequality normalize empty" << std::endl; std::vector< Node > ant; //we have a conflict : because the lengths are equal, the remainder needs to be empty, which will lead to a conflict @@ -2673,7 +3067,7 @@ int TheoryStrings::processSimpleDeq( std::vector< Node >& nfi, std::vector< Node conc = Rewriter::rewrite( conc ); sendInference( ant, conc, "Disequality Normalize Empty", true); return -1; - } else { + }else{ Node i = nfi[index]; Node j = nfj[index]; Trace("strings-solve-debug") << "...Processing(QED) " << i << " " << j << std::endl; @@ -2687,12 +3081,12 @@ int TheoryStrings::processSimpleDeq( std::vector< Node >& nfi, std::vector< Node Node nk = i.getConst<String>().size() < j.getConst<String>().size() ? i : j; Node nl = i.getConst<String>().size() < j.getConst<String>().size() ? j : i; Node remainderStr; - if(isRev) { + if( isRev ){ int new_len = nl.getConst<String>().size() - len_short; remainderStr = NodeManager::currentNM()->mkConst( nl.getConst<String>().substr(0, new_len) ); Trace("strings-solve-debug-test") << "Rev. Break normal form of " << nl << " into " << nk << ", " << remainderStr << std::endl; } else { - remainderStr = NodeManager::currentNM()->mkConst( j.getConst<String>().substr(len_short) ); + remainderStr = NodeManager::currentNM()->mkConst( nl.getConst<String>().substr( len_short ) ); Trace("strings-solve-debug-test") << "Break normal form of " << nl << " into " << nk << ", " << remainderStr << std::endl; } if( i.getConst<String>().size() < j.getConst<String>().size() ) { @@ -2702,18 +3096,18 @@ int TheoryStrings::processSimpleDeq( std::vector< Node >& nfi, std::vector< Node nfi.insert( nfi.begin() + index + 1, remainderStr ); nfi[index] = nfj[index]; } - } else { + }else{ return 1; } - } else { + }else{ std::vector< Node > lexp; Node li = getLength( i, lexp ); Node lj = getLength( j, lexp ); - if( areEqual( li, lj ) && areDisequal( i, j ) ) { + if( areEqual( li, lj ) && areDisequal( i, j ) ){ Trace("strings-solve") << "Simple Case 2 : found equal length disequal sub strings " << i << " " << j << std::endl; //we are done: D-Remove return 1; - } else { + }else{ return 0; } } @@ -2781,14 +3175,24 @@ void TheoryStrings::registerTerm( Node n, int effort ) { if(n.getType().isString()) { //register length information: // for variables, split on empty vs positive length - // for concat/const, introduce proxy var and state length relation + // for concat/const/replace, introduce proxy var and state length relation + Node lsum; + bool processed = false; if( n.getKind()!=kind::STRING_CONCAT && n.getKind()!=kind::CONST_STRING ) { - if( d_length_intro_vars.find(n)==d_length_intro_vars.end() ) { - sendLengthLemma( n ); - ++(d_statistics.d_splits); + if( d_length_lemma_terms_cache.find( n )==d_length_lemma_terms_cache.end() ){ + Node lsumb = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, n ); + lsum = Rewriter::rewrite( lsumb ); + // can register length term if it does not rewrite + if( lsum==lsumb ){ + sendLengthLemma( n ); + processed = true; + } + }else{ + processed = true; } - } else { - Node sk = mkSkolemS("lsym", 2); + } + if( !processed ){ + Node sk = mkSkolemS( "lsym", -1 ); StringsProxyVarAttribute spva; sk.setAttribute(spva,true); Node eq = Rewriter::rewrite( sk.eqNode(n) ); @@ -2797,8 +3201,7 @@ void TheoryStrings::registerTerm( Node n, int effort ) { Trace("strings-assert") << "(assert " << eq << ")" << std::endl; d_out->lemma(eq); Node skl = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk ); - Node lsum; - if( n.getKind() == kind::STRING_CONCAT ) { + if( n.getKind()==kind::STRING_CONCAT ){ std::vector<Node> node_vec; for( unsigned i=0; i<n.getNumChildren(); i++ ) { if( n[i].getAttribute(StringsProxyVarAttribute()) ){ @@ -2810,16 +3213,18 @@ void TheoryStrings::registerTerm( Node n, int effort ) { } } lsum = NodeManager::currentNM()->mkNode( kind::PLUS, node_vec ); - } else if( n.getKind() == kind::CONST_STRING ) { + lsum = Rewriter::rewrite( lsum ); + }else if( n.getKind()==kind::CONST_STRING ){ lsum = NodeManager::currentNM()->mkConst( ::CVC4::Rational( n.getConst<String>().size() ) ); } - lsum = Rewriter::rewrite( lsum ); + Assert( !lsum.isNull() ); d_proxy_var_to_length[sk] = lsum; Node ceq = Rewriter::rewrite( skl.eqNode( lsum ) ); Trace("strings-lemma") << "Strings::Lemma LENGTH : " << ceq << std::endl; Trace("strings-lemma-debug") << " prerewrite : " << skl.eqNode( lsum ) << std::endl; Trace("strings-assert") << "(assert " << ceq << ")" << std::endl; d_out->lemma(ceq); + } } else { AlwaysAssert(false, "String Terms only in registerTerm."); @@ -3035,15 +3440,27 @@ Node TheoryStrings::mkSkolemCached( Node a, Node b, int id, const char * c, int } } -//isLenSplit: 0-yes, 1-no, 2-ignore +//isLenSplit: -1-ignore, 0-no restriction, 1-greater than one, 2-one Node TheoryStrings::mkSkolemS( const char *c, int isLenSplit ) { Node n = NodeManager::currentNM()->mkSkolem( c, NodeManager::currentNM()->stringType(), "string sko" ); - d_length_intro_vars.insert(n); + d_length_lemma_terms_cache.insert( n ); ++(d_statistics.d_new_skolems); - if(isLenSplit == 0) { + if( isLenSplit==0 ){ sendLengthLemma( n ); - ++(d_statistics.d_splits); - } else if(isLenSplit == 1) { + } else if( isLenSplit == 1 ){ + registerNonEmptySkolem( n ); + }else if( isLenSplit==2 ){ + Node len_one = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, n ).eqNode( d_one ); + Trace("strings-lemma") << "Strings::Lemma SK-ONE : " << len_one << std::endl; + Trace("strings-assert") << "(assert " << len_one << ")" << std::endl; + d_out->lemma( len_one ); + } + return n; +} + +void TheoryStrings::registerNonEmptySkolem( Node n ) { + if( d_skolem_ne_reg_cache.find( n )==d_skolem_ne_reg_cache.end() ){ + d_skolem_ne_reg_cache.insert( n ); d_equalityEngine.assertEquality(n.eqNode(d_emptyString), false, d_true); Node len_n_gt_z = NodeManager::currentNM()->mkNode(kind::GT, NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, n), d_zero); @@ -3051,7 +3468,6 @@ Node TheoryStrings::mkSkolemS( const char *c, int isLenSplit ) { Trace("strings-assert") << "(assert " << len_n_gt_z << ")" << std::endl; d_out->lemma(len_n_gt_z); } - return n; } Node TheoryStrings::mkExplain( std::vector< Node >& a ) { @@ -3146,7 +3562,6 @@ void TheoryStrings::checkDeqNF() { 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]; @@ -3165,13 +3580,12 @@ void TheoryStrings::checkDeqNF() { 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 ){ + if( !hasProcessed() ){ separateByLength( d_strings_eqc, cols, lts ); for( unsigned i=0; i<cols.size(); i++ ){ if( cols[i].size()>1 && d_lemma_cache.empty() ){ @@ -3189,7 +3603,8 @@ void TheoryStrings::checkDeqNF() { 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] ) ){ + processDeq( cols[i][j], cols[i][k] ); + if( hasProcessed() ){ return; } } @@ -3212,14 +3627,24 @@ void TheoryStrings::checkLengthsEqc() { Node llt = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, lt ); //now, check if length normalization has occurred if( ei->d_normalized_length.get().isNull() ) { + Node nf = mkConcat( d_normal_forms[d_strings_eqc[i]] ); + if( Trace.isOn("strings-process-debug") ){ + Trace("strings-process-debug") << " normal form is " << nf << " from base " << d_normal_forms_base[d_strings_eqc[i]] << std::endl; + Trace("strings-process-debug") << " normal form exp is: " << std::endl; + for( unsigned j=0; j<d_normal_forms_exp[d_strings_eqc[i]].size(); j++ ){ + Trace("strings-process-debug") << " " << d_normal_forms_exp[d_strings_eqc[i]][j] << std::endl; + } + } + //if not, add the lemma std::vector< Node > ant; ant.insert( ant.end(), d_normal_forms_exp[d_strings_eqc[i]].begin(), d_normal_forms_exp[d_strings_eqc[i]].end() ); ant.push_back( d_normal_forms_base[d_strings_eqc[i]].eqNode( lt ) ); - Node lc = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, mkConcat( d_normal_forms[d_strings_eqc[i]] ) ); - lc = Rewriter::rewrite( lc ); - Node eq = llt.eqNode( lc ); - if( llt!=lc ){ + Node lc = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, nf ); + Node lcr = Rewriter::rewrite( lc ); + Trace("strings-process-debug") << "Rewrote length " << lc << " to " << lcr << std::endl; + Node eq = llt.eqNode( lcr ); + if( llt!=lcr ){ ei->d_normalized_length.set( eq ); sendInference( ant, eq, "LEN-NORM", true ); } @@ -3330,44 +3755,6 @@ void TheoryStrings::getEquivalenceClasses( std::vector< Node >& eqcs ) { } } -void TheoryStrings::getFinalNormalForm( Node n, std::vector< Node >& nf, std::vector< Node >& exp ) { - if( n!=d_emptyString ) { - if( n.getKind()==kind::STRING_CONCAT ) { - for( unsigned i=0; i<n.getNumChildren(); i++ ) { - getFinalNormalForm( n[i], nf, exp ); - } - } else { - Trace("strings-debug") << "Get final normal form " << n << std::endl; - Assert( d_equalityEngine.hasTerm( n ) ); - Node nr = d_equalityEngine.getRepresentative( n ); - EqcInfo *eqc_n = getOrMakeEqcInfo( nr, false ); - Node nc = eqc_n ? eqc_n->d_const_term.get() : Node::null(); - if( !nc.isNull() ) { - nf.push_back( nc ); - if( n!=nc ) { - exp.push_back( NodeManager::currentNM()->mkNode( kind::EQUAL, n, nc ) ); - } - } else { - Assert( d_normal_forms.find( nr )!=d_normal_forms.end() ); - if( d_normal_forms[nr][0]==nr ) { - Assert( d_normal_forms[nr].size()==1 ); - nf.push_back( nr ); - if( n!=nr ) { - exp.push_back( NodeManager::currentNM()->mkNode( kind::EQUAL, n, nr ) ); - } - } else { - for( unsigned i=0; i<d_normal_forms[nr].size(); i++ ) { - Assert( d_normal_forms[nr][i]!=nr ); - getFinalNormalForm( d_normal_forms[nr][i], nf, exp ); - } - exp.insert( exp.end(), d_normal_forms_exp[nr].begin(), d_normal_forms_exp[nr].end() ); - } - } - Trace("strings-ind-nf") << "The final normal form of " << n << " is " << nf << std::endl; - } - } -} - void TheoryStrings::separateByLength(std::vector< Node >& n, std::vector< std::vector< Node > >& cols, std::vector< Node >& lts ) { @@ -3409,17 +3796,139 @@ void TheoryStrings::printConcat( std::vector< Node >& n, const char * c ) { } -//// Measurements -/* -void TheoryStrings::updateMpl( Node n, int b ) { - if(d_mpl.find(n) == d_mpl.end()) { - //d_curr_cardinality.get(); - d_mpl[n] = b; - } else if(b < d_mpl[n]) { - d_mpl[n] = b; + +//// Finite Model Finding + +Node TheoryStrings::getNextDecisionRequest() { + if( options::stringFMF() && !d_conflict ){ + Node in_var_lsum = d_input_var_lsum.get(); + //Trace("strings-fmf-debug") << "Strings::FMF: Assertion Level = " << d_valuation.getAssertionLevel() << std::endl; + //initialize the term we will minimize + if( in_var_lsum.isNull() && !d_input_vars.empty() ){ + Trace("strings-fmf-debug") << "Input variables: "; + std::vector< Node > ll; + for(NodeSet::key_iterator itr = d_input_vars.key_begin(); + itr != d_input_vars.key_end(); ++itr) { + Trace("strings-fmf-debug") << " " << (*itr) ; + ll.push_back( NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, *itr ) ); + } + Trace("strings-fmf-debug") << std::endl; + in_var_lsum = ll.size()==1 ? ll[0] : NodeManager::currentNM()->mkNode( kind::PLUS, ll ); + in_var_lsum = Rewriter::rewrite( in_var_lsum ); + d_input_var_lsum.set( in_var_lsum ); + } + if( !in_var_lsum.isNull() ){ + //Trace("strings-fmf") << "Get next decision request." << std::endl; + //check if we need to decide on something + int decideCard = d_curr_cardinality.get(); + if( d_cardinality_lits.find( decideCard )!=d_cardinality_lits.end() ){ + bool value; + Node cnode = d_cardinality_lits[ d_curr_cardinality.get() ]; + if( d_valuation.hasSatValue( cnode, value ) ) { + if( !value ){ + d_curr_cardinality.set( d_curr_cardinality.get() + 1 ); + decideCard = d_curr_cardinality.get(); + Trace("strings-fmf-debug") << "Has false SAT value, increment and decide." << std::endl; + }else{ + decideCard = -1; + Trace("strings-fmf-debug") << "Has true SAT value, do not decide." << std::endl; + } + }else{ + Trace("strings-fmf-debug") << "No SAT value, decide." << std::endl; + } + } + if( decideCard!=-1 ){ + if( d_cardinality_lits.find( decideCard )==d_cardinality_lits.end() ){ + Node lit = NodeManager::currentNM()->mkNode( kind::LEQ, in_var_lsum, NodeManager::currentNM()->mkConst( Rational( decideCard ) ) ); + lit = Rewriter::rewrite( lit ); + d_cardinality_lits[decideCard] = lit; + Node lem = NodeManager::currentNM()->mkNode( kind::OR, lit, lit.negate() ); + Trace("strings-fmf") << "Strings::FMF: Add decision lemma " << lem << ", decideCard = " << decideCard << std::endl; + d_out->lemma( lem ); + d_out->requirePhase( lit, true ); + } + Node lit = d_cardinality_lits[ decideCard ]; + Trace("strings-fmf") << "Strings::FMF: Decide positive on " << lit << std::endl; + return lit; + } + } + } + return Node::null(); +} + +Node TheoryStrings::ppRewrite(TNode atom) { + Trace("strings-ppr") << "TheoryStrings::ppRewrite " << atom << std::endl; + if( !options::stringLazyPreproc() ){ + //eager preprocess here + std::vector< Node > new_nodes; + Node ret = d_preproc.processAssertion( atom, new_nodes ); + if( ret!=atom ){ + Trace("strings-ppr") << " rewrote " << atom << " -> " << ret << ", with " << new_nodes.size() << " lemmas." << std::endl; + for( unsigned i=0; i<new_nodes.size(); i++ ){ + Trace("strings-ppr") << " lemma : " << new_nodes[i] << std::endl; + d_out->lemma( new_nodes[i] ); + } + return ret; + }else{ + Assert( new_nodes.empty() ); + } + } + return atom; +} + +void TheoryStrings::collectExtendedFuncTerms( Node n, std::map< Node, bool >& visited ) { + if( visited.find( n )==visited.end() ){ + visited[n] = true; + d_extt->registerTerm( n ); + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + collectExtendedFuncTerms( n[i], visited ); + } } } -*/ + +// Stats +TheoryStrings::Statistics::Statistics(): + d_splits("TheoryStrings::NumOfSplitOnDemands", 0), + d_eq_splits("TheoryStrings::NumOfEqSplits", 0), + d_deq_splits("TheoryStrings::NumOfDiseqSplits", 0), + d_loop_lemmas("TheoryStrings::NumOfLoops", 0), + d_new_skolems("TheoryStrings::NumOfNewSkolems", 0) +{ + smtStatisticsRegistry()->registerStat(&d_splits); + smtStatisticsRegistry()->registerStat(&d_eq_splits); + smtStatisticsRegistry()->registerStat(&d_deq_splits); + smtStatisticsRegistry()->registerStat(&d_loop_lemmas); + smtStatisticsRegistry()->registerStat(&d_new_skolems); +} + +TheoryStrings::Statistics::~Statistics(){ + smtStatisticsRegistry()->unregisterStat(&d_splits); + smtStatisticsRegistry()->unregisterStat(&d_eq_splits); + smtStatisticsRegistry()->unregisterStat(&d_deq_splits); + smtStatisticsRegistry()->unregisterStat(&d_loop_lemmas); + smtStatisticsRegistry()->unregisterStat(&d_new_skolems); +} + + + + + + + + + + + + + + + + + + + + +//// Regular Expressions unsigned TheoryStrings::getNumMemberships( Node n, bool isPos ) { @@ -3441,7 +3950,6 @@ 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()) { return Rewriter::rewrite( NodeManager::currentNM()->mkNode(kind::AND, ant, atom) ); @@ -3749,7 +4257,7 @@ bool TheoryStrings::checkMemberships2() { } else { //TODO: split } - */ + */ } Assert(false); //TODO:tmp } @@ -3759,6 +4267,18 @@ bool TheoryStrings::checkMemberships2() { } void TheoryStrings::checkMemberships() { + //add the memberships + std::vector< Node > mems; + d_extt->getActive( mems, kind::STRING_IN_REGEXP ); + for( unsigned i=0; i<mems.size(); i++ ){ + Node n = mems[i]; + Assert( d_extf_info_tmp.find( n )!=d_extf_info_tmp.end() ); + Assert( d_extf_info_tmp[n].d_pol==1 || d_extf_info_tmp[n].d_pol==-1 ); + bool pol = d_extf_info_tmp[n].d_pol==1; + Trace("strings-process-debug") << " add membership : " << n << ", pol = " << pol << std::endl; + addMembership( pol ? n : n.negate() ); + } + bool addedLemma = false; bool changed = false; std::vector< Node > processed; @@ -3840,7 +4360,8 @@ void TheoryStrings::checkMemberships() { Node r = atom[1]; std::vector< Node > rnfexp; - if(options::stringOpt1()) { + //if(options::stringOpt1()) { + if(true){ if(!x.isConst()) { x = getNormalString( x, rnfexp); changed = true; @@ -3947,7 +4468,7 @@ void TheoryStrings::checkMemberships() { antec = Rewriter::rewrite( NodeManager::currentNM()->mkNode(kind::AND, antec, mkExplain(rnfexp)) ); Node conc = nvec.size()==1 ? nvec[0] : NodeManager::currentNM()->mkNode(kind::AND, nvec); conc = Rewriter::rewrite(conc); - sendLemma( antec, conc, "REGEXP" ); + sendLemma( antec, conc, "REGEXP_Unfold" ); addedLemma = true; if(changed) { cprocessed.push_back( assertion ); @@ -3955,7 +4476,7 @@ void TheoryStrings::checkMemberships() { processed.push_back( assertion ); } //d_regexp_ucached[assertion] = true; - } else { + }else{ Trace("strings-regexp") << "Unroll/simplify membership of non-atomic term " << xr << " = "; for( unsigned j=0; j<d_normal_forms[xr].size(); j++ ){ Trace("strings-regexp") << d_normal_forms[xr][j] << " "; @@ -4075,14 +4596,7 @@ void TheoryStrings::checkMemberships() { bool TheoryStrings::checkPDerivative(Node x, Node r, Node atom, bool &addedLemma, std::vector< Node > &processed, std::vector< Node > &cprocessed, std::vector< Node > &nf_exp) { - /*if(d_opt_regexp_gcd) { - if(d_membership_length.find(atom) == d_membership_length.end()) { - addedLemma = addMembershipLength(atom); - d_membership_length[atom] = true; - } else { - Trace("strings-regexp") << "Membership length is already added." << std::endl; - } - }*/ + Node antnf = mkExplain(nf_exp); if(areEqual(x, d_emptyString)) { @@ -4141,204 +4655,6 @@ bool TheoryStrings::checkPDerivative(Node x, Node r, Node atom, bool &addedLemma return true; } -void TheoryStrings::checkConstantEquivalenceClasses( TermIndex* ti, std::vector< Node >& vecc ) { - Node n = ti->d_data; - if( !n.isNull() ){ - //construct the constant - Node c = mkConcat( vecc ); - if( !areEqual( n, c ) ){ - Trace("strings-debug") << "Constant eqc : " << c << " for " << n << std::endl; - Trace("strings-debug") << " "; - for( unsigned i=0; i<vecc.size(); i++ ){ - Trace("strings-debug") << vecc[i] << " "; - } - Trace("strings-debug") << std::endl; - unsigned count = 0; - unsigned countc = 0; - std::vector< Node > exp; - while( count<n.getNumChildren() ){ - while( count<n.getNumChildren() && areEqual( n[count], d_emptyString ) ){ - addToExplanation( n[count], d_emptyString, exp ); - count++; - } - if( count<n.getNumChildren() ){ - Trace("strings-debug") << "...explain " << n[count] << " " << vecc[countc] << std::endl; - if( !areEqual( n[count], vecc[countc] ) ){ - Node nrr = getRepresentative( n[count] ); - Assert( !d_eqc_to_const_exp[nrr].isNull() ); - addToExplanation( n[count], d_eqc_to_const_base[nrr], exp ); - exp.push_back( d_eqc_to_const_exp[nrr] ); - }else{ - addToExplanation( n[count], vecc[countc], exp ); - } - countc++; - count++; - } - } - //exp contains an explanation of n==c - Assert( countc==vecc.size() ); - if( hasTerm( c ) ){ - sendInference( exp, n.eqNode( c ), "I_CONST_MERGE" ); - return; - }else if( !hasProcessed() ){ - Node nr = getRepresentative( n ); - std::map< Node, Node >::iterator it = d_eqc_to_const.find( nr ); - if( it==d_eqc_to_const.end() ){ - Trace("strings-debug") << "Set eqc const " << n << " to " << c << std::endl; - d_eqc_to_const[nr] = c; - d_eqc_to_const_base[nr] = n; - d_eqc_to_const_exp[nr] = mkAnd( exp ); - }else if( c!=it->second ){ - //conflict - Trace("strings-debug") << "Conflict, other constant was " << it->second << ", this constant was " << c << std::endl; - if( d_eqc_to_const_exp[nr].isNull() ){ - // n==c ^ n == c' => false - addToExplanation( n, it->second, exp ); - }else{ - // n==c ^ n == d_eqc_to_const_base[nr] == c' => false - exp.push_back( d_eqc_to_const_exp[nr] ); - addToExplanation( n, d_eqc_to_const_base[nr], exp ); - } - sendInference( exp, d_false, "I_CONST_CONFLICT" ); - return; - }else{ - Trace("strings-debug") << "Duplicate constant." << std::endl; - } - } - } - } - for( std::map< Node, TermIndex >::iterator it = ti->d_children.begin(); it != ti->d_children.end(); ++it ){ - std::map< Node, Node >::iterator itc = d_eqc_to_const.find( it->first ); - if( itc!=d_eqc_to_const.end() ){ - vecc.push_back( itc->second ); - checkConstantEquivalenceClasses( &it->second, vecc ); - vecc.pop_back(); - if( hasProcessed() ){ - break; - } - } - } -} - -void TheoryStrings::checkExtendedFuncs() { - if( options::stringExp() ){ - checkExtfReduction( 2 ); - } - if( !hasProcessed() ){ - //collect all remaining extended functions - std::vector< Node > pnContains; - std::map< bool, std::vector< Node > > pnMem; - for( NodeBoolMap::iterator it = d_ext_func_terms.begin(); it != d_ext_func_terms.end(); ++it ){ - if( (*it).second ){ - Node n = (*it).first; - if( n.getKind()==kind::STRING_STRCTN ) { - if( d_extf_pol[n]!=1 ){ - Assert( d_extf_pol[n]==-1 ); - pnContains.push_back( n ); - } - }else if( n.getKind()==kind::STRING_IN_REGEXP ) { - bool pol = d_extf_pol[n]==1; - Assert( d_extf_pol[n]==1 || d_extf_pol[n]==-1 ); - pnMem[pol].push_back( n ); - } - } - } - Trace("strings-process-debug") << "Checking negative contains..." << std::endl; - checkNegContains( pnContains ); - Trace("strings-process-debug") << "Done check negative contain constraints, addedLemma = " << !d_pending.empty() << " " << !d_lemma_cache.empty() << ", d_conflict = " << d_conflict << std::endl; - if( !hasProcessed() ) { - Trace("strings-process") << "Adding memberships..." << std::endl; - //add all non-evaluated memberships - for( std::map< bool, std::vector< Node > >::iterator it=pnMem.begin(); it != pnMem.end(); ++it ){ - for( unsigned i=0; i<it->second.size(); i++ ){ - Trace("strings-process-debug") << " add membership : " << it->second[i] << ", pol = " << it->first << std::endl; - addMembership( it->first ? it->second[i] : it->second[i].negate() ); - } - } - Trace("strings-process") << "Checking memberships..." << std::endl; - checkMemberships(); - Trace("strings-process") << "Done check memberships, addedLemma = " << !d_pending.empty() << " " << !d_lemma_cache.empty() << ", d_conflict = " << d_conflict << std::endl; - } - } -} - -void TheoryStrings::checkNegContains( std::vector< Node >& negContains ) { - for( unsigned i=0; i<negContains.size(); i++ ){ - Node atom = negContains[i]; - Trace("strings-ctn") << "We have negative contain assertion : (not " << atom << " )" << std::endl; - //should have already reduced these things by now - Assert( !areEqual( atom[1], d_emptyString ) ); - Assert( !areEqual( atom[1], atom[0] ) ); - } - //check for lemmas - if(options::stringExp()) { - for( unsigned i=0; i<negContains.size(); i++ ){ - Node atom = negContains[i]; - Node x = atom[0]; - Node s = atom[1]; - std::vector< Node > lexp; - Node lenx = getLength( x, lexp ); - Node lens = getLength( s, lexp ); - if( areEqual(lenx, lens) ){ - if(d_neg_ctn_eqlen.find(atom) == d_neg_ctn_eqlen.end()) { - lexp.push_back( lenx.eqNode(lens) ); - lexp.push_back( atom.negate() ); - Node xneqs = x.eqNode(s).negate(); - d_neg_ctn_eqlen.insert( atom ); - sendInference( lexp, xneqs, "NEG-CTN-EQL", true ); - } - }else if( !areDisequal( lenx, lens ) ){ - if(d_neg_ctn_ulen.find(atom) == d_neg_ctn_ulen.end()) { - lenx = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, x); - lens = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, s); - d_neg_ctn_ulen.insert( atom ); - sendSplit( lenx, lens, "NEG-CTN-SP" ); - } - }else{ - if(d_neg_ctn_cached.find(atom) == d_neg_ctn_cached.end()) { - lenx = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, x); - lens = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, s); - Node b1 = NodeManager::currentNM()->mkBoundVar(NodeManager::currentNM()->integerType()); - Node b1v = NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, b1); - Node g1 = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::AND, NodeManager::currentNM()->mkNode( kind::GEQ, b1, d_zero ), - NodeManager::currentNM()->mkNode( kind::GEQ, NodeManager::currentNM()->mkNode( kind::MINUS, lenx, lens ), b1 ) ) ); - Node b2 = NodeManager::currentNM()->mkBoundVar(NodeManager::currentNM()->integerType()); - Node s2 = NodeManager::currentNM()->mkNode(kind::STRING_SUBSTR, x, NodeManager::currentNM()->mkNode( kind::PLUS, b1, b2 ), d_one); - Node s5 = NodeManager::currentNM()->mkNode(kind::STRING_SUBSTR, s, b2, d_one); - - Node b2v = NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, b2);//, s1, s3, s4, s6); - - std::vector< Node > vec_nodes; - Node cc = NodeManager::currentNM()->mkNode( kind::GEQ, b2, d_zero ); - vec_nodes.push_back(cc); - cc = NodeManager::currentNM()->mkNode( kind::GT, lens, b2 ); - vec_nodes.push_back(cc); - - cc = s2.eqNode(s5).negate(); - vec_nodes.push_back(cc); - - Node conc = NodeManager::currentNM()->mkNode(kind::AND, vec_nodes); - conc = NodeManager::currentNM()->mkNode( kind::EXISTS, b2v, conc ); - conc = NodeManager::currentNM()->mkNode( kind::IMPLIES, g1, conc ); - conc = NodeManager::currentNM()->mkNode( kind::FORALL, b1v, conc ); - Node xlss = NodeManager::currentNM()->mkNode( kind::GT, lens, lenx ); - conc = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::OR, xlss, conc ) ); - - d_neg_ctn_cached.insert( atom ); - std::vector< Node > exp; - exp.push_back( atom.negate() ); - sendInference( d_empty_vec, exp, conc, "NEG-CTN-BRK", true ); - //d_pending_req_phase[xlss] = true; - } - } - } - } else { - if( !negContains.empty() ){ - throw LogicException("Strings Incomplete (due to Negative Contain) by default, try --strings-exp option."); - } - } -} - CVC4::String TheoryStrings::getHeadConst( Node x ) { if( x.isConst() ) { return x.getConst< String >(); @@ -4353,27 +4669,6 @@ CVC4::String TheoryStrings::getHeadConst( Node x ) { } } -bool TheoryStrings::addMembershipLength(Node atom) { - //Node x = atom[0]; - //Node r = atom[1]; - - /*std::vector< int > co; - co.push_back(0); - for(unsigned int k=0; k<lts.size(); ++k) { - if(lts[k].isConst() && lts[k].getType().isInteger()) { - int len = lts[k].getConst<Rational>().getNumerator().toUnsignedInt(); - co[0] += cols[k].size() * len; - } else { - co.push_back( cols[k].size() ); - } - } - int g_co = co[0]; - for(unsigned k=1; k<co.size(); ++k) { - g_co = gcd(g_co, co[k]); - }*/ - return false; -} - bool TheoryStrings::deriveRegExp( Node x, Node r, Node ant ) { // TODO cstr in vre Assert(x != d_emptyString); @@ -4494,7 +4789,7 @@ void TheoryStrings::addMembership(Node assertion) { } } -Node TheoryStrings::getNormalString( Node x, std::vector<Node> &nf_exp ){ +Node TheoryStrings::getNormalString( Node x, std::vector< Node >& nf_exp ){ if( !x.isConst() ){ Node xr = getRepresentative( x ); if( d_normal_forms.find( xr ) != d_normal_forms.end() ){ @@ -4570,111 +4865,9 @@ Node TheoryStrings::getNormalSymRegExp(Node r, std::vector<Node> &nf_exp) { //return Node::null(); } } - return ret; } -//// Finite Model Finding - -Node TheoryStrings::getNextDecisionRequest() { - if( options::stringFMF() && !d_conflict ){ - Node in_var_lsum = d_input_var_lsum.get(); - //Trace("strings-fmf-debug") << "Strings::FMF: Assertion Level = " << d_valuation.getAssertionLevel() << std::endl; - //initialize the term we will minimize - if( in_var_lsum.isNull() && !d_input_vars.empty() ){ - Trace("strings-fmf-debug") << "Input variables: "; - std::vector< Node > ll; - for(NodeSet::key_iterator itr = d_input_vars.key_begin(); - itr != d_input_vars.key_end(); ++itr) { - Trace("strings-fmf-debug") << " " << (*itr) ; - ll.push_back( NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, *itr ) ); - } - Trace("strings-fmf-debug") << std::endl; - in_var_lsum = ll.size()==1 ? ll[0] : NodeManager::currentNM()->mkNode( kind::PLUS, ll ); - in_var_lsum = Rewriter::rewrite( in_var_lsum ); - d_input_var_lsum.set( in_var_lsum ); - } - if( !in_var_lsum.isNull() ){ - //Trace("strings-fmf") << "Get next decision request." << std::endl; - //check if we need to decide on something - int decideCard = d_curr_cardinality.get(); - if( d_cardinality_lits.find( decideCard )!=d_cardinality_lits.end() ){ - bool value; - Node cnode = d_cardinality_lits[ d_curr_cardinality.get() ]; - if( d_valuation.hasSatValue( cnode, value ) ) { - if( !value ){ - d_curr_cardinality.set( d_curr_cardinality.get() + 1 ); - decideCard = d_curr_cardinality.get(); - Trace("strings-fmf-debug") << "Has false SAT value, increment and decide." << std::endl; - }else{ - decideCard = -1; - Trace("strings-fmf-debug") << "Has true SAT value, do not decide." << std::endl; - } - }else{ - Trace("strings-fmf-debug") << "No SAT value, decide." << std::endl; - } - } - if( decideCard!=-1 ){ - if( d_cardinality_lits.find( decideCard )==d_cardinality_lits.end() ){ - Node lit = NodeManager::currentNM()->mkNode( kind::LEQ, in_var_lsum, NodeManager::currentNM()->mkConst( Rational( decideCard ) ) ); - lit = Rewriter::rewrite( lit ); - d_cardinality_lits[decideCard] = lit; - Node lem = NodeManager::currentNM()->mkNode( kind::OR, lit, lit.negate() ); - Trace("strings-fmf") << "Strings::FMF: Add decision lemma " << lem << ", decideCard = " << decideCard << std::endl; - d_out->lemma( lem ); - d_out->requirePhase( lit, true ); - } - Node lit = d_cardinality_lits[ decideCard ]; - Trace("strings-fmf") << "Strings::FMF: Decide positive on " << lit << std::endl; - return lit; - } - } - } - - return Node::null(); -} - -void TheoryStrings::collectExtendedFuncTerms( Node n, std::map< Node, bool >& visited ) { - if( visited.find( n )==visited.end() ){ - visited[n] = true; - if( n.getKind()==kind::STRING_SUBSTR || n.getKind()==kind::STRING_STRIDOF || - n.getKind() == kind::STRING_ITOS || n.getKind() == kind::STRING_U16TOS || n.getKind() == kind::STRING_U32TOS || - n.getKind() == kind::STRING_STOI || n.getKind() == kind::STRING_STOU16 || n.getKind() == kind::STRING_STOU32 || - n.getKind() == kind::STRING_STRREPL || n.getKind() == kind::STRING_STRCTN ){ - if( d_ext_func_terms.find( n )==d_ext_func_terms.end() ){ - Trace("strings-extf-debug2") << "Found extended function : " << n << std::endl; - d_ext_func_terms[n] = true; - } - } - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - collectExtendedFuncTerms( n[i], visited ); - } - } -} - -// Stats -TheoryStrings::Statistics::Statistics(): - d_splits("TheoryStrings::NumOfSplitOnDemands", 0), - d_eq_splits("TheoryStrings::NumOfEqSplits", 0), - d_deq_splits("TheoryStrings::NumOfDiseqSplits", 0), - d_loop_lemmas("TheoryStrings::NumOfLoops", 0), - d_new_skolems("TheoryStrings::NumOfNewSkolems", 0) -{ - smtStatisticsRegistry()->registerStat(&d_splits); - smtStatisticsRegistry()->registerStat(&d_eq_splits); - smtStatisticsRegistry()->registerStat(&d_deq_splits); - smtStatisticsRegistry()->registerStat(&d_loop_lemmas); - smtStatisticsRegistry()->registerStat(&d_new_skolems); -} - -TheoryStrings::Statistics::~Statistics(){ - smtStatisticsRegistry()->unregisterStat(&d_splits); - smtStatisticsRegistry()->unregisterStat(&d_eq_splits); - smtStatisticsRegistry()->unregisterStat(&d_deq_splits); - smtStatisticsRegistry()->unregisterStat(&d_loop_lemmas); - smtStatisticsRegistry()->unregisterStat(&d_new_skolems); -} - }/* CVC4::theory::strings namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/strings/theory_strings.h b/src/theory/strings/theory_strings.h index 2deb09654..fe72bd8e7 100644 --- a/src/theory/strings/theory_strings.h +++ b/src/theory/strings/theory_strings.h @@ -70,8 +70,9 @@ public: bool propagate(TNode literal); void explain( TNode literal, std::vector<TNode>& assumptions ); Node explain( TNode literal ); - - + eq::EqualityEngine * getEqualityEngine() { return &d_equalityEngine; } + bool getCurrentSubstitution( int effort, std::vector< Node >& vars, std::vector< Node >& subs, std::map< Node, std::vector< Node > >& exp ); + // NotifyClass for equality engine class NotifyClass : public eq::EqualityEngineNotify { TheoryStrings& d_str; @@ -171,15 +172,17 @@ private: bool isNormalFormPair2( Node n1, Node n2 ); // loop ant NodeSet d_loop_antec; - NodeSet d_length_intro_vars; // preReg cache NodeSet d_pregistered_terms_cache; NodeSet d_registered_terms_cache; + NodeSet d_length_lemma_terms_cache; + NodeSet d_skolem_ne_reg_cache; // preprocess cache StringsPreprocess d_preproc; NodeBoolMap d_preproc_cache; // extended functions inferences cache NodeSet d_extf_infer_cache; + NodeSet d_extf_infer_cache_u; std::vector< Node > d_empty_vec; // NodeList d_ee_disequalities; @@ -188,14 +191,16 @@ private: std::map< Node, Node > d_eqc_to_const; std::map< Node, Node > d_eqc_to_const_base; std::map< Node, Node > d_eqc_to_const_exp; + Node getConstantEqc( Node eqc ); + std::map< Node, Node > d_eqc_to_len_term; std::vector< Node > d_strings_eqc; Node d_emptyString_r; class TermIndex { public: Node d_data; - std::map< Node, TermIndex > d_children; - Node add( Node n, unsigned index, TheoryStrings* t, Node er, std::vector< Node >& c ); + std::map< TNode, TermIndex > d_children; + Node add( TNode n, unsigned index, TheoryStrings* t, Node er, std::vector< Node >& c ); void clear(){ d_children.clear(); } }; std::map< Kind, TermIndex > d_term_index; @@ -205,6 +210,7 @@ private: std::map< Node, std::vector< int > > d_flat_form_index; void debugPrintFlatForms( const char * tc ); + void debugPrintNormalForms( const char * tc ); ///////////////////////////////////////////////////////////////////////////// // MODEL GENERATION ///////////////////////////////////////////////////////////////////////////// @@ -246,51 +252,107 @@ private: /** All the function terms that the theory has seen */ context::CDList<TNode> d_functionsTerms; private: + //any non-reduced extended functions exist + context::CDO< bool > d_has_extf; + // static information about extf + class ExtfInfo { + public: + //all variables in this term + std::vector< Node > d_vars; + }; + // non-static information about extf + class ExtfInfoTmp { + public: + void init(){ + d_pol = 0; + d_model_active = true; + } + // list of terms that something (does not) contain and their explanation + std::map< bool, std::vector< Node > > d_ctn; + std::map< bool, std::vector< Node > > d_ctn_from; + //polarity + int d_pol; + //explanation + std::vector< Node > d_exp; + //reps -> list of variables + //std::map< Node, std::vector< Node > > d_rep_vars; + //false if it is reduced in the model + bool d_model_active; + }; + std::map< Node, ExtfInfoTmp > d_extf_info_tmp; + //collect extended operator terms + void collectExtendedFuncTerms( Node n, std::map< Node, bool >& visited ); +private: + class InferInfo { + public: + unsigned d_i; + unsigned d_j; + bool d_rev; + std::vector< Node > d_ant; + std::vector< Node > d_antn; + std::map< int, std::vector< Node > > d_new_skolem; + Node d_conc; + unsigned d_id; + std::map< Node, bool > d_pending_phase; + unsigned d_index; + const char * getId() { + switch( d_id ){ + case 1:return "S-Split(CST-P)-prop";break; + case 2:return "S-Split(VAR)-prop";break; + case 3:return "Len-Split(Len)";break; + case 4:return "Len-Split(Emp)";break; + case 5:return "S-Split(CST-P)-binary";break; + case 6:return "S-Split(CST-P)";break; + case 7:return "S-Split(VAR)";break; + case 8:return "F-Loop";break; + default:break; + } + return ""; + } + bool sendAsLemma(); + }; //initial check void checkInit(); void checkConstantEquivalenceClasses( TermIndex* ti, std::vector< Node >& vecc ); //extended functions evaluation check - void checkExtendedFuncsEval( int effort = 0 ); - void checkExtfInference( Node n, Node nr, int effort ); - void collectVars( Node n, std::map< Node, std::vector< Node > >& vars, std::map< Node, bool >& visited ); + void checkExtfEval( int effort = 0 ); + void checkExtfInference( Node n, Node nr, ExtfInfoTmp& in, int effort ); + void collectVars( Node n, std::vector< Node >& vars, std::map< Node, bool >& visited ); Node getSymbolicDefinition( Node n, std::vector< Node >& exp ); //check extf reduction - void checkExtfReduction( int effort ); - void checkReduction( Node atom, int pol, int effort ); + void checkExtfReductions( int effort ); + bool checkExtfReduction( Node atom, int pol, int effort ); //flat forms check void checkFlatForms(); Node checkCycles( Node eqc, std::vector< Node >& curr, std::vector< Node >& exp ); //normal forms check void checkNormalForms(); - bool normalizeEquivalenceClass( Node n, std::vector< Node > & nf, std::vector< Node > & nf_exp ); - bool getNormalForms( Node &eqc, std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, - std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend); - bool detectLoop(std::vector< std::vector< Node > > &normal_forms, - int i, int j, int index, int &loop_in_i, int &loop_in_j); - bool processLoop(std::vector< Node > &antec, - std::vector< std::vector< Node > > &normal_forms, - std::vector< Node > &normal_form_src, - int i, int j, int loop_n_index, int other_n_index, - int loop_index, int index); - bool processNEqc( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, + void normalizeEquivalenceClass( Node n ); + void getNormalForms( Node &eqc, std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, + std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend ); + bool detectLoop( std::vector< std::vector< Node > > &normal_forms, int i, int j, int index, int &loop_in_i, int &loop_in_j, unsigned rproc ); + bool processLoop( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, + int i, int j, int loop_n_index, int other_n_index,int loop_index, int index, InferInfo& info ); + void processNEqc( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend ); - bool processReverseNEq( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, + void processReverseNEq( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend, - unsigned i, unsigned j ); - bool processSimpleNEq( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, + unsigned i, unsigned j, unsigned& index, unsigned rproc, std::vector< InferInfo >& pinfer ); + void processSimpleNEq( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend, - unsigned i, unsigned j, unsigned& index, bool isRev ); - bool processDeq( Node n1, Node n2 ); + unsigned i, unsigned j, unsigned& index, bool isRev, unsigned rproc, std::vector< InferInfo >& pinfer ); + void processDeq( Node n1, Node n2 ); int processReverseDeq( std::vector< Node >& nfi, std::vector< Node >& nfj, Node ni, Node nj ); int processSimpleDeq( std::vector< Node >& nfi, std::vector< Node >& nfj, Node ni, Node nj, unsigned& index, bool isRev ); void checkDeqNF(); - - void getExplanationVectorForPrefix( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, - std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend, - unsigned i, unsigned j, int index, bool isRev, std::vector< Node >& curr_exp ); + void getExplanationVectorForPrefix( std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend, + unsigned i, int index, bool isRev, std::vector< Node >& curr_exp ); + void getExplanationVectorForPrefixEq( std::vector< std::vector< Node > > &normal_forms, std::vector< Node > &normal_form_src, + std::vector< std::vector< Node > > &normal_forms_exp, std::vector< std::map< Node, std::map< bool, int > > >& normal_forms_exp_depend, + unsigned i, unsigned j, int index_i, int index_j, bool isRev, std::vector< Node >& curr_exp ); + + Node collectConstantStringAt( std::vector< Node >& vec, int& index, bool isRev ); - //check for extended functions - void checkExtendedFuncs(); //check membership constraints Node mkRegExpAntec(Node atom, Node ant); Node normalizeRegexp(Node r); @@ -322,6 +384,8 @@ public: Node expandDefinition(LogicRequest &logicRequest, Node n); /** Check at effort e */ void check(Effort e); + /** needs check last effort */ + bool needsCheckLastEffort(); /** Conflict when merging two constants */ void conflict(TNode a, TNode b); /** called when a new equivalence class is created */ @@ -364,9 +428,16 @@ protected: enum { sk_id_c_spt, sk_id_vc_spt, - sk_id_v_spt, + sk_id_vc_bin_spt, + sk_id_v_spt, + sk_id_c_spt_rev, + sk_id_vc_spt_rev, + sk_id_vc_bin_spt_rev, + sk_id_v_spt_rev, sk_id_ctn_pre, sk_id_ctn_post, + sk_id_dc_spt, + sk_id_dc_spt_rem, sk_id_deq_x, sk_id_deq_y, sk_id_deq_z, @@ -374,6 +445,7 @@ protected: std::map< Node, std::map< Node, std::map< int, Node > > > d_skolem_cache; Node mkSkolemCached( Node a, Node b, int id, const char * c, int isLenSplit = 0 ); inline Node mkSkolemS(const char * c, int isLenSplit = 0); + void registerNonEmptySkolem( Node sk ); //inline Node mkSkolemI(const char * c); /** mkExplain **/ Node mkExplain( std::vector< Node >& a ); @@ -385,34 +457,12 @@ protected: //get equivalence classes void getEquivalenceClasses( std::vector< Node >& eqcs ); - //get final normal form - void getFinalNormalForm( Node n, std::vector< Node >& nf, std::vector< Node >& exp ); //separate into collections with equal length void separateByLength( std::vector< Node >& n, std::vector< std::vector< Node > >& col, std::vector< Node >& lts ); void printConcat( std::vector< Node >& n, const char * c ); void inferSubstitutionProxyVars( Node n, std::vector< Node >& vars, std::vector< Node >& subs, std::vector< Node >& unproc ); -private: - - // Special String Functions - NodeSet d_neg_ctn_eqlen; - NodeSet d_neg_ctn_ulen; - NodeSet d_neg_ctn_cached; - //extended string terms and whether they have been reduced - NodeBoolMap d_ext_func_terms; - std::map< Node, std::map< Node, std::vector< Node > > > d_extf_vars; - // list of terms that something (does not) contain and their explanation - class ExtfInfo { - public: - std::map< bool, std::vector< Node > > d_ctn; - std::map< bool, std::vector< Node > > d_ctn_from; - }; - std::map< Node, int > d_extf_pol; - std::map< Node, std::vector< Node > > d_extf_exp; - std::map< Node, ExtfInfo > d_extf_info; - //collect extended operator terms - void collectExtendedFuncTerms( Node n, std::map< Node, bool >& visited ); // Symbolic Regular Expression private: @@ -444,7 +494,6 @@ private: CVC4::String getHeadConst( Node x ); bool deriveRegExp( Node x, Node r, Node ant ); - bool addMembershipLength(Node atom); void addMembership(Node assertion); Node getNormalString(Node x, std::vector<Node> &nf_exp); Node getNormalSymRegExp(Node r, std::vector<Node> &nf_exp); @@ -459,7 +508,8 @@ private: public: //for finite model finding Node getNextDecisionRequest(); - + //ppRewrite + Node ppRewrite(TNode atom); public: /** statistics class */ class Statistics { diff --git a/src/theory/strings/theory_strings_preprocess.cpp b/src/theory/strings/theory_strings_preprocess.cpp index ba811644a..a697dad75 100644 --- a/src/theory/strings/theory_strings_preprocess.cpp +++ b/src/theory/strings/theory_strings_preprocess.cpp @@ -28,89 +28,58 @@ namespace CVC4 { namespace theory { namespace strings { -StringsPreprocess::StringsPreprocess( context::UserContext* u ) : d_cache( u ){ +StringsPreprocess::StringsPreprocess( context::UserContext* u ){ //Constants d_zero = NodeManager::currentNM()->mkConst( ::CVC4::Rational(0) ); + d_one = NodeManager::currentNM()->mkConst( ::CVC4::Rational(1) ); } StringsPreprocess::~StringsPreprocess(){ } -/* -int StringsPreprocess::checkFixLenVar( Node t ) { - int ret = 2; - if(t.getKind() == kind::EQUAL) { - if(t[0].getType().isInteger() && t[0].isConst() && t[1].getKind() == kind::STRING_LENGTH) { - if(t[1][0].getKind() == kind::VARIABLE) { - ret = 0; - } - } else if(t[1].getType().isInteger() && t[1].isConst() && t[0].getKind() == kind::STRING_LENGTH) { - if(t[0][0].getKind() == kind::VARIABLE) { - ret = 1; - } - } - } - if(ret != 2) { - unsigned len = t[ret].getConst<Rational>().getNumerator().toUnsignedInt(); - if(len < 2) { - ret = 2; +Node StringsPreprocess::getUfForNode( Kind k, Node n, unsigned id ) { + std::map< unsigned, Node >::iterator it = d_uf[k].find( id ); + if( it==d_uf[k].end() ){ + std::vector< TypeNode > types; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + types.push_back( n[i].getType() ); } + TypeNode typ = NodeManager::currentNM()->mkFunctionType( types, n.getType() ); + Node f = NodeManager::currentNM()->mkSkolem( "sop", typ, "op created for string op" ); + d_uf[k][id] = f; + return f; + }else{ + return it->second; } - if(!options::stringExp()) { - ret = 2; - } - return ret; } -*/ -Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { - NodeNodeMap::const_iterator i = d_cache.find(t); - if(i != d_cache.end()) { - return (*i).second.isNull() ? t : (*i).second; + +//pro: congruence possible, con: introduces UF/requires theory combination +// currently hurts performance +//TODO: for all skolems below +Node StringsPreprocess::getUfAppForNode( Kind k, Node n, unsigned id ) { + std::vector< Node > children; + children.push_back( getUfForNode( k, n, id ) ); + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + children.push_back( n[i] ); } + return NodeManager::currentNM()->mkNode( kind::APPLY_UF, children ); +} - Trace("strings-preprocess") << "StringsPreprocess::simplify: " << t << std::endl; +//returns an n such that t can be replaced by n, under the assumption of lemmas in new_nodes + +Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { + unsigned prev_new_nodes = new_nodes.size(); + Trace("strings-preprocess-debug") << "StringsPreprocess::simplify: " << t << std::endl; Node retNode = t; - /*int c_id = checkFixLenVar(t); - if( c_id != 2 ) { - int v_id = 1 - c_id; - int len = t[c_id].getConst<Rational>().getNumerator().toUnsignedInt(); - if(len > 1) { - Node one = NodeManager::currentNM()->mkConst( ::CVC4::Rational(1) ); - std::vector< Node > vec; - for(int i=0; i<len; i++) { - Node num = NodeManager::currentNM()->mkConst( ::CVC4::Rational(i) ); - //Node sk = NodeManager::currentNM()->mkNode(kind::STRING_CHARAT, t[v_id][0], num); - Node sk = NodeManager::currentNM()->mkNode(kind::APPLY_UF, d_ufSubstr, t[v_id][0], num, one); - vec.push_back(sk); - Node cc = one.eqNode(NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk )); - new_nodes.push_back( cc ); - } - Node lem = t[v_id][0].eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, vec ) ); - lem = NodeManager::currentNM()->mkNode( kind::IMPLIES, t, lem ); - new_nodes.push_back( lem ); - d_cache[t] = t; - retNode = t; - } - } else */ if( t.getKind() == kind::STRING_SUBSTR ) { - /* - Node lenxgti = NodeManager::currentNM()->mkNode( kind::GEQ, - NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, t[0] ), - NodeManager::currentNM()->mkNode( kind::PLUS, t[1], t[2] ) ); - Node t1geq0 = NodeManager::currentNM()->mkNode(kind::GEQ, t[1], d_zero); - Node t2geq0 = NodeManager::currentNM()->mkNode(kind::GT, t[2], d_zero); - Node cond = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::AND, lenxgti, t1geq0, t2geq0 )); - Node sk1 = NodeManager::currentNM()->mkSkolem( "ss1", NodeManager::currentNM()->stringType(), "created for charat/substr" ); - Node sk3 = NodeManager::currentNM()->mkSkolem( "ss3", NodeManager::currentNM()->stringType(), "created for charat/substr" ); - Node x_eq_123 = t[0].eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, t, sk3 ) ); - Node len_sk1_eq_i = t[1].eqNode( NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk1 ) ); - Node lenc = t[2].eqNode( NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, t ) ); - Node lemma = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::ITE, cond, - NodeManager::currentNM()->mkNode( kind::AND, x_eq_123, len_sk1_eq_i, lenc ), - t.eqNode(NodeManager::currentNM()->mkConst( ::CVC4::String("") )) )); - */ + Node skt; + if( options::stringUfReduct() ){ + skt = getUfAppForNode( kind::STRING_SUBSTR, t ); + }else{ + skt = NodeManager::currentNM()->mkSkolem( "sst", NodeManager::currentNM()->stringType(), "created for substr" ); + } Node t12 = NodeManager::currentNM()->mkNode( kind::PLUS, t[1], t[2] ); Node lt0 = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, t[0] ); //start point is greater than or equal zero @@ -120,10 +89,10 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { //length is positive Node c3 = NodeManager::currentNM()->mkNode( kind::GT, t[2], d_zero ); Node cond = NodeManager::currentNM()->mkNode( kind::AND, c1, c2, c3 ); - + Node sk1 = NodeManager::currentNM()->mkSkolem( "ss1", NodeManager::currentNM()->stringType(), "created for substr" ); Node sk2 = NodeManager::currentNM()->mkSkolem( "ss2", NodeManager::currentNM()->stringType(), "created for substr" ); - Node b11 = t[0].eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, t, sk2 ) ); + Node b11 = t[0].eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, skt, sk2 ) ); //length of first skolem is second argument Node b12 = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk1 ).eqNode( t[1] ); //length of second skolem is abs difference between end point and end of string @@ -132,34 +101,38 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { NodeManager::currentNM()->mkNode( kind::MINUS, lt0, t12 ), d_zero ) ); Node b1 = NodeManager::currentNM()->mkNode( kind::AND, b11, b12, b13 ); - Node b2 = t.eqNode( NodeManager::currentNM()->mkConst( ::CVC4::String("") ) ); - - Node lemma = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::ITE, cond, b1, b2 ) ); + Node b2 = skt.eqNode( NodeManager::currentNM()->mkConst( ::CVC4::String("") ) ); + Node lemma = NodeManager::currentNM()->mkNode( kind::ITE, cond, b1, b2 ); new_nodes.push_back( lemma ); - d_cache[t] = t; + retNode = skt; } else if( t.getKind() == kind::STRING_STRIDOF ) { Node sk2 = NodeManager::currentNM()->mkSkolem( "io2", NodeManager::currentNM()->stringType(), "created for indexof" ); Node sk3 = NodeManager::currentNM()->mkSkolem( "io3", NodeManager::currentNM()->stringType(), "created for indexof" ); Node sk4 = NodeManager::currentNM()->mkSkolem( "io4", NodeManager::currentNM()->stringType(), "created for indexof" ); - Node skk = NodeManager::currentNM()->mkSkolem( "iok", NodeManager::currentNM()->integerType(), "created for indexof" ); + Node skk; + if( options::stringUfReduct() ){ + skk = getUfAppForNode( kind::STRING_STRIDOF, t ); + }else{ + skk = NodeManager::currentNM()->mkSkolem( "iok", NodeManager::currentNM()->integerType(), "created for indexof" ); + } Node st = NodeManager::currentNM()->mkNode( kind::STRING_SUBSTR, t[0], t[2], NodeManager::currentNM()->mkNode( kind::MINUS, NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, t[0] ), t[2] ) ); Node eq = st.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk2, sk3, sk4 ) ); new_nodes.push_back( eq ); Node negone = NodeManager::currentNM()->mkConst( ::CVC4::Rational(-1) ); Node krange = NodeManager::currentNM()->mkNode( kind::GEQ, skk, negone ); new_nodes.push_back( krange ); - krange = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::GT, NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, t[0] ), skk) ); + krange = NodeManager::currentNM()->mkNode( kind::GT, NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, t[0] ), skk); new_nodes.push_back( krange ); - krange = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::GT, NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, t[1] ), d_zero) ); + krange = NodeManager::currentNM()->mkNode( kind::GT, NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, t[1] ), d_zero); new_nodes.push_back( krange ); - Node start_valid = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::GEQ, t[2], d_zero) ); + Node start_valid = NodeManager::currentNM()->mkNode( kind::GEQ, t[2], d_zero); //str.len(s1) < y + str.len(s2) - Node c1 = Rewriter::rewrite(NodeManager::currentNM()->mkNode( kind::GT, - NodeManager::currentNM()->mkNode( kind::PLUS, t[2], NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, t[1] )), - NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, t[0] ))); + Node c1 = NodeManager::currentNM()->mkNode( kind::GT, + NodeManager::currentNM()->mkNode( kind::PLUS, t[2], NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, t[1] )), + NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, t[0] )); //~contain(t234, s2) - Node c3 = Rewriter::rewrite(NodeManager::currentNM()->mkNode( kind::STRING_STRCTN, st, t[1] ).negate()); + Node c3 = NodeManager::currentNM()->mkNode( kind::STRING_STRCTN, st, t[1] ).negate(); //left Node left = NodeManager::currentNM()->mkNode( kind::OR, c1, c3, start_valid.negate() ); //t3 = s2 @@ -176,23 +149,22 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Node c6 = skk.eqNode( NodeManager::currentNM()->mkNode( kind::PLUS, t[2], NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk2 )) ); //right - Node right = Rewriter::rewrite(NodeManager::currentNM()->mkNode( kind::AND, c4, c5, c6, start_valid )); + Node right = NodeManager::currentNM()->mkNode( kind::AND, c4, c5, c6, start_valid ); Node cond = skk.eqNode( negone ); Node rr = NodeManager::currentNM()->mkNode( kind::ITE, cond, left, right ); new_nodes.push_back( rr ); - if( options::stringLazyPreproc() ){ - new_nodes.push_back( t.eqNode( skk ) ); - d_cache[t] = Node::null(); - }else{ - d_cache[t] = skk; - retNode = skk; - } + retNode = skk; } else if( t.getKind() == kind::STRING_ITOS || t.getKind() == kind::STRING_U16TOS || t.getKind() == kind::STRING_U32TOS ) { //Node num = Rewriter::rewrite(NodeManager::currentNM()->mkNode(kind::ITE, // NodeManager::currentNM()->mkNode(kind::GEQ, t[0], d_zero), // t[0], NodeManager::currentNM()->mkNode(kind::UMINUS, t[0]))); Node num = t[0]; - Node pret = NodeManager::currentNM()->mkNode(kind::STRING_ITOS, num); + Node pret; + if( options::stringUfReduct() ){ + pret = NodeManager::currentNM()->mkNode(kind::STRING_ITOS, num); + }else{ + pret = NodeManager::currentNM()->mkSkolem( "itost", NodeManager::currentNM()->stringType(), "created for itos" ); + } Node lenp = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, pret); Node nonneg = NodeManager::currentNM()->mkNode(kind::GEQ, t[0], d_zero); @@ -214,8 +186,8 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { //non-neg Node b1 = NodeManager::currentNM()->mkBoundVar(NodeManager::currentNM()->integerType()); Node b1v = NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, b1); - Node g1 = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::AND, NodeManager::currentNM()->mkNode( kind::GEQ, b1, d_zero ), - NodeManager::currentNM()->mkNode( kind::GT, lenp, b1 ) ) ); + Node g1 = NodeManager::currentNM()->mkNode( kind::AND, NodeManager::currentNM()->mkNode( kind::GEQ, b1, d_zero ), + NodeManager::currentNM()->mkNode( kind::GT, lenp, b1 ) ); Node one = NodeManager::currentNM()->mkConst( ::CVC4::Rational(1) ); Node nine = NodeManager::currentNM()->mkConst( ::CVC4::Rational(9) ); Node ten = NodeManager::currentNM()->mkConst( ::CVC4::Rational(10) ); @@ -284,7 +256,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { svec.push_back(cc1);svec.push_back(cc2); svec.push_back(cc21); svec.push_back(cc3);svec.push_back(cc4);svec.push_back(cc5); - Node conc = Rewriter::rewrite( NodeManager::currentNM()->mkNode(kind::AND, svec) ); + Node conc = NodeManager::currentNM()->mkNode(kind::AND, svec); conc = NodeManager::currentNM()->mkNode( kind::IMPLIES, g1, conc ); conc = NodeManager::currentNM()->mkNode( kind::FORALL, b1v, conc ); conc = NodeManager::currentNM()->mkNode( kind::IMPLIES, nonneg, conc ); @@ -295,20 +267,17 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { t.eqNode(NodeManager::currentNM()->mkNode(kind::STRING_CONCAT, NodeManager::currentNM()->mkConst(::CVC4::String("-")), pret)))); new_nodes.push_back( conc );*/ - if( options::stringLazyPreproc() && t!=pret ){ - new_nodes.push_back( t.eqNode( pret ) ); - d_cache[t] = Node::null(); - }else{ - d_cache[t] = pret; - retNode = pret; - } - //don't rewrite processed - if(t != pret) { - d_cache[pret] = pret; - } + retNode = pret; } else if( t.getKind() == kind::STRING_STOI || t.getKind() == kind::STRING_STOU16 || t.getKind() == kind::STRING_STOU32 ) { Node str = t[0]; - Node pret = NodeManager::currentNM()->mkNode(kind::STRING_STOI, str); + Node pret; + if( options::stringUfReduct() ){ + pret = getUfAppForNode( kind::STRING_STOI, t ); + }else{ + pret = NodeManager::currentNM()->mkSkolem( "stoit", NodeManager::currentNM()->integerType(), "created for stoi" ); + } + //Node pret = NodeManager::currentNM()->mkNode(kind::STRING_STOI, str); + //Node pret = getUfAppForNode( kind::STRING_STOI, t ); Node lenp = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, str); Node negone = NodeManager::currentNM()->mkConst( ::CVC4::Rational(-1) ); @@ -363,7 +332,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { g = z2.eqNode( NodeManager::currentNM()->mkConst(::CVC4::String(stmp)) ).negate(); vec_n.push_back(g); } - Node cc2 = Rewriter::rewrite(NodeManager::currentNM()->mkNode(kind::AND, vec_n)); + Node cc2 = NodeManager::currentNM()->mkNode(kind::AND, vec_n); //cc3 Node b2 = NodeManager::currentNM()->mkBoundVar(NodeManager::currentNM()->integerType()); Node b2v = NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, b2); @@ -397,7 +366,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { ufMx))); vec_c3b.push_back(c3cc); c3cc = NodeManager::currentNM()->mkNode(kind::AND, vec_c3b); - c3cc = Rewriter::rewrite( NodeManager::currentNM()->mkNode(kind::IMPLIES, g2, c3cc) ); + c3cc = NodeManager::currentNM()->mkNode(kind::IMPLIES, g2, c3cc); c3cc = NodeManager::currentNM()->mkNode(kind::FORALL, b2v, c3cc); vec_c3.push_back(c3cc); //unbound @@ -411,16 +380,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Node conc = NodeManager::currentNM()->mkNode(kind::ITE, pret.eqNode(negone), NodeManager::currentNM()->mkNode(kind::OR, cc1, cc2), cc3); new_nodes.push_back( conc ); - if( options::stringLazyPreproc() && t!=pret ){ - new_nodes.push_back( t.eqNode( pret ) ); - d_cache[t] = Node::null(); - }else{ - d_cache[t] = pret; - retNode = pret; - } - if(t != pret) { - d_cache[pret] = pret; - } + retNode = pret; } else if( t.getKind() == kind::STRING_STRREPL ) { Node x = t[0]; Node y = t[1]; @@ -429,6 +389,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Node sk2 = NodeManager::currentNM()->mkSkolem( "rp2", t[0].getType(), "created for replace" ); Node skw = NodeManager::currentNM()->mkSkolem( "rpw", t[0].getType(), "created for replace" ); Node cond = NodeManager::currentNM()->mkNode( kind::STRING_STRCTN, x, y ); + cond = NodeManager::currentNM()->mkNode( kind::AND, cond, NodeManager::currentNM()->mkNode(kind::GT, NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, y), d_zero) ); Node c1 = x.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, y, sk2 ) ); Node c2 = skw.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, z, sk2 ) ); Node c3 = NodeManager::currentNM()->mkNode(kind::STRING_STRCTN, @@ -437,98 +398,107 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { NodeManager::currentNM()->mkNode(kind::MINUS, NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, y), NodeManager::currentNM()->mkConst(::CVC4::Rational(1))))), y).negate(); - Node rr = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::ITE, cond, - NodeManager::currentNM()->mkNode( kind::AND, c1, c2, c3), - skw.eqNode(x) ) ); - new_nodes.push_back( rr ); - rr = Rewriter::rewrite( NodeManager::currentNM()->mkNode(kind::GT, NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, y), d_zero) ); + Node rr = NodeManager::currentNM()->mkNode( kind::ITE, cond, + NodeManager::currentNM()->mkNode( kind::AND, c1, c2, c3), + skw.eqNode(x) ); new_nodes.push_back( rr ); - if( options::stringLazyPreproc() ){ - new_nodes.push_back( t.eqNode( skw ) ); - d_cache[t] = Node::null(); - }else{ - d_cache[t] = skw; - retNode = skw; - } - } else{ - d_cache[t] = Node::null(); + retNode = skw; + } else if( t.getKind() == kind::STRING_STRCTN ){ + Node x = t[0]; + Node s = t[1]; + //negative contains reduces to existential + Node lenx = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, x); + Node lens = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, s); + Node b1 = NodeManager::currentNM()->mkBoundVar(NodeManager::currentNM()->integerType()); + Node b1v = NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, b1); + Node body = NodeManager::currentNM()->mkNode( kind::AND, + NodeManager::currentNM()->mkNode( kind::LEQ, d_zero, b1 ), + NodeManager::currentNM()->mkNode( kind::LEQ, b1, NodeManager::currentNM()->mkNode( kind::MINUS, lenx, lens ) ), + NodeManager::currentNM()->mkNode( kind::EQUAL, NodeManager::currentNM()->mkNode(kind::STRING_SUBSTR, x, b1, lens), s ) + ); + retNode = NodeManager::currentNM()->mkNode( kind::EXISTS, b1v, body ); } - /*if( t.getNumChildren()>0 ) { - std::vector< Node > cc; - if (t.getMetaKind() == kind::metakind::PARAMETERIZED) { - cc.push_back(t.getOperator()); - } - bool changed = false; - for( unsigned i=0; i<t.getNumChildren(); i++ ){ - Node tn = simplify( t[i], new_nodes ); - cc.push_back( tn ); - changed = changed || tn!=t[i]; - } - if(changed) { - Node n = NodeManager::currentNM()->mkNode( t.getKind(), cc ); - d_cache[t] = n; - retNode = n; - } else { - d_cache[t] = Node::null(); - retNode = t; - } - }*/ if( t!=retNode ){ - Trace("strings-preprocess-debug") << "StringsPreprocess::simplify: " << t << " -> " << retNode << std::endl; + Trace("strings-preprocess") << "StringsPreprocess::simplify: " << t << " -> " << retNode << std::endl; if(!new_nodes.empty()) { - Trace("strings-preprocess-debug") << " ... new nodes (" << new_nodes.size() << "):\n"; - for(unsigned int i=0; i<new_nodes.size(); ++i) { - Trace("strings-preprocess-debug") << "\t" << new_nodes[i] << "\n"; + Trace("strings-preprocess") << " ... new nodes (" << (new_nodes.size()-prev_new_nodes) << "):" << std::endl; + for(unsigned int i=prev_new_nodes; i<new_nodes.size(); ++i) { + Trace("strings-preprocess") << " " << new_nodes[i] << std::endl; } } } return retNode; } -Node StringsPreprocess::decompose(Node t, std::vector< Node > & new_nodes) { - NodeNodeMap::const_iterator i = d_cache.find(t); - if(i != d_cache.end()) { - return (*i).second.isNull() ? t : (*i).second; - } - - unsigned num = t.getNumChildren(); - if(num == 0) { - return simplify(t, new_nodes); +Node StringsPreprocess::simplifyRec( Node t, std::vector< Node > & new_nodes, std::map< Node, Node >& visited ){ + std::map< Node, Node >::iterator it = visited.find(t); + if( it!=visited.end() ){ + return it->second; }else{ - bool changed = false; - std::vector< Node > cc; - if (t.getMetaKind() == kind::metakind::PARAMETERIZED) { - cc.push_back(t.getOperator()); - } - for(unsigned i=0; i<t.getNumChildren(); i++) { - Node s = decompose(t[i], new_nodes); - cc.push_back( s ); - if(s != t[i]) { - changed = true; + Node retNode; + if( t.getNumChildren()==0 ){ + retNode = simplify( t, new_nodes ); + }else if( t.getKind()!=kind::FORALL ){ + bool changed = false; + std::vector< Node > cc; + if( t.getMetaKind() == kind::metakind::PARAMETERIZED ){ + cc.push_back( t.getOperator() ); } + for(unsigned i=0; i<t.getNumChildren(); i++) { + Node s = simplifyRec( t[i], new_nodes, visited ); + cc.push_back( s ); + if( s!=t[i] ) { + changed = true; + } + } + Node tmp = t; + if( changed ){ + tmp = NodeManager::currentNM()->mkNode( t.getKind(), cc ); + } + retNode = simplify( tmp, new_nodes ); } - if(changed) { - Node tmp = NodeManager::currentNM()->mkNode( t.getKind(), cc ); - return simplify(tmp, new_nodes); - } else { - return simplify(t, new_nodes); - } + visited[t] = retNode; + return retNode; + } +} + +Node StringsPreprocess::processAssertion( Node n, std::vector< Node > &new_nodes ) { + std::map< Node, Node > visited; + std::vector< Node > new_nodes_curr; + Node ret = simplifyRec( n, new_nodes_curr, visited ); + while( !new_nodes_curr.empty() ){ + Node curr = new_nodes_curr.back(); + new_nodes_curr.pop_back(); + std::vector< Node > new_nodes_tmp; + curr = simplifyRec( curr, new_nodes_tmp, visited ); + new_nodes_curr.insert( new_nodes_curr.end(), new_nodes_tmp.begin(), new_nodes_tmp.end() ); + new_nodes.push_back( curr ); } + return ret; } -void StringsPreprocess::simplify(std::vector< Node > &vec_node) { +void StringsPreprocess::processAssertions( std::vector< Node > &vec_node ){ + std::map< Node, Node > visited; for( unsigned i=0; i<vec_node.size(); i++ ){ + Trace("strings-preprocess-debug") << "Preprocessing assertion " << vec_node[i] << std::endl; + //preprocess until fixed point std::vector< Node > new_nodes; - Node curr = decompose( vec_node[i], new_nodes ); - if( !new_nodes.empty() ){ - new_nodes.insert( new_nodes.begin(), curr ); - curr = NodeManager::currentNM()->mkNode( kind::AND, new_nodes ); + std::vector< Node > new_nodes_curr; + new_nodes_curr.push_back( vec_node[i] ); + while( !new_nodes_curr.empty() ){ + Node curr = new_nodes_curr.back(); + new_nodes_curr.pop_back(); + std::vector< Node > new_nodes_tmp; + curr = simplifyRec( curr, new_nodes_tmp, visited ); + new_nodes_curr.insert( new_nodes_curr.end(), new_nodes_tmp.begin(), new_nodes_tmp.end() ); + new_nodes.push_back( curr ); } - if( curr!=vec_node[i] ){ - curr = Rewriter::rewrite( curr ); - PROOF( ProofManager::currentPM()->addDependence(curr, vec_node[i]); ); - vec_node[i] = curr; + Node res = new_nodes.size()==1 ? new_nodes[0] : NodeManager::currentNM()->mkNode( kind::AND, new_nodes ); + if( res!=vec_node[i] ){ + res = Rewriter::rewrite( res ); + PROOF( ProofManager::currentPM()->addDependence( res, vec_node[i] ); ); + vec_node[i] = res; } } } diff --git a/src/theory/strings/theory_strings_preprocess.h b/src/theory/strings/theory_strings_preprocess.h index abc7b5a91..faaeb53c3 100644 --- a/src/theory/strings/theory_strings_preprocess.h +++ b/src/theory/strings/theory_strings_preprocess.h @@ -31,19 +31,25 @@ namespace theory { namespace strings { class StringsPreprocess { - typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; - NodeNodeMap d_cache; //Constants Node d_zero; -private: - //int checkFixLenVar( Node t ); - Node simplify( Node t, std::vector< Node > &new_nodes ); + Node d_one; + //mapping from kinds to UF + std::map< Kind, std::map< unsigned, Node > > d_uf; + //get UF for node + Node getUfForNode( Kind k, Node n, unsigned id = 0 ); + Node getUfAppForNode( Kind k, Node n, unsigned id = 0 ); + //recursive simplify + Node simplifyRec( Node t, std::vector< Node > &new_nodes, std::map< Node, Node >& visited ); public: StringsPreprocess( context::UserContext* u ); ~StringsPreprocess(); - - Node decompose( Node t, std::vector< Node > &new_nodes ); - void simplify(std::vector< Node > &vec_node); + //returns a node that is equivalent to t under assumptions in new_nodes + Node simplify( Node t, std::vector< Node > &new_nodes ); + //process assertion: guarentees to remove all extf + Node processAssertion( Node n, std::vector< Node > &new_nodes ); + //proces assertions: guarentees to remove all extf, rewrite in place + void processAssertions( std::vector< Node > &vec_node ); }; }/* CVC4::theory::strings namespace */ diff --git a/src/theory/strings/theory_strings_rewriter.cpp b/src/theory/strings/theory_strings_rewriter.cpp index 75243b84d..92b18eed0 100644 --- a/src/theory/strings/theory_strings_rewriter.cpp +++ b/src/theory/strings/theory_strings_rewriter.cpp @@ -1026,22 +1026,22 @@ RewriteResponse TheoryStringsRewriter::postRewrite(TNode node) { retNode = NodeManager::currentNM()->mkNode(kind::EQUAL, leftNode, rightNode); } } else if(node.getKind() == kind::STRING_LENGTH) { - if(node[0].isConst()) { + if( node[0].isConst() ){ retNode = NodeManager::currentNM()->mkConst( ::CVC4::Rational( node[0].getConst<String>().size() ) ); - } else if(node[0].getKind() == kind::STRING_CONCAT) { + }else if( node[0].getKind() == kind::STRING_CONCAT ){ Node tmpNode = rewriteConcatString(node[0]); if(tmpNode.isConst()) { retNode = NodeManager::currentNM()->mkConst( ::CVC4::Rational( tmpNode.getConst<String>().size() ) ); - } else if(tmpNode.getKind() == kind::STRING_SUBSTR) { + //} else if(tmpNode.getKind() == kind::STRING_SUBSTR) { //retNode = tmpNode[2]; - } else { + }else if( tmpNode.getKind()==kind::STRING_CONCAT ){ // it has to be string concat std::vector<Node> node_vec; for(unsigned int i=0; i<tmpNode.getNumChildren(); ++i) { if(tmpNode[i].isConst()) { node_vec.push_back( NodeManager::currentNM()->mkConst( ::CVC4::Rational( tmpNode[i].getConst<String>().size() ) ) ); - } else if(tmpNode[i].getKind() == kind::STRING_SUBSTR) { - node_vec.push_back( tmpNode[i][2] ); + //} else if(tmpNode[i].getKind() == kind::STRING_SUBSTR) { + // node_vec.push_back( tmpNode[i][2] ); } else { node_vec.push_back( NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, tmpNode[i]) ); } @@ -1055,8 +1055,6 @@ RewriteResponse TheoryStringsRewriter::postRewrite(TNode node) { } } } - //else if(node[0].getKind() == kind::STRING_SUBSTR) { - //retNode = node[0][2]; }else if( node.getKind() == kind::STRING_CHARAT ){ Node one = NodeManager::currentNM()->mkConst( Rational( 1 ) ); retNode = NodeManager::currentNM()->mkNode(kind::STRING_SUBSTR, node[0], node[1], one); @@ -1502,22 +1500,12 @@ Node TheoryStringsRewriter::rewriteContains( Node node ) { } } }else if( node[0].isConst() ){ - CVC4::String t = node[0].getConst<String>(); - if( t.size()==0 ){ + if( node[0].getConst<String>().size()==0 ){ return NodeManager::currentNM()->mkNode( kind::EQUAL, node[0], node[1] ); }else if( node[1].getKind()==kind::STRING_CONCAT ){ - //must find constant components in order - size_t pos = 0; - for(unsigned i=0; i<node[1].getNumChildren(); i++) { - if( node[1][i].isConst() ){ - CVC4::String s = node[1][i].getConst<String>(); - size_t new_pos = t.find(s,pos); - if( new_pos==std::string::npos ) { - return NodeManager::currentNM()->mkConst( false ); - }else{ - pos = new_pos + s.size(); - } - } + int firstc, lastc; + if( !canConstantContainConcat( node[0], node[1], firstc, lastc ) ){ + return NodeManager::currentNM()->mkConst( false ); } } } @@ -1710,3 +1698,83 @@ Node TheoryStringsRewriter::splitConstant( Node a, Node b, int& index, bool isRe return Node::null(); } } + +bool TheoryStringsRewriter::canConstantContainConcat( Node c, Node n, int& firstc, int& lastc ) { + Assert( c.isConst() ); + CVC4::String t = c.getConst<String>(); + Assert( n.getKind()==kind::STRING_CONCAT ); + //must find constant components in order + size_t pos = 0; + firstc = -1; + lastc = -1; + for(unsigned i=0; i<n.getNumChildren(); i++) { + if( n[i].isConst() ){ + firstc = firstc==-1 ? i : firstc; + lastc = i; + CVC4::String s = n[i].getConst<String>(); + size_t new_pos = t.find(s,pos); + if( new_pos==std::string::npos ) { + return false; + }else{ + pos = new_pos + s.size(); + } + } + } + return true; +} + +bool TheoryStringsRewriter::canConstantContainList( Node c, std::vector< Node >& l, int& firstc, int& lastc ) { + Assert( c.isConst() ); + CVC4::String t = c.getConst<String>(); + //must find constant components in order + size_t pos = 0; + firstc = -1; + lastc = -1; + for(unsigned i=0; i<l.size(); i++) { + if( l[i].isConst() ){ + firstc = firstc==-1 ? i : firstc; + lastc = i; + CVC4::String s = l[i].getConst<String>(); + size_t new_pos = t.find(s,pos); + if( new_pos==std::string::npos ) { + return false; + }else{ + pos = new_pos + s.size(); + } + } + } + return true; +} + +Node TheoryStringsRewriter::getNextConstantAt( std::vector< Node >& vec, unsigned& start_index, unsigned& end_index, bool isRev ) { + while( vec.size()>start_index && !vec[ start_index ].isConst() ){ + //return Node::null(); + start_index++; + } + if( start_index<vec.size() ){ + end_index = start_index; + return collectConstantStringAt( vec, end_index, isRev ); + }else{ + return Node::null(); + } +} + +Node TheoryStringsRewriter::collectConstantStringAt( std::vector< Node >& vec, unsigned& end_index, bool isRev ) { + std::vector< Node > c; + while( vec.size()>end_index && vec[ end_index ].isConst() ){ + c.push_back( vec[ end_index ] ); + end_index++; + //break; + } + if( !c.empty() ){ + if( isRev ){ + std::reverse( c.begin(), c.end() ); + } + Node cc = Rewriter::rewrite( mkConcat( kind::STRING_CONCAT, c ) ); + Assert( cc.isConst() ); + return cc; + }else{ + return Node::null(); + } +} + diff --git a/src/theory/strings/theory_strings_rewriter.h b/src/theory/strings/theory_strings_rewriter.h index 59588eda2..e166bfaeb 100644 --- a/src/theory/strings/theory_strings_rewriter.h +++ b/src/theory/strings/theory_strings_rewriter.h @@ -61,6 +61,12 @@ public: static void getConcat( Node n, std::vector< Node >& c ); static Node mkConcat( Kind k, std::vector< Node >& c ); static Node splitConstant( Node a, Node b, int& index, bool isRev ); + /** return true if constant c can contain the concat n/list l in order + firstc/lastc store which indices were used */ + static bool canConstantContainConcat( Node c, Node n, int& firstc, int& lastc ); + static bool canConstantContainList( Node c, std::vector< Node >& l, int& firstc, int& lastc ); + static Node getNextConstantAt( std::vector< Node >& vec, unsigned& start_index, unsigned& end_index, bool isRev ); + static Node collectConstantStringAt( std::vector< Node >& vec, unsigned& end_index, bool isRev ); };/* class TheoryStringsRewriter */ }/* CVC4::theory::strings namespace */ diff --git a/src/theory/theory.cpp b/src/theory/theory.cpp index 7e2b6df55..7fa74df6a 100644 --- a/src/theory/theory.cpp +++ b/src/theory/theory.cpp @@ -66,6 +66,7 @@ Theory::Theory(TheoryId id, context::Context* satContext, , d_sharedTermsIndex(satContext, 0) , d_careGraph(NULL) , d_quantEngine(NULL) + , d_extt(NULL) , d_checkTime(getFullInstanceName() + "::checkTime") , d_computeCareGraphTime(getFullInstanceName() + "::computeCareGraphTime") , d_sharedTerms(satContext) @@ -311,5 +312,145 @@ TheoryId EntailmentCheckSideEffects::getTheoryId() const { EntailmentCheckSideEffects::~EntailmentCheckSideEffects() { } + +ExtTheory::ExtTheory( Theory * p ) : d_parent( p ), +d_ext_func_terms( p->getSatContext() ), d_has_extf( p->getSatContext() ){ + +} + +void ExtTheory::collectVars( Node n, std::vector< Node >& vars, std::map< Node, bool >& visited ) { + if( !n.isConst() ){ + if( visited.find( n )==visited.end() ){ + visited[n] = true; + if( n.getNumChildren()>0 ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + collectVars( n[i], vars, visited ); + } + }else{ + vars.push_back( n ); + } + } + } +} + +//do inferences +void ExtTheory::getInferences( int effort, std::vector< Node >& terms, std::vector< Node >& sterms, std::vector< std::vector< Node > >& exp ) { + //all variables we need to find a substitution for + std::vector< Node > vars; + std::vector< Node > sub; + std::map< Node, std::vector< Node > > expc; + Trace("extt-debug") << "Checking " << d_ext_func_terms.size() << " extended functions." << std::endl; + for( NodeBoolMap::iterator it = d_ext_func_terms.begin(); it != d_ext_func_terms.end(); ++it ){ + //if not already reduced + if( (*it).second ){ + Node n = (*it).first; + terms.push_back( n ); + std::map< Node, ExtfInfo >::iterator iti = d_extf_info.find( n ); + Assert( iti!=d_extf_info.end() ); + for( unsigned i=0; i<iti->second.d_vars.size(); i++ ){ + if( std::find( vars.begin(), vars.end(), iti->second.d_vars[i] )==vars.end() ){ + vars.push_back( iti->second.d_vars[i] ); + } + } + } + } + Trace("extt-debug") << "..." << terms.size() << " unreduced." << std::endl; + if( !terms.empty() ){ + //get the current substitution + if( d_parent->getCurrentSubstitution( effort, vars, sub, expc ) ){ + for( unsigned i=0; i<terms.size(); i++ ){ + //do substitution, rewrite + Node n = terms[i]; + Node ns = n.substitute( vars.begin(), vars.end(), sub.begin(), sub.end() ); + std::vector< Node > expn; + if( ns!=n ){ + //build explanation: explanation vars = sub for each vars in FV( n ) + std::map< Node, ExtfInfo >::iterator iti = d_extf_info.find( n ); + Assert( iti!=d_extf_info.end() ); + for( unsigned j=0; j<iti->second.d_vars.size(); j++ ){ + Node v = iti->second.d_vars[j]; + std::map< Node, std::vector< Node > >::iterator itx = expc.find( v ); + if( itx!=expc.end() ){ + for( unsigned k=0; k<itx->second.size(); k++ ){ + if( std::find( expn.begin(), expn.end(), itx->second[k] )==expn.end() ){ + expn.push_back( itx->second[k] ); + } + } + } + } + } + Trace("extt-debug") << " have " << n << " == " << ns << ", exp size=" << expn.size() << "." << std::endl; + //add to vector + sterms.push_back( ns ); + exp.push_back( expn ); + } + }else{ + for( unsigned i=0; i<terms.size(); i++ ){ + sterms.push_back( terms[i] ); + } + } + } +} + +//register term +void ExtTheory::registerTerm( Node n ) { + if( d_extf_kind.find( n.getKind() )!=d_extf_kind.end() ){ + if( d_ext_func_terms.find( n )==d_ext_func_terms.end() ){ + Trace("extt-debug") << "Found extended function : " << n << " in " << d_parent->getId() << std::endl; + d_ext_func_terms[n] = true; + d_has_extf = true; + std::map< Node, bool > visited; + collectVars( n, d_extf_info[n].d_vars, visited ); + } + } +} + +//mark reduced +void ExtTheory::markReduced( Node n ) { + d_ext_func_terms[n] = false; + //TODO update has_extf +} + +//mark congruent +void ExtTheory::markCongruent( Node a, Node b ) { + NodeBoolMap::const_iterator it = d_ext_func_terms.find( b ); + if( it!=d_ext_func_terms.end() ){ + if( d_ext_func_terms.find( a )==d_ext_func_terms.end() ){ + d_ext_func_terms[a] = (*it).second; + }else{ + d_ext_func_terms[a] = d_ext_func_terms[a] && (*it).second; + } + d_ext_func_terms[b] = false; + } +} + +//is active +bool ExtTheory::isActive( Node n ) { + NodeBoolMap::const_iterator it = d_ext_func_terms.find( n ); + if( it!=d_ext_func_terms.end() ){ + return (*it).second; + }else{ + return false; + } +} +//get active +void ExtTheory::getActive( std::vector< Node >& active ) { + for( NodeBoolMap::iterator it = d_ext_func_terms.begin(); it != d_ext_func_terms.end(); ++it ){ + //if not already reduced + if( (*it).second ){ + active.push_back( (*it).first ); + } + } +} + +void ExtTheory::getActive( std::vector< Node >& active, Kind k ) { + for( NodeBoolMap::iterator it = d_ext_func_terms.begin(); it != d_ext_func_terms.end(); ++it ){ + //if not already reduced + if( (*it).second && (*it).first.getKind()==k ){ + active.push_back( (*it).first ); + } + } +} + }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/theory.h b/src/theory/theory.h index e8518b1f6..ede06fd2d 100644 --- a/src/theory/theory.h +++ b/src/theory/theory.h @@ -48,6 +48,7 @@ namespace theory { class QuantifiersEngine; class TheoryModel; class SubstitutionMap; +class ExtTheory; class EntailmentCheckParameters; class EntailmentCheckSideEffects; @@ -201,6 +202,9 @@ private: protected: + /** extended theory */ + ExtTheory * d_extt; + // === STATISTICS === /** time spent in check calls */ TimerStat d_checkTime; @@ -565,7 +569,11 @@ public: * - or call get() until done() is true. */ virtual void check(Effort level = EFFORT_FULL) { } - + + /** + * Needs last effort check? + */ + virtual bool needsCheckLastEffort() { return false; } /** * T-propagate new literal assignments in the current context. */ @@ -589,7 +597,9 @@ public: * class. */ virtual void collectModelInfo( TheoryModel* m, bool fullModel ){ } - + /** if theories want to do something with model after building, do it here */ + virtual void postProcessModel( TheoryModel* m ){ } + /** * Return a decision request, if the theory has one, or the NULL node * otherwise. @@ -875,6 +885,16 @@ public: */ virtual std::pair<bool, Node> entailmentCheck(TNode lit, const EntailmentCheckParameters* params = NULL, EntailmentCheckSideEffects* out = NULL); + /* equality engine */ + virtual eq::EqualityEngine * getEqualityEngine() { return NULL; } + + /* get current substitution at an effort + * input : vars + * output : subs, exp + * where ( exp[vars[i]] => vars[i] = subs[i] ) holds for all i + */ + virtual bool getCurrentSubstitution( int effort, std::vector< Node >& vars, std::vector< Node >& subs, std::map< Node, std::vector< Node > >& exp ) { return false; } + /** * Turn on proof-production mode. */ @@ -944,6 +964,49 @@ public: virtual ~EntailmentCheckSideEffects(); };/* class EntailmentCheckSideEffects */ + +class ExtTheory { + friend class Theory; + typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap; +protected: + Theory * d_parent; + //extended string terms, map to whether they are active + NodeBoolMap d_ext_func_terms; + //any non-reduced extended functions exist + context::CDO< bool > d_has_extf; + //extf kind + std::map< Kind, bool > d_extf_kind; + //information for extf + class ExtfInfo { + public: + //all variables in this term + std::vector< Node > d_vars; + }; + std::map< Node, ExtfInfo > d_extf_info; + //collect variables + void collectVars( Node n, std::vector< Node >& vars, std::map< Node, bool >& visited ); +public: + ExtTheory( Theory * p ); + virtual ~ExtTheory(){} + //add extf kind + void addFunctionKind( Kind k ) { d_extf_kind[k] = true; } + //do inferences + // input : effort + // output : terms, sterms, exp, where ( exp[i] => terms[i] = sterms[i] ) for all i + void getInferences( int effort, std::vector< Node >& terms, std::vector< Node >& sterms, std::vector< std::vector< Node > >& exp ); + //register term + void registerTerm( Node n ); + //mark reduced + void markReduced( Node n ); + //mark congruent + void markCongruent( Node a, Node b ); + //is active + bool isActive( Node n ); + //get active + void getActive( std::vector< Node >& active ); + void getActive( std::vector< Node >& active, Kind k ); +}; + }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index 881acdddd..94281156f 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -83,33 +83,8 @@ theory::LemmaStatus TheoryEngine::EngineOutputChannel::lemma(TNode lemma, ++ 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); + registerLemmaRecipe(lemma, lemma, preprocess, d_theory); }); theory::LemmaStatus result = d_engine->lemma(lemma, @@ -117,22 +92,101 @@ theory::LemmaStatus TheoryEngine::EngineOutputChannel::lemma(TNode lemma, false, removable, preprocess, - sendAtoms ? d_theory : theory::THEORY_LAST, - proofRecipe); - PROOF(delete proofRecipe;); + sendAtoms ? d_theory : theory::THEORY_LAST); return result; } +void TheoryEngine::EngineOutputChannel::registerLemmaRecipe(Node lemma, Node originalLemma, bool preprocess, theory::TheoryId theoryId) { + // During CNF conversion, conjunctions will be broken down into + // multiple lemmas. In order for the recipes to match, we have to do + // the same here. + NodeManager* nm = NodeManager::currentNM(); + + if (preprocess) + lemma = d_engine->preprocess(lemma); + + bool negated = (lemma.getKind() == kind::NOT); + Node nnLemma = negated ? lemma[0] : lemma; + + switch (nnLemma.getKind()) { + + case kind::AND: + if (!negated) { + for (unsigned i = 0; i < nnLemma.getNumChildren(); ++i) + registerLemmaRecipe(nnLemma[i], originalLemma, false, theoryId); + } else { + NodeBuilder<> builder(kind::OR); + for (unsigned i = 0; i < nnLemma.getNumChildren(); ++i) + builder << nnLemma[i].negate(); + + Node disjunction = (builder.getNumChildren() == 1) ? builder[0] : builder; + registerLemmaRecipe(disjunction, originalLemma, false, theoryId); + } + break; + + case kind::IFF: + if (!negated) { + registerLemmaRecipe(nm->mkNode(kind::OR, nnLemma[0], nnLemma[1].negate()), originalLemma, false, theoryId); + registerLemmaRecipe(nm->mkNode(kind::OR, nnLemma[0].negate(), nnLemma[1]), originalLemma, false, theoryId); + } else { + registerLemmaRecipe(nm->mkNode(kind::OR, nnLemma[0], nnLemma[1]), originalLemma, false, theoryId); + registerLemmaRecipe(nm->mkNode(kind::OR, nnLemma[0].negate(), nnLemma[1].negate()), originalLemma, false, theoryId); + } + break; + + case kind::ITE: + if (!negated) { + registerLemmaRecipe(nm->mkNode(kind::OR, nnLemma[0].negate(), nnLemma[1]), originalLemma, false, theoryId); + registerLemmaRecipe(nm->mkNode(kind::OR, nnLemma[0], nnLemma[2]), originalLemma, false, theoryId); + } else { + registerLemmaRecipe(nm->mkNode(kind::OR, nnLemma[0].negate(), nnLemma[1].negate()), originalLemma, false, theoryId); + registerLemmaRecipe(nm->mkNode(kind::OR, nnLemma[0], nnLemma[2].negate()), originalLemma, false, theoryId); + } + break; + + default: + break; + } + + // Theory lemmas have one step that proves the empty clause + LemmaProofRecipe proofRecipe; + Node emptyNode; + LemmaProofRecipe::ProofStep proofStep(theoryId, emptyNode); + + // Remember the original lemma, so we can report this later when asked to + proofRecipe.setOriginalLemma(originalLemma); + + // Record the assertions and rewrites + 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); + ProofManager::getCnfProof()->setProofRecipe(&proofRecipe); +} + 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); + theory::LemmaStatus result = d_engine->lemma(lemma, RULE_SPLIT, false, removable, false, d_theory); return result; } @@ -157,6 +211,14 @@ void TheoryEngine::finishInit() { // initialize the quantifiers engine d_quantEngine = new QuantifiersEngine(d_context, d_userContext, this); + //initialize the model + if( d_logicInfo.isQuantified() ) { + d_curr_model = d_quantEngine->getModel(); + } else { + d_curr_model = new theory::TheoryModel(d_userContext, "DefaultModel", true); + d_aloc_curr_model = true; + } + if (d_logicInfo.isQuantified()) { d_quantEngine->finishInit(); Assert(d_masterEqualityEngine == 0); @@ -217,6 +279,7 @@ TheoryEngine::TheoryEngine(context::Context* context, d_masterEENotify(*this), d_quantEngine(NULL), d_curr_model(NULL), + d_aloc_curr_model(false), d_curr_model_builder(NULL), d_ppCache(), d_possiblePropagations(context), @@ -254,7 +317,6 @@ TheoryEngine::TheoryEngine(context::Context* context, } // build model information if applicable - d_curr_model = new theory::TheoryModel(userContext, "DefaultModel", true); d_curr_model_builder = new theory::TheoryEngineModelBuilder(this); smtStatisticsRegistry()->registerStat(&d_combineTheoriesTime); @@ -281,7 +343,9 @@ TheoryEngine::~TheoryEngine() { } delete d_curr_model_builder; - delete d_curr_model; + if( d_aloc_curr_model ){ + delete d_curr_model; + } delete d_quantEngine; @@ -518,24 +582,30 @@ 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() ) { - //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); + //checks for theories requiring the model go at last call + bool builtModel = false; + for (TheoryId theoryId = THEORY_FIRST; theoryId < THEORY_LAST; ++theoryId) { + if( theoryId!=THEORY_QUANTIFIERS ){ + Theory* theory = d_theoryTable[theoryId]; + if (theory && d_logicInfo.isTheoryEnabled(theoryId)) { + if( theory->needsCheckLastEffort() ){ + if( !builtModel ){ + builtModel = true; + d_curr_model_builder->buildModel(d_curr_model, false); + } + theory->check(Theory::EFFORT_LAST_CALL); + } + } } } 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 + // if returning incomplete or SAT, we have ensured that d_curr_model has been built with fullModel=true } else if(options::produceModels()) { // must build model at this point - d_curr_model_builder->buildModel(getModel(), true); + d_curr_model_builder->buildModel(d_curr_model, true); } Trace("theory::assertions-model") << endl; if (Trace.isOn("theory::assertions-model")) { @@ -611,8 +681,7 @@ void TheoryEngine::combineTheories() { // We need to split on it Debug("combineTheories") << "TheoryEngine::combineTheories(): requesting a split " << endl; - LemmaProofRecipe* proofRecipe = NULL; - lemma(equality.orNode(equality.notNode()), RULE_INVALID, false, false, false, carePair.theory, proofRecipe); + lemma(equality.orNode(equality.notNode()), RULE_INVALID, false, false, false, carePair.theory); // 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 @@ -780,16 +849,18 @@ void TheoryEngine::collectModelInfo( theory::TheoryModel* m, bool fullModel ){ } } +void TheoryEngine::postProcessModel( theory::TheoryModel* m ){ + for(TheoryId theoryId = theory::THEORY_FIRST; theoryId < theory::THEORY_LAST; ++theoryId) { + if(d_logicInfo.isTheoryEnabled(theoryId)) { + Trace("model-builder-debug") << " PostProcessModel on theory: " << theoryId << endl; + d_theoryTable[theoryId]->postProcessModel( m ); + } + } +} + /* get model */ TheoryModel* TheoryEngine::getModel() { - Debug("model") << "TheoryEngine::getModel()" << endl; - if( d_logicInfo.isQuantified() ) { - Debug("model") << "Get model from quantifiers engine." << endl; - return d_quantEngine->getModel(); - } else { - Debug("model") << "Get default model." << endl; - return d_curr_model; - } + return d_curr_model; } bool TheoryEngine::presolve() { @@ -1596,8 +1667,7 @@ theory::LemmaStatus TheoryEngine::lemma(TNode node, bool negated, bool removable, bool preprocess, - theory::TheoryId atomsTo, - LemmaProofRecipe* proofRecipe) { + theory::TheoryId atomsTo) { // For resource-limiting (also does a time check). // spendResource(); @@ -1647,10 +1717,10 @@ theory::LemmaStatus TheoryEngine::lemma(TNode node, } // assert to prop engine - d_propEngine->assertLemma(additionalLemmas[0], negated, removable, rule, proofRecipe, node); + d_propEngine->assertLemma(additionalLemmas[0], negated, removable, rule, node); for (unsigned i = 1; i < additionalLemmas.size(); ++ i) { additionalLemmas[i] = theory::Rewriter::rewrite(additionalLemmas[i]); - d_propEngine->assertLemma(additionalLemmas[i], false, removable, rule, proofRecipe, node); + d_propEngine->assertLemma(additionalLemmas[i], false, removable, rule, node); } // WARNING: Below this point don't assume additionalLemmas[0] to be not negated. @@ -1709,10 +1779,11 @@ void TheoryEngine::conflict(TNode conflict, TheoryId theoryId) { // Process the explanation getExplanation(explanationVector, proofRecipe); + PROOF(ProofManager::getCnfProof()->setProofRecipe(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, proofRecipe); + lemma(fullConflict, RULE_CONFLICT, true, true, false, THEORY_LAST); } else { // When only one theory, the conflict should need no processing @@ -1738,9 +1809,11 @@ void TheoryEngine::conflict(TNode conflict, TheoryId theoryId) { proofRecipe->getStep(0)->addAssertion(conflict.negate()); proofRecipe->addBaseAssertion(conflict.negate()); } + + ProofManager::getCnfProof()->setProofRecipe(proofRecipe); }); - lemma(conflict, RULE_CONFLICT, true, true, false, THEORY_LAST, proofRecipe); + lemma(conflict, RULE_CONFLICT, true, true, false, THEORY_LAST); } PROOF({ diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index 53c4aac77..9316066a5 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -183,6 +183,7 @@ class TheoryEngine { * Default model object */ theory::TheoryModel* d_curr_model; + bool d_aloc_curr_model; /** * Model builder object */ @@ -321,6 +322,13 @@ class TheoryEngine { void handleUserAttribute( const char* attr, theory::Theory* t ){ d_engine->handleUserAttribute( attr, t ); } + + private: + + /** + * A helper function for registering lemma recipes with the proof engine + */ + void registerLemmaRecipe(Node lemma, Node originalLemma, bool preprocess, theory::TheoryId theoryId); };/* class TheoryEngine::EngineOutputChannel */ /** @@ -428,8 +436,7 @@ class TheoryEngine { bool negated, bool removable, bool preprocess, - theory::TheoryId atomsTo, - LemmaProofRecipe* proofRecipe); + theory::TheoryId atomsTo); /** Enusre that the given atoms are send to the given theory */ void ensureLemmaAtoms(const std::vector<TNode>& atoms, theory::TheoryId theory); @@ -710,6 +717,8 @@ public: * collect model info */ void collectModelInfo( theory::TheoryModel* m, bool fullModel ); + /** post process model */ + void postProcessModel( theory::TheoryModel* m ); /** * Get the current model diff --git a/src/theory/theory_model.cpp b/src/theory/theory_model.cpp index f43a2aa7f..3cdaeb106 100644 --- a/src/theory/theory_model.cpp +++ b/src/theory/theory_model.cpp @@ -56,6 +56,9 @@ TheoryModel::~TheoryModel() throw() { void TheoryModel::reset(){ d_modelCache.clear(); + d_comment_str.clear(); + d_sep_heap = Node::null(); + d_sep_nil_eq = Node::null(); d_reps.clear(); d_rep_set.clear(); d_uf_terms.clear(); @@ -64,6 +67,26 @@ void TheoryModel::reset(){ d_eeContext->push(); } +void TheoryModel::getComments(std::ostream& out) const { + Trace("model-builder") << "get comments..." << std::endl; + out << d_comment_str.str(); +} + +void TheoryModel::setHeapModel( Node h, Node neq ) { + d_sep_heap = h; + d_sep_nil_eq = neq; +} + +bool TheoryModel::getHeapModel( Expr& h, Expr& neq ) const { + if( d_sep_heap.isNull() || d_sep_nil_eq.isNull() ){ + return false; + }else{ + h = d_sep_heap.toExpr(); + neq = d_sep_nil_eq.toExpr(); + return true; + } +} + Node TheoryModel::getValue(TNode n, bool useDontCares) const { //apply substitutions Node nn = d_substitutions.apply(n); @@ -352,6 +375,11 @@ void TheoryModel::assertEqualityEngine(const eq::EqualityEngine* ee, set<Node>* } else { if (first) { rep = (*eqc_i); + //add the term (this is specifically for the case of singleton equivalence classes) + if( !rep.getType().isRegExp() ){ + d_equalityEngine->addTerm( rep ); + Trace("model-builder-debug") << "Add term to ee within assertEqualityEngine: " << rep << std::endl; + } first = false; } else { @@ -651,7 +679,8 @@ void TheoryEngineModelBuilder::buildModel(Model* m, bool fullModel) // Assign representative for this EC if (!const_rep.isNull()) { // Theories should not specify a rep if there is already a constant in the EC - Assert(rep.isNull() || rep == const_rep); + //AJR: I believe this assertion is too strict, eqc with asserted reps may merge with constant eqc + //Assert(rep.isNull() || rep == const_rep); assignConstantRep( tm, constantReps, eqc, const_rep, fullModel ); typeConstSet.add(eqct.getBaseType(), const_rep); } @@ -789,14 +818,16 @@ void TheoryEngineModelBuilder::buildModel(Model* m, bool fullModel) //get properties of this type bool isCorecursive = false; - bool isUSortFiniteRestricted = false; if( t.isDatatype() ){ const Datatype& dt = ((DatatypeType)(t).toType()).getDatatype(); isCorecursive = dt.isCodatatype() && ( !dt.isFinite() || dt.isRecursiveSingleton() ); } +#ifdef CVC4_ASSERTIONS + bool isUSortFiniteRestricted = false; if( options::finiteModelFind() ){ isUSortFiniteRestricted = !t.isSort() && involvesUSort( t ); } +#endif set<Node>* repSet = typeRepSet.getSet(t); TypeNode tb = t.getBaseType(); @@ -841,8 +872,8 @@ void TheoryEngineModelBuilder::buildModel(Model* m, bool fullModel) Assert( !n.isNull() ); success = true; Trace("model-builder-debug") << "Check if excluded : " << n << std::endl; - if( isUSortFiniteRestricted ){ #ifdef CVC4_ASSERTIONS + if( isUSortFiniteRestricted ){ //must not involve uninterpreted constants beyond cardinality bound (which assumed to coincide with #eqc) //this is just an assertion now, since TypeEnumeratorProperties should ensure that only legal values are enumerated wrt this constraint. std::map< Node, bool > visited; @@ -851,8 +882,8 @@ void TheoryEngineModelBuilder::buildModel(Model* m, bool fullModel) Trace("model-builder") << "Excluded value for " << t << " : " << n << " due to out of range uninterpreted constant." << std::endl; } Assert( success ); -#endif } +#endif if( success && isCorecursive ){ if (repSet != NULL && !repSet->empty()) { // in the case of codatatypes, check if it is in the set of values that we cannot assign @@ -937,6 +968,12 @@ void TheoryEngineModelBuilder::buildModel(Model* m, bool fullModel) //modelBuilder-specific initialization processBuildModel( tm, fullModel ); + // Do post-processing of model from the theories (used for THEORY_SEP to construct heap model) + if( fullModel ){ + Trace("model-builder") << "TheoryEngineModelBuilder: Post-process model..." << std::endl; + d_te->postProcessModel(tm); + } + #ifdef CVC4_ASSERTIONS if (fullModel) { // Check that every term evaluates to its representative in the model @@ -1015,7 +1052,7 @@ Node TheoryEngineModelBuilder::normalize(TheoryModel* m, TNode r, std::map< Node retNode = NodeManager::currentNM()->mkNode( r.getKind(), children ); if (childrenConst) { retNode = Rewriter::rewrite(retNode); - Assert(retNode.getKind()==kind::APPLY_UF || retNode.getKind()==kind::REGEXP_RANGE || retNode.isConst()); + Assert(retNode.getKind()==kind::APPLY_UF || retNode.getType().isRegExp() || retNode.isConst()); } } d_normalizedCache[r] = retNode; diff --git a/src/theory/theory_model.h b/src/theory/theory_model.h index 833b124eb..7157433f9 100644 --- a/src/theory/theory_model.h +++ b/src/theory/theory_model.h @@ -53,7 +53,18 @@ public: Node d_true; Node d_false; mutable std::hash_map<Node, Node, NodeHashFunction> d_modelCache; - +public: + /** comment stream to include in printing */ + std::stringstream d_comment_str; + /** get comments */ + void getComments(std::ostream& out) const; +private: + /** information for separation logic */ + Node d_sep_heap; + Node d_sep_nil_eq; +public: + void setHeapModel( Node h, Node neq ); + bool getHeapModel( Expr& h, Expr& neq ) const; protected: /** reset the model */ virtual void reset(); diff --git a/src/theory/uf/equality_engine.cpp b/src/theory/uf/equality_engine.cpp index 25b12f75f..09d348584 100644 --- a/src/theory/uf/equality_engine.cpp +++ b/src/theory/uf/equality_engine.cpp @@ -393,7 +393,7 @@ const EqualityNode& EqualityEngine::getEqualityNode(EqualityNodeId nodeId) const void EqualityEngine::assertEqualityInternal(TNode t1, TNode t2, TNode reason, unsigned pid) { - Debug("equality") << d_name << "::eq::addEqualityInternal(" << t1 << "," << t2 << "), pid = " << pid << std::endl; + Debug("equality") << d_name << "::eq::addEqualityInternal(" << t1 << "," << t2 << "), reason = " << reason << ", pid = " << pid << std::endl; if (d_done) { return; @@ -1204,7 +1204,6 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st Debug("equality") << d_name << "::eq::getExplanation(): adding: " << reason << std::endl; Debug("equality") << d_name << "::eq::getExplanation(): reason type = " << reasonType << std::endl; - Node a = d_nodes[currentNode]; Node b = d_nodes[d_equalityEdges[currentEdge].getNodeId()]; diff --git a/src/util/integer_cln_imp.cpp b/src/util/integer_cln_imp.cpp index 0064f2904..aa6cb03af 100644 --- a/src/util/integer_cln_imp.cpp +++ b/src/util/integer_cln_imp.cpp @@ -40,6 +40,10 @@ signed long Integer::s_slowSignedIntMax = (signed long) std::numeric_limits<sig unsigned int Integer::s_fastUnsignedIntMax = (1<<29)-1; unsigned long Integer::s_slowUnsignedIntMax = (unsigned long) std::numeric_limits<unsigned int>::max(); +unsigned long Integer::s_signedLongMin = std::numeric_limits<signed long>::min(); +unsigned long Integer::s_signedLongMax = std::numeric_limits<signed long>::max(); +unsigned long Integer::s_unsignedLongMax = std::numeric_limits<unsigned long>::max(); + Integer Integer::oneExtend(uint32_t size, uint32_t amount) const { DebugCheckArgument((*this) < Integer(1).multiplyByPow2(size), size); cln::cl_byte range(amount, size); @@ -133,4 +137,12 @@ unsigned int Integer::getUnsignedInt() const { return cln::cl_I_to_uint(d_value); } +bool Integer::fitsSignedLong() const { + return d_value <= s_signedLongMax && d_value >= s_signedLongMin; +} + +bool Integer::fitsUnsignedLong() const { + return sgn() >= 0 && d_value <= s_unsignedLongMax; +} + } /* namespace CVC4 */ diff --git a/src/util/integer_cln_imp.h b/src/util/integer_cln_imp.h index 177fc02cf..6bfebbaf1 100644 --- a/src/util/integer_cln_imp.h +++ b/src/util/integer_cln_imp.h @@ -66,7 +66,9 @@ private: static signed long s_slowSignedIntMax; /* std::numeric_limits<signed int>::max() */ static signed long s_slowSignedIntMin; /* std::numeric_limits<signed int>::min() */ static unsigned long s_slowUnsignedIntMax; /* std::numeric_limits<unsigned int>::max() */ - + static unsigned long s_signedLongMin; + static unsigned long s_signedLongMax; + static unsigned long s_unsignedLongMax; public: /** Constructs a rational with the value 0. */ @@ -425,6 +427,10 @@ public: unsigned int getUnsignedInt() const; + bool fitsSignedLong() const; + + bool fitsUnsignedLong() const; + long getLong() const { // ensure there isn't overflow CheckArgument(d_value <= std::numeric_limits<long>::max(), this, diff --git a/src/util/integer_gmp_imp.cpp b/src/util/integer_gmp_imp.cpp index d165dbec3..e0472ac4c 100644 --- a/src/util/integer_gmp_imp.cpp +++ b/src/util/integer_gmp_imp.cpp @@ -73,6 +73,14 @@ unsigned int Integer::getUnsignedInt() const { return (unsigned int) d_value.get_ui(); } +bool Integer::fitsSignedLong() const { + return d_value.fits_slong_p(); +} + +bool Integer::fitsUnsignedLong() const { + return d_value.fits_ulong_p(); +} + Integer Integer::oneExtend(uint32_t size, uint32_t amount) const { // check that the size is accurate DebugCheckArgument((*this) < Integer(1).multiplyByPow2(size), size); diff --git a/src/util/integer_gmp_imp.h b/src/util/integer_gmp_imp.h index 0c5665e38..3a95c6b85 100644 --- a/src/util/integer_gmp_imp.h +++ b/src/util/integer_gmp_imp.h @@ -411,6 +411,10 @@ public: unsigned int getUnsignedInt() const; + bool fitsSignedLong() const; + + bool fitsUnsignedLong() const; + long getLong() const { long si = d_value.get_si(); // ensure there wasn't overflow diff --git a/src/util/regexp.cpp b/src/util/regexp.cpp index d211aaf1b..a6f0de4b3 100644 --- a/src/util/regexp.cpp +++ b/src/util/regexp.cpp @@ -110,6 +110,17 @@ std::size_t String::overlap(String &y) const { } return i; } +std::size_t String::roverlap(String &y) const { + std::size_t i = d_str.size() < y.size() ? d_str.size() : y.size(); + for(; i>0; i--) { + String s = prefix(i); + String p = y.suffix(i); + if(s == p) { + return i; + } + } + return i; +} std::string String::toString() const { std::string str; diff --git a/src/util/regexp.h b/src/util/regexp.h index 2cfcbc4e4..06766e046 100644 --- a/src/util/regexp.h +++ b/src/util/regexp.h @@ -239,24 +239,25 @@ public: if(d_str.size() < y.d_str.size() + start) return std::string::npos; if(y.d_str.size() == 0) return start; if(d_str.size() == 0) return std::string::npos; - std::size_t ret = std::string::npos; - /*for(std::size_t i = start; i <= d_str.size() - y.d_str.size(); i++) { - if(d_str[i] == y.d_str[0]) { - std::size_t j=0; - for(; j<y.d_str.size(); j++) { - if(d_str[i+j] != y.d_str[j]) break; - } - if(j == y.d_str.size()) { - ret = i; - break; - } - } - }*/ std::vector<unsigned>::const_iterator itr = std::search(d_str.begin() + start, d_str.end(), y.d_str.begin(), y.d_str.end()); if(itr != d_str.end()) { - ret = itr - d_str.begin(); + return itr - d_str.begin(); + }else{ + return std::string::npos; + } + } + + std::size_t rfind( String &y, const std::size_t start = 0) { + std::reverse( d_str.begin(), d_str.end() ); + std::reverse( y.d_str.begin(), y.d_str.end() ); + std::size_t f = find( y, start ); + std::reverse( d_str.begin(), d_str.end() ); + std::reverse( y.d_str.begin(), y.d_str.end() ); + if( f==std::string::npos ){ + return std::string::npos; + }else{ + return f; } - return ret; } String replace(const String &s, const String &t) const { @@ -295,6 +296,8 @@ public: } // if y=y1...yn and overlap returns m, then this is x1...y1...ym std::size_t overlap(String &y) const; + // if y=y1...yn and overlap returns m, then this is y(n+1-m)...yn...xk + std::size_t roverlap(String &y) const; bool isNumber() const { if(d_str.size() == 0) return false; |