From 2039eab2d76cf5f4cfe44680f5325f44a4fc5a99 Mon Sep 17 00:00:00 2001 From: Kshitij Bansal Date: Tue, 3 Nov 2015 04:47:30 -0500 Subject: [PATCH] cardinality operation for finite sets (based on my thesis / ijcar16 paper) Some further cleanup/fixes pending This is squash of 39 commits (kbansal/card branch + cleanup): * add card operator * local reasoning * towards graph building * first implementation * close cardinality terms * model building * more * more * more * Add aggressive sets rewriting. * Recursively aggressive rewrite sets. * Fix * incomplete card2 implementation * ... * Avoid using auto in sets. * fix merge * more * ... * more * ... * Fixed for loops * Slight modification to computeRelevantTerms * more * .. * more * ... * mv empty set lemma generation to later point * more options/reordering * debug related * more trace * ... * fix merge_nodes, models * rm warnigns * fix compile errors * warning * bug fixes/cleanup * mroe fixes * cleanup * ... --- src/lib/clock_gettime.c | 2 +- src/options/options_public_functions.cpp | 2 +- src/options/sets_options | 8 + src/parser/cvc/Cvc.g | 5 + src/parser/smt2/smt2.cpp | 1 + src/printer/cvc/cvc_printer.cpp | 7 + src/printer/smt2/smt2_printer.cpp | 2 +- src/smt/smt_engine_check_proof.cpp | 2 +- src/theory/output_channel.h | 1 - .../sets/card_unused_implementation.cpp | 312 +++++ src/theory/sets/expr_patterns.h | 4 + src/theory/sets/kinds | 5 +- src/theory/sets/theory_sets.cpp | 4 + src/theory/sets/theory_sets.h | 2 + src/theory/sets/theory_sets_private.cpp | 1197 ++++++++++++++++- src/theory/sets/theory_sets_private.h | 95 ++ src/theory/sets/theory_sets_rewriter.cpp | 397 +++++- src/theory/sets/theory_sets_rewriter.h | 4 + src/theory/sets/theory_sets_type_rules.h | 20 +- src/theory/theory.cpp | 4 +- src/theory/theory.h | 2 +- src/theory/theory_engine.cpp | 1 + src/theory/theory_engine.h | 9 +- test/regress/regress0/sets/card-2.smt2 | 10 + test/regress/regress0/sets/card-3.smt2 | 11 + test/regress/regress0/sets/card-4.smt2 | 23 + test/regress/regress0/sets/card-5.smt2 | 24 + test/regress/regress0/sets/card-6.smt2 | 16 + test/regress/regress0/sets/card-7.smt2 | 46 + test/regress/regress0/sets/card.smt2 | 8 + 30 files changed, 2151 insertions(+), 73 deletions(-) create mode 100644 src/theory/sets/card_unused_implementation.cpp create mode 100644 test/regress/regress0/sets/card-2.smt2 create mode 100644 test/regress/regress0/sets/card-3.smt2 create mode 100644 test/regress/regress0/sets/card-4.smt2 create mode 100644 test/regress/regress0/sets/card-5.smt2 create mode 100644 test/regress/regress0/sets/card-6.smt2 create mode 100644 test/regress/regress0/sets/card-7.smt2 create mode 100644 test/regress/regress0/sets/card.smt2 diff --git a/src/lib/clock_gettime.c b/src/lib/clock_gettime.c index d9143a14f..9bd4eafe8 100644 --- a/src/lib/clock_gettime.c +++ b/src/lib/clock_gettime.c @@ -16,7 +16,7 @@ ** OS X). **/ -#warning "TODO(taking): Make lib/clock_gettime.h cvc4_private.h again." +// #warning "TODO(taking): Make lib/clock_gettime.h cvc4_private.h again." #include "lib/clock_gettime.h" diff --git a/src/options/options_public_functions.cpp b/src/options/options_public_functions.cpp index 0ee9cc21a..8f3a63175 100644 --- a/src/options/options_public_functions.cpp +++ b/src/options/options_public_functions.cpp @@ -204,7 +204,7 @@ std::ostream* Options::getOut(){ } std::ostream* Options::getOutConst() const{ -#warning "Remove Options::getOutConst" + // #warning "Remove Options::getOutConst" return (*this)[options::out]; } diff --git a/src/options/sets_options b/src/options/sets_options index 67bed5fe7..be945e4c9 100644 --- a/src/options/sets_options +++ b/src/options/sets_options @@ -17,4 +17,12 @@ expert-option setsCare1 --sets-care1 bool :default false option setsPropFull --sets-prop-full bool :default true additional propagation at full effort +option setsAggRewrite --sets-agg-rewrite bool :default false + aggressive sets rewriting + +option setsGuessEmpty --sets-guess-empty int :default 0 + when to guess leaf nodes being empty (0...2 : most aggressive..least aggressive) + +option setsSlowLemmas --sets-slow-lemmas bool :default true + endmodule diff --git a/src/parser/cvc/Cvc.g b/src/parser/cvc/Cvc.g index 4a8f7eb7b..470fc5253 100644 --- a/src/parser/cvc/Cvc.g +++ b/src/parser/cvc/Cvc.g @@ -1985,6 +1985,11 @@ simpleTerm[CVC4::Expr& f] } } + /* set cardinality literal */ + | BAR BAR formula[f] { args.push_back(f); } BAR BAR + { f = MK_EXPR(kind::CARD, args[0]); + } + /* array literals */ | ARRAY_TOK /* { PARSER_STATE->pushScope(); } */ LPAREN restrictedType[t, CHECK_DECLARED] OF_TOK restrictedType[t2, CHECK_DECLARED] diff --git a/src/parser/smt2/smt2.cpp b/src/parser/smt2/smt2.cpp index 55fb4f60d..ebad90583 100644 --- a/src/parser/smt2/smt2.cpp +++ b/src/parser/smt2/smt2.cpp @@ -226,6 +226,7 @@ void Smt2::addTheory(Theory theory) { addOperator(kind::MEMBER, "member"); addOperator(kind::SINGLETON, "singleton"); addOperator(kind::INSERT, "insert"); + addOperator(kind::CARD, "card"); break; case THEORY_DATATYPES: diff --git a/src/printer/cvc/cvc_printer.cpp b/src/printer/cvc/cvc_printer.cpp index 55e19510b..bc59e37ba 100644 --- a/src/printer/cvc/cvc_printer.cpp +++ b/src/printer/cvc/cvc_printer.cpp @@ -793,6 +793,13 @@ void CvcPrinter::toStream(std::ostream& out, TNode n, int depth, bool types, boo return; break; } + case kind::CARD: { + out << "||"; + toStream(out, n[0], depth, types, false); + out << "||"; + return; + break; + } // Quantifiers case kind::FORALL: diff --git a/src/printer/smt2/smt2_printer.cpp b/src/printer/smt2/smt2_printer.cpp index 62153571c..4defc7691 100644 --- a/src/printer/smt2/smt2_printer.cpp +++ b/src/printer/smt2/smt2_printer.cpp @@ -977,7 +977,7 @@ void Smt2Printer::toStream(std::ostream& out, const Command* c, static std::string quoteSymbol(TNode n) { -#warning "check the old implementation. It seems off." + // #warning "check the old implementation. It seems off." std::stringstream ss; ss << language::SetLanguage(language::output::LANG_SMTLIB_V2_5); return CVC4::quoteSymbol(ss.str()); diff --git a/src/smt/smt_engine_check_proof.cpp b/src/smt/smt_engine_check_proof.cpp index a58102d86..5634a4651 100644 --- a/src/smt/smt_engine_check_proof.cpp +++ b/src/smt/smt_engine_check_proof.cpp @@ -22,7 +22,7 @@ #include #include -#warning "TODO: Why is lfsc's check.h being included like this?" +// #warning "TODO: Why is lfsc's check.h being included like this?" #include "check.h" #include "base/configuration_private.h" diff --git a/src/theory/output_channel.h b/src/theory/output_channel.h index da0b92ae8..639793c7f 100644 --- a/src/theory/output_channel.h +++ b/src/theory/output_channel.h @@ -142,7 +142,6 @@ public: return lemma(n, RULE_INVALID, removable, preprocess, sendAtoms); } - /** * Request a split on a new theory atom. This is equivalent to * calling lemma({OR n (NOT n)}). diff --git a/src/theory/sets/card_unused_implementation.cpp b/src/theory/sets/card_unused_implementation.cpp new file mode 100644 index 000000000..488ee1026 --- /dev/null +++ b/src/theory/sets/card_unused_implementation.cpp @@ -0,0 +1,312 @@ +// Removing old cardinality implementation, dumping it here. + +/////////////////////////////////////////////////////////////// +// Commenting out processCard, creates confusion when writing +// processCard2 +/////////////////////////////////////////////////////////////// + + +// void TheorySetsPrivate::processCard(Theory::Effort level) { +// if(level != Theory::EFFORT_FULL) return; + + +// Trace("sets-card") << "[sets-card] processCard( " << level << ")" << std::endl; +// Trace("sets-card") << "[sets-card] # processed terms = " << d_processedCardTerms.size() << std::endl; +// Trace("sets-card") << "[sets-card] # processed pairs = " << d_processedCardPairs.size() << std::endl; +// NodeManager* nm = NodeManager::currentNM(); + +// bool newLemmaGenerated = false; + +// // Introduce lemma +// for(typeof(d_cardTerms.begin()) it = d_cardTerms.begin(); +// it != d_cardTerms.end(); ++it) { + +// for(eq::EqClassIterator j(d_equalityEngine.getRepresentative((*it)[0]), &d_equalityEngine); +// !j.isFinished(); ++j) { + +// Node n = nm->mkNode(kind::CARD, (*j)); + +// if(d_processedCardTerms.find(n) != d_processedCardTerms.end()) { +// continue; +// } + +// Trace("sets-card") << "[sets-card] Processing " << n << " in eq cl of " << (*it) << std::endl; + +// newLemmaGenerated = true; +// d_processedCardTerms.insert(n); + +// Kind k = n[0].getKind(); + +// if(k == kind::SINGLETON) { +// d_external.d_out->lemma(nm->mkNode(kind::EQUAL, +// n, +// nm->mkConst(Rational(1)))); +// continue; +// } else { +// d_external.d_out->lemma(nm->mkNode(kind::GEQ, +// n, +// nm->mkConst(Rational(0)))); +// } + +// // rest of the processing is for compound terms +// if(k != kind::UNION && k != kind::INTERSECTION && k != kind::SETMINUS) { +// continue; +// } + +// Node s = min(n[0][0], n[0][1]); +// Node t = max(n[0][0], n[0][1]); +// bool isUnion = (k == kind::UNION); +// Assert(Rewriter::rewrite(s) == s); +// Assert(Rewriter::rewrite(t) == t); + +// typeof(d_processedCardPairs.begin()) processedInfo = d_processedCardPairs.find(make_pair(s, t)); + +// if(processedInfo == d_processedCardPairs.end()) { + +// Node sNt = nm->mkNode(kind::INTERSECTION, s, t); +// sNt = Rewriter::rewrite(sNt); +// Node sMt = nm->mkNode(kind::SETMINUS, s, t); +// sMt = Rewriter::rewrite(sMt); +// Node tMs = nm->mkNode(kind::SETMINUS, t, s); +// tMs = Rewriter::rewrite(tMs); + +// Node card_s = nm->mkNode(kind::CARD, s); +// Node card_t = nm->mkNode(kind::CARD, t); +// Node card_sNt = nm->mkNode(kind::CARD, sNt); +// Node card_sMt = nm->mkNode(kind::CARD, sMt); +// Node card_tMs = nm->mkNode(kind::CARD, tMs); + +// Node lem; + +// // for s +// lem = nm->mkNode(kind::EQUAL, +// card_s, +// nm->mkNode(kind::PLUS, card_sNt, card_sMt)); +// d_external.d_out->lemma(lem); + +// // for t +// lem = nm->mkNode(kind::EQUAL, +// card_t, +// nm->mkNode(kind::PLUS, card_sNt, card_tMs)); + +// d_external.d_out->lemma(lem); + +// // for union +// if(isUnion) { +// lem = nm->mkNode(kind::EQUAL, +// n, // card(s union t) +// nm->mkNode(kind::PLUS, card_sNt, card_sMt, card_tMs)); +// d_external.d_out->lemma(lem); +// } + +// d_processedCardPairs.insert(make_pair(make_pair(s, t), isUnion)); + +// } else if(isUnion && processedInfo->second == false) { + +// Node sNt = nm->mkNode(kind::INTERSECTION, s, t); +// sNt = Rewriter::rewrite(sNt); +// Node sMt = nm->mkNode(kind::SETMINUS, s, t); +// sMt = Rewriter::rewrite(sMt); +// Node tMs = nm->mkNode(kind::SETMINUS, t, s); +// tMs = Rewriter::rewrite(tMs); + +// Node card_s = nm->mkNode(kind::CARD, s); +// Node card_t = nm->mkNode(kind::CARD, t); +// Node card_sNt = nm->mkNode(kind::CARD, sNt); +// Node card_sMt = nm->mkNode(kind::CARD, sMt); +// Node card_tMs = nm->mkNode(kind::CARD, tMs); + +// Assert(Rewriter::rewrite(n[0]) == n[0]); + +// Node lem = nm->mkNode(kind::EQUAL, +// n, // card(s union t) +// nm->mkNode(kind::PLUS, card_sNt, card_sMt, card_tMs)); +// d_external.d_out->lemma(lem); + +// processedInfo->second = true; +// } + +// }//equivalence class loop + +// }//d_cardTerms loop + +// if(newLemmaGenerated) { +// Trace("sets-card") << "[sets-card] New introduce done. Returning." << std::endl; +// return; +// } + + + +// // Leaves disjoint lemmas +// buildGraph(); + +// // Leaves disjoint lemmas +// for(typeof(leaves.begin()) it = leaves.begin(); it != leaves.end(); ++it) { +// TNode l1 = (*it); +// if(d_equalityEngine.getRepresentative(l1).getKind() == kind::EMPTYSET) continue; +// for(typeof(leaves.begin()) jt = leaves.begin(); jt != leaves.end(); ++jt) { +// TNode l2 = (*jt); + +// if(d_equalityEngine.getRepresentative(l2).getKind() == kind::EMPTYSET) continue; + +// if( l1 == l2 ) continue; + +// Node l1_inter_l2 = nm->mkNode(kind::INTERSECTION, min(l1, l2), max(l1, l2)); +// l1_inter_l2 = Rewriter::rewrite(l1_inter_l2); +// Node emptySet = nm->mkConst(EmptySet(nm->toType(l1_inter_l2.getType()))); +// if(d_equalityEngine.hasTerm(l1_inter_l2) && +// d_equalityEngine.hasTerm(emptySet) && +// d_equalityEngine.areEqual(l1_inter_l2, emptySet)) { +// Debug("sets-card-graph") << "[sets-card-graph] Disjoint (asserted): " << l1 << " and " << l2 << std::endl; +// continue; // known to be disjoint +// } + +// std::set l1_ancestors = getReachable(edgesBk, l1); +// std::set l2_ancestors = getReachable(edgesBk, l2); + +// // have a disjoint edge +// bool loop = true; +// bool equality = false; +// for(typeof(l1_ancestors.begin()) l1_it = l1_ancestors.begin(); +// l1_it != l1_ancestors.end() && loop; ++l1_it) { +// for(typeof(l2_ancestors.begin()) l2_it = l2_ancestors.begin(); +// l2_it != l2_ancestors.end() && loop; ++l2_it) { +// TNode n1 = (*l1_it); +// TNode n2 = (*l2_it); +// if(disjoint.find(make_pair(n1, n2)) != disjoint.find(make_pair(n2, n1))) { +// loop = false; +// } +// if(n1 == n2) { +// equality = true; +// } +// if(d_equalityEngine.hasTerm(n1) && d_equalityEngine.hasTerm(n2) && +// d_equalityEngine.areEqual(n1, n2)) { +// equality = true; +// } +// } +// } +// if(loop == false) { +// Debug("sets-card-graph") << "[sets-card-graph] Disjoint (always): " << l1 << " and " << l2 << std::endl; +// continue; +// } +// if(equality == false) { +// Debug("sets-card-graph") << "[sets-card-graph] No equality found: " << l1 << " and " << l2 << std::endl; +// continue; +// } + +// Node lem = nm->mkNode(kind::OR, +// nm->mkNode(kind::EQUAL, l1_inter_l2, emptySet), +// nm->mkNode(kind::LT, nm->mkConst(Rational(0)), +// nm->mkNode(kind::CARD, l1_inter_l2))); + +// d_external.d_out->lemma(lem); +// Trace("sets-card") << "[sets-card] Guessing disjointness of : " << l1 << " and " << l2 << std::endl; +// if(Debug.isOn("sets-card-disjoint")) { +// Debug("sets-card-disjoint") << "[sets-card-disjoint] Lemma for " << l1 << " and " << l2 << " generated because:" << std::endl; +// for(typeof(disjoint.begin()) it = disjoint.begin(); it != disjoint.end(); ++it) { +// Debug("sets-card-disjoint") << "[sets-card-disjoint] " << it->first << " " << it->second << std::endl; +// } +// } +// newLemmaGenerated = true; +// Trace("sets-card") << "[sets-card] New intersection being empty lemma generated. Returning." << std::endl; +// return; +// } +// } + +// Assert(!newLemmaGenerated); + + + +// // Elements being either equal or disequal + +// for(typeof(leaves.begin()) it = leaves.begin(); +// it != leaves.end(); ++it) { +// Assert(d_equalityEngine.hasTerm(*it)); +// Node n = d_equalityEngine.getRepresentative(*it); +// Assert(n.getKind() == kind::EMPTYSET || leaves.find(n) != leaves.end()); +// if(n != *it) continue; +// const CDTNodeList* l = d_termInfoManager->getMembers(*it); +// std::set elems; +// for(typeof(l->begin()) l_it = l->begin(); l_it != l->end(); ++l_it) { +// elems.insert(d_equalityEngine.getRepresentative(*l_it)); +// } +// for(typeof(elems.begin()) e1_it = elems.begin(); e1_it != elems.end(); ++e1_it) { +// for(typeof(elems.begin()) e2_it = elems.begin(); e2_it != elems.end(); ++e2_it) { +// if(*e1_it == *e2_it) continue; +// if(!d_equalityEngine.areDisequal(*e1_it, *e2_it, false)) { +// Node lem = nm->mkNode(kind::EQUAL, *e1_it, *e2_it); +// lem = nm->mkNode(kind::OR, lem, nm->mkNode(kind::NOT, lem)); +// d_external.d_out->lemma(lem); +// newLemmaGenerated = true; +// } +// } +// } +// } + +// if(newLemmaGenerated) { +// Trace("sets-card") << "[sets-card] Members arrangments lemmas. Returning." << std::endl; +// return; +// } + + +// // Guess leaf nodes being empty or non-empty +// for(typeof(leaves.begin()) it = leaves.begin(); it != leaves.end(); ++it) { +// Node n = d_equalityEngine.getRepresentative(*it); +// if(n.getKind() == kind::EMPTYSET) continue; +// if(d_termInfoManager->getMembers(n)->size() > 0) continue; +// Node emptySet = nm->mkConst(EmptySet(nm->toType(n.getType()))); +// if(!d_equalityEngine.hasTerm(emptySet)) { +// d_equalityEngine.addTerm(emptySet); +// } +// if(!d_equalityEngine.areDisequal(n, emptySet, false)) { +// Node lem = nm->mkNode(kind::EQUAL, n, emptySet); +// lem = nm->mkNode(kind::OR, lem, nm->mkNode(kind::NOT, lem)); +// Assert(d_cardLowerLemmaCache.find(lem) == d_cardLowerLemmaCache.end()); +// d_cardLowerLemmaCache.insert(lem); +// d_external.d_out->lemma(lem); +// newLemmaGenerated = true; +// break; +// } +// } + +// if(newLemmaGenerated) { +// Trace("sets-card") << "[sets-card] New guessing leaves being empty done." << std::endl; +// return; +// } + +// // Assert Lower bound +// for(typeof(leaves.begin()) it = leaves.begin(); +// it != leaves.end(); ++it) { +// Assert(d_equalityEngine.hasTerm(*it)); +// Node n = d_equalityEngine.getRepresentative(*it); +// Assert(n.getKind() == kind::EMPTYSET || leaves.find(n) != leaves.end()); +// if(n != *it) continue; +// const CDTNodeList* l = d_termInfoManager->getMembers(n); +// std::set elems; +// for(typeof(l->begin()) l_it = l->begin(); l_it != l->end(); ++l_it) { +// elems.insert(d_equalityEngine.getRepresentative(*l_it)); +// } +// if(elems.size() == 0) continue; +// NodeBuilder<> nb(kind::OR); +// nb << ( nm->mkNode(kind::LEQ, nm->mkConst(Rational(elems.size())), nm->mkNode(kind::CARD, n)) ); +// if(elems.size() > 1) { +// for(typeof(elems.begin()) e1_it = elems.begin(); e1_it != elems.end(); ++e1_it) { +// for(typeof(elems.begin()) e2_it = elems.begin(); e2_it != elems.end(); ++e2_it) { +// if(*e1_it == *e2_it) continue; +// nb << (nm->mkNode(kind::EQUAL, *e1_it, *e2_it)); +// } +// } +// } +// for(typeof(elems.begin()) e_it = elems.begin(); e_it != elems.end(); ++e_it) { +// nb << nm->mkNode(kind::NOT, nm->mkNode(kind::MEMBER, *e_it, n)); +// } +// Node lem = Node(nb); +// if(d_cardLowerLemmaCache.find(lem) == d_cardLowerLemmaCache.end()) { +// Trace("sets-card") << "[sets-card] Card Lower: " << lem << std::endl; +// d_external.d_out->lemma(lem); +// d_cardLowerLemmaCache.insert(lem); +// newLemmaGenerated = true; +// } +// } +// } + diff --git a/src/theory/sets/expr_patterns.h b/src/theory/sets/expr_patterns.h index 768797ecf..32e77d8b8 100644 --- a/src/theory/sets/expr_patterns.h +++ b/src/theory/sets/expr_patterns.h @@ -54,6 +54,10 @@ static Node EQUAL(TNode a, TNode b) { return NodeManager::currentNM()->mkNode(kind::EQUAL, a, b); } +static Node CARD(TNode a) { + return NodeManager::currentNM()->mkNode(kind::CARD, a); +} + }/* CVC4::expr::pattern namespace */ }/* CVC4::expr namespace */ }/* CVC4 namespace */ diff --git a/src/theory/sets/kinds b/src/theory/sets/kinds index a43902b1b..14c87a947 100644 --- a/src/theory/sets/kinds +++ b/src/theory/sets/kinds @@ -12,7 +12,7 @@ rewriter ::CVC4::theory::sets::TheorySetsRewriter \ "theory/sets/theory_sets_rewriter.h" properties parametric -properties check propagate +properties check propagate presolve # constants constant EMPTYSET \ @@ -42,6 +42,7 @@ operator SUBSET 2 "subset predicate; first parameter a subset of second" operator MEMBER 2 "set membership predicate; first parameter a member of second" operator SINGLETON 1 "the set of the single element given as a parameter" operator INSERT 2: "set obtained by inserting elements (first N-1 parameters) into a set (the last parameter)" +operator CARD 1 "set cardinality operator" typerule UNION ::CVC4::theory::sets::SetsBinaryOperatorTypeRule typerule INTERSECTION ::CVC4::theory::sets::SetsBinaryOperatorTypeRule @@ -51,11 +52,13 @@ typerule MEMBER ::CVC4::theory::sets::MemberTypeRule typerule SINGLETON ::CVC4::theory::sets::SingletonTypeRule typerule EMPTYSET ::CVC4::theory::sets::EmptySetTypeRule typerule INSERT ::CVC4::theory::sets::InsertTypeRule +typerule CARD ::CVC4::theory::sets::CardTypeRule construle UNION ::CVC4::theory::sets::SetsBinaryOperatorTypeRule construle INTERSECTION ::CVC4::theory::sets::SetsBinaryOperatorTypeRule construle SETMINUS ::CVC4::theory::sets::SetsBinaryOperatorTypeRule construle SINGLETON ::CVC4::theory::sets::SingletonTypeRule construle INSERT ::CVC4::theory::sets::InsertTypeRule +construle CARD ::CVC4::theory::sets::CardTypeRule endtheory diff --git a/src/theory/sets/theory_sets.cpp b/src/theory/sets/theory_sets.cpp index 36265134f..bdbc964c6 100644 --- a/src/theory/sets/theory_sets.cpp +++ b/src/theory/sets/theory_sets.cpp @@ -70,6 +70,10 @@ void TheorySets::preRegisterTerm(TNode node) { d_internal->preRegisterTerm(node); } +void TheorySets::presolve() { + d_internal->presolve(); +} + void TheorySets::propagate(Effort e) { d_internal->propagate(e); } diff --git a/src/theory/sets/theory_sets.h b/src/theory/sets/theory_sets.h index d5a965799..bbeaf4a4c 100644 --- a/src/theory/sets/theory_sets.h +++ b/src/theory/sets/theory_sets.h @@ -63,6 +63,8 @@ public: void preRegisterTerm(TNode node); + void presolve(); + void propagate(Effort); void setMasterEqualityEngine(eq::EqualityEngine* eq); diff --git a/src/theory/sets/theory_sets_private.cpp b/src/theory/sets/theory_sets_private.cpp index dbdec0cdc..a16e857dd 100644 --- a/src/theory/sets/theory_sets_private.cpp +++ b/src/theory/sets/theory_sets_private.cpp @@ -14,6 +14,7 @@ ** Sets theory implementation. **/ +#include #include "theory/sets/theory_sets_private.h" #include @@ -36,12 +37,15 @@ namespace sets { const char* element_of_str = " \u2208 "; +// Declaration of functions defined later in this CPP file +const std::set getLeaves(map >& edges, TNode node); + /**************************** TheorySetsPrivate *****************************/ /**************************** TheorySetsPrivate *****************************/ /**************************** TheorySetsPrivate *****************************/ void TheorySetsPrivate::check(Theory::Effort level) { - + d_newLemmaGenerated = false; while(!d_external.done() && !d_conflict) { // Get all the assertions Assertion assertion = d_external.get(); @@ -96,13 +100,17 @@ void TheorySetsPrivate::check(Theory::Effort level) { } if( (level == Theory::EFFORT_FULL || options::setsEagerLemmas() ) && !isComplete()) { - d_external.d_out->lemma(getLemma()); + lemma(getLemma(), SETS_LEMMA_OTHER); return; } + + //processCard(level); + processCard2(level); + // if we are here, there is no conflict and we are complete if(Debug.isOn("sets-scrutinize")) { d_scrutinize->postCheckInvariants(); } - + return; }/* TheorySetsPrivate::check() */ @@ -134,10 +142,23 @@ void TheorySetsPrivate::assertEquality(TNode fact, TNode reason, bool learnt) return; } + if(atom[0].getKind() == kind::CARD && isCardVar(atom[0])) { + NodeManager* nm = NodeManager::currentNM(); + Node emptySet = nm->mkConst(EmptySet(nm->toType(atom[0].getType()))); + Node newFact = nm->mkNode(kind::EQUAL, getCardVar(atom[0]), emptySet); + if(!polarity) newFact = nm->mkNode(kind::NOT, newFact); + learnLiteral(newFact, fact); + } + + // disequality lemma if(!polarity && atom[0].getType().isSet()) { addToPending(atom); } + // for cardinality + if(polarity && atom[0].getType().isSet()) { + d_graphMergesPending.push(make_pair(atom[0], atom[1])); + } }/* TheorySetsPrivate::assertEquality() */ @@ -365,11 +386,12 @@ void TheorySetsPrivate::doSettermPropagation(TNode x, TNode S) addToPending( MEMBER(x, S[0]) ); break; case kind::SETMINUS: // intentional fallthrough - case kind::INTERSECTION: if( holds(MEMBER(x, S[0])) && !present( MEMBER(x, S[1]) )) addToPending( MEMBER(x, S[1]) ); break; + case kind::INTERSECTION: + return; default: Assert(false, "MembershipEngine::doSettermPropagation"); } @@ -406,6 +428,33 @@ void TheorySetsPrivate::learnLiteral(TNode atom, bool polarity, Node reason) { }/*TheorySetsPrivate::learnLiteral(...)*/ +/************************ CardVar ************************/ + +Node TheorySetsPrivate::getCardVar(TNode n) { + NodeNodeHashMap::iterator it = d_setTermToCardVar.find(n); + if(it == d_setTermToCardVar.end()) { + return it->second; + } else { + NodeManager* nm = NodeManager::currentNM(); + Node cardVar = nm->mkSkolem("scv_", n.getType()); + d_setTermToCardVar[n] = cardVar; + d_cardVarToSetTerm[cardVar] = n; + return cardVar; + } +} + +Node TheorySetsPrivate::newCardVar(TNode n) { + NodeNodeHashMap::iterator it = d_cardVarToSetTerm.find(n); + Assert(it != d_cardVarToSetTerm.end()); + return it->second; +} + +bool TheorySetsPrivate::isCardVar(TNode n) { + NodeNodeHashMap::iterator it = d_cardVarToSetTerm.find(n); + return it != d_cardVarToSetTerm.end(); +} + + /************************ Sharing ************************/ /************************ Sharing ************************/ /************************ Sharing ************************/ @@ -765,11 +814,23 @@ Node TheorySetsPrivate::elementsToShape(set elements, TypeNode setType) co void TheorySetsPrivate::collectModelInfo(TheoryModel* m, bool fullModel) { - Debug("sets-model") << "[sets-model] collectModelInfo(..., fullModel=" + Trace("sets-model") << "[sets-model] collectModelInfo(..., fullModel=" << (fullModel ? "true)" : "false)") << std::endl; set terms; + NodeManager* nm = NodeManager::currentNM(); + + // // this is for processCard -- commenting out for now + // if(Debug.isOn("sets-card")) { + // for(typeof(d_cardTerms.begin()) it = d_cardTerms.begin(); + // it != d_cardTerms.end(); ++it) { + // Debug("sets-card") << "[sets-card] " << *it << " = " + // << d_external.d_valuation.getModelValue(*it) + // << std::endl; + // } + // } + if(Trace.isOn("sets-assertions")) { dumpAssertionsHumanified(); } @@ -777,6 +838,23 @@ void TheorySetsPrivate::collectModelInfo(TheoryModel* m, bool fullModel) // Compute terms appearing assertions and shared terms d_external.computeRelevantTerms(terms); + //processCard2 begin + if(Debug.isOn("sets-card")) { + for(typeof(d_V.begin()) it = d_V.begin(); it != d_V.end(); ++it) { + Node n = nm->mkNode(kind::CARD, *it); + Debug("sets-card") << "[sets-card] " << n << " = "; + // if(d_external.d_sharedTerms.find(n) == d_external.d_sharedTerms.end()) continue; + if((Rewriter::rewrite(n)).isConst()) { + Debug("sets-card") << (Rewriter::rewrite(n)) + << std::endl; + } else { + Debug("sets-card") << d_external.d_valuation.getModelValue(n) + << std::endl; + } + } + } + //processCard2 end + // Compute for each setterm elements that it contains SettermElementsMap settermElementsMap; for(eq::EqClassIterator it_eqclasses(d_trueNode, &d_equalityEngine); @@ -789,10 +867,10 @@ void TheorySetsPrivate::collectModelInfo(TheoryModel* m, bool fullModel) settermElementsMap[S].insert(x); } if(Debug.isOn("sets-model-details")) { - vector explanation; - d_equalityEngine.explainPredicate(n, true, explanation); Debug("sets-model-details") << "[sets-model-details] > node: " << n << ", explanation:" << std::endl; + vector explanation; + d_equalityEngine.explainPredicate(n, true, explanation); BOOST_FOREACH(TNode m, explanation) { Debug("sets-model-details") << "[sets-model-details] >> " << m << std::endl; } @@ -804,9 +882,9 @@ void TheorySetsPrivate::collectModelInfo(TheoryModel* m, bool fullModel) ! it_eqclasses.isFinished() ; ++it_eqclasses) { TNode n = (*it_eqclasses); vector explanation; - d_equalityEngine.explainPredicate(n, false, explanation); Debug("sets-model-details") << "[sets-model-details] > node: not: " << n << ", explanation:" << std::endl; + d_equalityEngine.explainPredicate(n, false, explanation); BOOST_FOREACH(TNode m, explanation) { Debug("sets-model-details") << "[sets-model-details] >> " << m << std::endl; } @@ -838,16 +916,74 @@ void TheorySetsPrivate::collectModelInfo(TheoryModel* m, bool fullModel) } } - BOOST_FOREACH( SettermElementsMap::value_type &it, settermElementsMap ) { - BOOST_FOREACH( TNode element, it.second /* elements */ ) { - Debug("sets-model-details") << "[sets-model-details] > " << - (it.first /* setterm */) << ": " << element << std::endl; + if(Debug.isOn("sets-model-details")) { + BOOST_FOREACH( SettermElementsMap::value_type &it, settermElementsMap ) { + BOOST_FOREACH( TNode element, it.second /* elements */ ) { + Debug("sets-model-details") << "[sets-model-details] > " << + (it.first /* setterm */) << ": " << element << std::endl; + } + } + } + + // build graph, and create sufficient number of skolems + // buildGraph(); // this is for processCard + + //processCard2 begin + leaves.clear(); + for(typeof(d_V.begin()) it = d_V.begin(); it != d_V.end(); ++it) + if(d_E.find(*it) == d_E.end()) + leaves.insert(*it); + d_statistics.d_numLeaves.setData(leaves.size()); + d_statistics.d_numLeavesMax.maxAssign(leaves.size()); + //processCard2 end + + std::hash_map, TNodeHashFunction> slackElements; + BOOST_FOREACH( TNode setterm, leaves ) { + if(setterm.getKind() == kind::EMPTYSET) { continue; } + // Assert(d_cardTerms.find(nm->mkNode(kind::CARD,setterm)) != d_cardTerms.end()); // for processCard + Assert(d_V.find(setterm) != d_V.end()); + Node cardValNode = d_external.d_valuation.getModelValue(nm->mkNode(kind::CARD,setterm)); + Rational cardValRational = cardValNode.getConst(); + Assert(cardValRational.isIntegral()); + Integer cardValInteger = cardValRational.getNumerator(); + Assert(cardValInteger.fitsSignedInt(), "Can't build models that big."); + int cardValInt = cardValInteger.getSignedInt(); + Assert(cardValInt >= 0); + int numElems = getElements(setterm, settermElementsMap).size(); + Trace("sets-model-card") << "[sets-model-card] cardValInt = " << cardValInt << std::endl + << " numElems = " << numElems << std::endl; + Trace("sets-model-card") << "[sets-model-card] Creating " << cardValInt-numElems + << " slack variables for " << setterm << std::endl; + Assert(cardValInt >= numElems, "Run with -d sets-model-card for details"); + + TypeNode elementType = setterm.getType().getSetElementType(); + std::vector& cur = slackElements[setterm]; + for(int i = numElems; i < cardValInt; ++i) { + // slk = slack + cur.push_back(nm->mkSkolem("slk_", elementType)); } } // assign representatives to equivalence class BOOST_FOREACH( TNode setterm, settermsModEq ) { Elements elements = getElements(setterm, settermElementsMap); + if(d_E.find(setterm) != d_E.end()) { + Trace("sets-model-card") << "[sets-model-card] " << setterm << " (before slacks): " << elements.size() << std::endl; + std::set leafChildren = get_leaves(setterm); + BOOST_FOREACH( TNode leafChild, leafChildren ) { + if(leaves.find(leafChild) == leaves.end()) { continue; } + BOOST_FOREACH( TNode slackVar, slackElements[leafChild] ) { + elements.insert(slackVar); + } + } + Trace("sets-model-card") << "[sets-model-card] " << setterm << " (after slacks): " << elements.size() << std::endl; + } else if(d_V.find(setterm) != d_V.end()) { + Trace("sets-model-card") << "[sets-model-card] " << setterm << " (before slacks): " << elements.size() << std::endl; + BOOST_FOREACH( TNode slackVar, slackElements[setterm] ) { + elements.insert(slackVar); + } + Trace("sets-model-card") << "[sets-model-card] " << setterm << " (after slacks): " << elements.size() << std::endl; + } Node shape = elementsToShape(elements, setterm.getType()); shape = theory::Rewriter::rewrite(shape); m->assertEquality(shape, setterm, true); @@ -921,20 +1057,44 @@ Node mkAnd(const std::vector& conjunctions) { TheorySetsPrivate::Statistics::Statistics() : - d_getModelValueTime("theory::sets::getModelValueTime") + d_getModelValueTime("theory::sets::getModelValueTime") + , d_mergeTime("theory::sets::merge_nodes::time") + , d_processCard2Time("theory::sets::processCard2::time") , d_memberLemmas("theory::sets::lemmas::member", 0) , d_disequalityLemmas("theory::sets::lemmas::disequality", 0) + , d_numVertices("theory::sets::vertices", 0) + , d_numVerticesMax("theory::sets::vertices-max", 0) + , d_numMergeEq1or2("theory::sets::merge1or2", 0) + , d_numMergeEq3("theory::sets::merge3", 0) + , d_numLeaves("theory::sets::leaves", 0) + , d_numLeavesMax("theory::sets::leaves-max", 0) { smtStatisticsRegistry()->registerStat(&d_getModelValueTime); + smtStatisticsRegistry()->registerStat(&d_mergeTime); + smtStatisticsRegistry()->registerStat(&d_processCard2Time); smtStatisticsRegistry()->registerStat(&d_memberLemmas); smtStatisticsRegistry()->registerStat(&d_disequalityLemmas); + smtStatisticsRegistry()->registerStat(&d_numVertices); + smtStatisticsRegistry()->registerStat(&d_numVerticesMax); + smtStatisticsRegistry()->registerStat(&d_numMergeEq1or2); + smtStatisticsRegistry()->registerStat(&d_numMergeEq3); + smtStatisticsRegistry()->registerStat(&d_numLeaves); + smtStatisticsRegistry()->registerStat(&d_numLeavesMax); } TheorySetsPrivate::Statistics::~Statistics() { smtStatisticsRegistry()->unregisterStat(&d_getModelValueTime); + smtStatisticsRegistry()->unregisterStat(&d_mergeTime); + smtStatisticsRegistry()->unregisterStat(&d_processCard2Time); smtStatisticsRegistry()->unregisterStat(&d_memberLemmas); smtStatisticsRegistry()->unregisterStat(&d_disequalityLemmas); + smtStatisticsRegistry()->unregisterStat(&d_numVertices); + smtStatisticsRegistry()->unregisterStat(&d_numVerticesMax); + smtStatisticsRegistry()->unregisterStat(&d_numMergeEq1or2); + smtStatisticsRegistry()->unregisterStat(&d_numMergeEq3); + smtStatisticsRegistry()->unregisterStat(&d_numLeaves); + smtStatisticsRegistry()->unregisterStat(&d_numLeavesMax); } @@ -961,9 +1121,10 @@ void TheorySetsPrivate::registerReason(TNode reason, bool save) if(save) d_nodeSaver.insert(reason); if(reason.getKind() == kind::AND) { - Assert(reason.getNumChildren() == 2); - registerReason(reason[0], false); - registerReason(reason[1], false); + //Assert(reason.getNumChildren() == 2); + for(unsigned i = 0; i < reason.getNumChildren(); ++i) { + registerReason(reason[i], false); + } } else if(reason.getKind() == kind::NOT) { registerReason(reason[0], false); } else if(reason.getKind() == kind::MEMBER) { @@ -1025,7 +1186,8 @@ void TheorySetsPrivate::addToPending(Node n) { << std::endl; ++d_statistics.d_memberLemmas; d_pending.push(n); - d_external.d_out->splitLemma(getLemma()); + lemma(getLemma(), SETS_LEMMA_MEMBER); + // d_external.d_out->splitLemma(); Assert(isComplete()); } else { @@ -1035,7 +1197,8 @@ void TheorySetsPrivate::addToPending(Node n) { Assert(n.getKind() == kind::EQUAL); ++d_statistics.d_disequalityLemmas; d_pendingDisequal.push(n); - d_external.d_out->splitLemma(getLemma()); + lemma(getLemma(), SETS_LEMMA_DISEQUAL); + // d_external.d_out->splitLemma(); Assert(isComplete()); } @@ -1076,7 +1239,13 @@ Node TheorySetsPrivate::getLemma() { Node x = NodeManager::currentNM()->mkSkolem("sde_", elementType); Node l1 = MEMBER(x, n[0]), l2 = MEMBER(x, n[1]); - lemma = OR(n, AND(l1, NOT(l2)), AND(NOT(l1), l2)); + if(n[0].getKind() == kind::EMPTYSET) { + lemma = OR(n, l2); + } else if(n[1].getKind() == kind::EMPTYSET) { + lemma = OR(n, l1); + } else { + lemma = OR(n, AND(l1, NOT(l2)), AND(NOT(l1), l2)); + } } Debug("sets-lemma") << "[sets-lemma] Generating for " << n @@ -1096,6 +1265,8 @@ TheorySetsPrivate::TheorySetsPrivate(TheorySets& external, d_falseNode(NodeManager::currentNM()->mkConst(false)), d_conflict(c), d_termInfoManager(NULL), + d_setTermToCardVar(), + d_cardVarToSetTerm(), d_propagationQueue(c), d_settermPropagationQueue(c), d_nodeSaver(c), @@ -1105,7 +1276,24 @@ TheorySetsPrivate::TheorySetsPrivate(TheorySets& external, d_modelCache(c), d_ccg_i(c), d_ccg_j(c), - d_scrutinize(NULL) + d_scrutinize(NULL), + d_cardEnabled(false), + d_cardTerms(c), + d_typesAdded(), + d_processedCardTerms(c), + d_processedCardPairs(), + d_cardLowerLemmaCache(u), + edgesFd(), + edgesBk(), + disjoint(), + leaves(), + d_V(c), + d_E(c), + d_graphMergesPending(c), + d_allSetEqualitiesSoFar(c), + d_lemmasGenerated(u), + d_newLemmaGenerated(false), + d_relTerms(u) { d_termInfoManager = new TermInfoManager(*this, c, &d_equalityEngine); @@ -1116,6 +1304,9 @@ TheorySetsPrivate::TheorySetsPrivate(TheorySets& external, d_equalityEngine.addFunctionKind(kind::MEMBER); d_equalityEngine.addFunctionKind(kind::SUBSET); + // If cardinality is on. + d_equalityEngine.addFunctionKind(kind::CARD); + if( Debug.isOn("sets-scrutinize") ) { d_scrutinize = new TheorySetsScrutinize(this); } @@ -1230,6 +1421,30 @@ Node TheorySetsPrivate::explain(TNode literal) return mkAnd(assumptions); } +bool TheorySetsPrivate::lemma(Node n, SetsLemmaTag t) +{ + if(d_lemmasGenerated.find(n) != d_lemmasGenerated.end()) { + return false; + } + d_lemmasGenerated.insert(n); + d_newLemmaGenerated = true; + switch(t) { + case SETS_LEMMA_DISEQUAL: + case SETS_LEMMA_MEMBER: { + d_external.d_out->splitLemma(n); + break; + } + case SETS_LEMMA_GRAPH:// { + // d_external.d_out->preservedLemma(n, false, false); + // break; + // } + case SETS_LEMMA_OTHER: { + d_external.d_out->lemma(n); + break; + } + } + return true; +} void TheorySetsPrivate::preRegisterTerm(TNode node) { @@ -1245,6 +1460,11 @@ void TheorySetsPrivate::preRegisterTerm(TNode node) // TODO: what's the point of this d_equalityEngine.addTriggerPredicate(node); break; + case kind::CARD: + if(!d_cardEnabled) { enableCard(); } + registerCard(node); + d_equalityEngine.addTriggerTerm(node, THEORY_SETS); + break; default: d_termInfoManager->addTerm(node); d_equalityEngine.addTriggerTerm(node, THEORY_SETS); @@ -1253,9 +1473,38 @@ void TheorySetsPrivate::preRegisterTerm(TNode node) if(node.getKind() == kind::SINGLETON) { learnLiteral(MEMBER(node[0], node), true, d_trueNode); } + + // ** For cardinality reasoning ** + if(node.getType().isSet() && d_typesAdded.find(node.getType()) == d_typesAdded.end()) { + d_typesAdded.insert(node.getType()); + + if(d_cardEnabled) { + cardCreateEmptysetSkolem(node.getType()); + } + } + if(d_cardEnabled && node.getKind() == kind::SINGLETON) { + registerCard(NodeManager::currentNM()->mkNode(kind::CARD, node)); + } } +void TheorySetsPrivate::presolve() { + + for(typeof(d_termInfoManager->d_terms.begin()) it = d_termInfoManager->d_terms.begin(); + it != d_termInfoManager->d_terms.end(); ++it) { + d_relTerms.insert(*it); + } + + if(Trace.isOn("sets-relterms")) { + Trace("sets-relterms") << "[sets-relterms] "; + for(typeof(d_relTerms.begin()) it = d_relTerms.begin(); + it != d_relTerms.end(); ++it ) { + Trace("sets-relterms") << (*it) << ", "; + } + Trace("sets-relterms") << "\n"; + } + +} /**************************** eq::NotifyClass *****************************/ /**************************** eq::NotifyClass *****************************/ @@ -1289,7 +1538,7 @@ bool TheorySetsPrivate::NotifyClass::eqNotifyTriggerTermEquality(TheoryId tag, T { Debug("sets-eq") << "[sets-eq] eqNotifyTriggerTermEquality: tag = " << tag << " t1 = " << t1 << " t2 = " << t2 << " value = " << value << std::endl; - if(value) { + if(value && t1.getKind() != kind::CARD && t2.getKind() != kind::CARD) { d_theory.d_termInfoManager->mergeTerms(t1, t2); } d_theory.propagate( value ? EQUAL(t1, t2) : NOT(EQUAL(t1, t2)) ); @@ -1408,7 +1657,12 @@ void TheorySetsPrivate::TermInfoManager::addTerm(TNode n) { if(d_terms.contains(n[i])) { Debug("sets-parent") << "Adding " << n << " to parent list of " << n[i] << std::endl; + + // introduce cardinality of this set if a child's cardinality appears d_info[n[i]]->parents->push_back(n); + if(d_theory.d_cardTerms.find(CARD(n[i])) != d_theory.d_cardTerms.end()) { + d_theory.registerCard(CARD(n)); + } typeof(d_info.begin()) ita = d_info.find(d_eqEngine->getRepresentative(n[i])); Assert(ita != d_info.end()); @@ -1576,6 +1830,907 @@ Node TheorySetsPrivate::TermInfoManager::getModelValue(TNode n) return v; } + + + +/********************** Cardinality ***************************/ +/********************** Cardinality ***************************/ +/********************** Cardinality ***************************/ + +void TheorySetsPrivate::enableCard() +{ + Assert(!d_cardEnabled); + Trace("sets-card") << "[sets-card] Enabling cardinality reasoning" << std::endl; + d_cardEnabled = true; + + BOOST_FOREACH( TypeNode t, d_typesAdded ) { + cardCreateEmptysetSkolem(t); + } + + for(typeof(d_termInfoManager->d_terms.begin()) it = d_termInfoManager->d_terms.begin(); + it != d_termInfoManager->d_terms.end(); ++it) { + Node n = (*it); + if(n.getKind() == kind::SINGLETON) { + registerCard(NodeManager::currentNM()->mkNode(kind::CARD, n)); + } + } +} + +void TheorySetsPrivate::registerCard(TNode node) { + Trace("sets-card") << "[sets-card] registerCard( " << node << ")" << std::endl; + if(d_cardTerms.find(node) == d_cardTerms.end()) { + d_cardTerms.insert(node); + + // introduce cardinality of any set-term containing this term + NodeManager* nm = NodeManager::currentNM(); + const CDTNodeList* parentList = d_termInfoManager->getParents(node[0]); + for(typeof(parentList->begin()) it = parentList->begin(); + it != parentList->end(); ++it) { + registerCard(nm->mkNode(kind::CARD, *it)); + } + } +} + + +void TheorySetsPrivate::cardCreateEmptysetSkolem(TypeNode t) { + // set cardinality zero + NodeManager* nm = NodeManager::currentNM(); + Debug("sets-card") << "Creating skolem for emptyset for type " + << t << std::endl; + Node emptySet = nm->mkConst(EmptySet(nm->toType(t))); + Node sk = nm->mkSkolem("scz_", t); + lemma(nm->mkNode(kind::EQUAL, sk, emptySet), SETS_LEMMA_OTHER); + lemma(nm->mkNode(kind::EQUAL, nm->mkConst(Rational(0)), nm->mkNode(kind::CARD, sk)), SETS_LEMMA_OTHER); +} + + +void TheorySetsPrivate::buildGraph() { + + NodeManager* nm = NodeManager::currentNM(); + + edgesFd.clear(); + edgesBk.clear(); + disjoint.clear(); + + for(typeof(d_processedCardPairs.begin()) it = d_processedCardPairs.begin(); + it != d_processedCardPairs.end(); ++it) { + Node s = (it->first).first; + Assert(Rewriter::rewrite(s) == s); + Node t = (it->first).second; + Assert(Rewriter::rewrite(t) == t); + bool hasUnion = (it->second); + + Node sNt = nm->mkNode(kind::INTERSECTION, s, t); + sNt = Rewriter::rewrite(sNt); + Node sMt = nm->mkNode(kind::SETMINUS, s, t); + sMt = Rewriter::rewrite(sMt); + Node tMs = nm->mkNode(kind::SETMINUS, t, s); + tMs = Rewriter::rewrite(tMs); + + edgesFd[s].insert(sNt); + edgesFd[s].insert(sMt); + edgesBk[sNt].insert(s); + edgesBk[sMt].insert(s); + + edgesFd[t].insert(sNt); + edgesFd[t].insert(tMs); + edgesBk[sNt].insert(t); + edgesBk[tMs].insert(t); + + if(hasUnion) { + Node sUt = nm->mkNode(kind::UNION, s, t); + sUt = Rewriter::rewrite(sUt); + + edgesFd[sUt].insert(sNt); + edgesFd[sUt].insert(sMt); + edgesFd[sUt].insert(tMs); + edgesBk[sNt].insert(sUt); + edgesBk[sMt].insert(sUt); + edgesBk[tMs].insert(sUt); + } + + disjoint.insert(make_pair(sNt, sMt)); + disjoint.insert(make_pair(sMt, sNt)); + disjoint.insert(make_pair(sNt, tMs)); + disjoint.insert(make_pair(tMs, sNt)); + disjoint.insert(make_pair(tMs, sMt)); + disjoint.insert(make_pair(sMt, tMs)); + } + + if(Debug.isOn("sets-card-graph")) { + Debug("sets-card-graph") << "[sets-card-graph] Fd:" << std::endl; + for(typeof(edgesFd.begin()) it = edgesFd.begin(); + it != edgesFd.end(); ++it) { + Debug("sets-card-graph") << "[sets-card-graph] " << (it->first) << std::endl; + for(typeof( (it->second).begin()) jt = (it->second).begin(); + jt != (it->second).end(); ++jt) { + Debug("sets-card-graph") << "[sets-card-graph] " << (*jt) << std::endl; + } + } + Debug("sets-card-graph") << "[sets-card-graph] Bk:" << std::endl; + for(typeof(edgesBk.begin()) it = edgesBk.begin(); + it != edgesBk.end(); ++it) { + Debug("sets-card-graph") << "[sets-card-graph] " << (it->first) << std::endl; + for(typeof( (it->second).begin()) jt = (it->second).begin(); + jt != (it->second).end(); ++jt) { + Debug("sets-card-graph") << "[sets-card-graph] " << (*jt) << std::endl; + } + } + } + + + + leaves.clear(); + + for(typeof(d_processedCardTerms.begin()) it = d_processedCardTerms.begin(); + it != d_processedCardTerms.end(); ++it) { + Node n = (*it)[0]; + if( edgesFd.find(n) == edgesFd.end() ) { + leaves.insert(n); + Debug("sets-card-graph") << "[sets-card-graph] Leaf: " << n << std::endl; + } + // if( edgesBk.find(n) != edgesBk.end() ) { + // Assert(n.getKind() == kind::INTERSECTION || + // n.getKind() == kind::SETMINUS); + // } + } + +} + +const std::set getReachable(map >& edges, TNode node) { + Debug("sets-getreachable-debug") << "[sets-getreachable-debug] " << node << ":" << std::endl; + queue Q; + std::set ret; + ret.insert(node); + if(edges.find(node) != edges.end()) { + Debug("sets-getreachable-debug") << "[sets-getreachable-debug] " << node << ":" << std::endl; + Q.push(node); + } + while(!Q.empty()) { + TNode n = Q.front(); + Q.pop(); + for(set::iterator it = edges[n].begin(); + it != edges[n].end(); ++it) { + if(ret.find(*it) == ret.end()) { + if(edges.find(*it) != edges.end()) { + Debug("sets-getreachable-debug") << "[sets-getreachable-debug] " << *it << ":" << std::endl; + Q.push(*it); + } + ret.insert(*it); + } + } + } + return ret; +} + +const std::set getLeaves(map >& edges, TNode node) { + Debug("sets-getreachable-debug") << "[sets-getreachable-debug] " << node << ":" << std::endl; + queue Q; + std::set ret; + std::set visited; + visited.insert(node); + if(edges.find(node) != edges.end()) { + Q.push(node); + } else { + Debug("sets-getreachable-debug") << "[sets-getreachable-debug] " << node << std::endl; + ret.insert(node); + } + while(!Q.empty()) { + TNode n = Q.front(); + Q.pop(); + for(set::iterator it = edges[n].begin(); + it != edges[n].end(); ++it) { + if(visited.find(*it) == visited.end()) { + if(edges.find(*it) != edges.end()) { + Q.push(*it); + } else { + Debug("sets-getreachable-debug") << "[sets-getreachable-debug] " << *it << std::endl; + ret.insert(*it); + } + visited.insert(*it); + } + } + } + return ret; +} + +/************ New cardinality implementation **************/ + + +/*** + * Data structures: + * d_V : vertices in the graph (context dependent data structure) + * d_E : edges between vertices in the graph + * + * Methods: + * + * merge(vector a, vector b) + * get non empty leaves + * of a & b, for each internal node, there will be two parent nodes + * + * Introduce + * + */ + +void TheorySetsPrivate::add_edges(TNode source, TNode dest) { + vector V; + V.push_back(dest); + add_edges(source, V); +} + +void TheorySetsPrivate::add_edges(TNode source, TNode dest1, TNode dest2) { + vector V; + V.push_back(dest1); + V.push_back(dest2); + add_edges(source, V); +} + +void TheorySetsPrivate::add_edges(TNode source, TNode dest1, TNode dest2, TNode dest3) { + vector V; + V.push_back(dest1); + V.push_back(dest2); + V.push_back(dest3); + add_edges(source, V); +} + +void TheorySetsPrivate::add_edges(TNode source, const std::vector& dests) { + + if(Debug.isOn("sets-graph-details")) { + Debug("sets-graph-details") << "[sets-graph-details] add_edges " << source + << " ["; + BOOST_FOREACH(TNode v, dests) { + Debug("sets-graph-details") << v << ", "; + Assert(d_V.find(v) != d_V.end()); + } + Debug("sets-graph-details") << "]" << std::endl; + } + + Assert(d_E.find(source) == d_E.end()); + if(dests.size() == 1 && dests[0] == source) { + return; + } + d_E.insert(source, dests); +} + + +void TheorySetsPrivate::add_node(TNode vertex) { + NodeManager* nm = NodeManager::currentNM(); + Debug("sets-graph-details") << "[sets-graph-details] add_node " << vertex << std::endl; + if(d_V.find(vertex) == d_V.end()) { + d_V.insert(vertex); + Kind k = vertex.getKind(); + if(k == kind::SINGLETON) { + // newLemmaGenerated = true; + lemma(nm->mkNode(kind::EQUAL, + nm->mkNode(kind::CARD, vertex), + nm->mkConst(Rational(1))), + SETS_LEMMA_OTHER); + } else if(k != kind::EMPTYSET) { + // newLemmaGenerated = true; + lemma(nm->mkNode(kind::GEQ, + nm->mkNode(kind::CARD, vertex), + nm->mkConst(Rational(0))), + SETS_LEMMA_OTHER); + } + d_statistics.d_numVerticesMax.maxAssign(d_V.size()); + } + d_equalityEngine.addTerm(vertex); + d_termInfoManager->addTerm(vertex); +} + +std::set TheorySetsPrivate::non_empty(std::set vertices) +{ + std::set ret; + NodeManager* nm = NodeManager::currentNM(); + BOOST_FOREACH(TNode vertex, vertices) { + Node emptySet = nm->mkConst(EmptySet(nm->toType(vertex.getType()))); + if(!d_equalityEngine.areEqual(vertex, emptySet)) { + ret.insert(vertex); + } + } + return ret; +} + +std::set TheorySetsPrivate::get_leaves(Node vertex) { + Debug("sets-graph-details") << "[sets-graph-details] get_leaves " << vertex << std::endl; + std::set a; + Assert(d_V.find(vertex) != d_V.end()); + if(d_E.find(vertex) != d_E.end()) { + Assert(d_E[vertex].get().size() > 0); + BOOST_FOREACH(TNode v , d_E[vertex].get()) { + std::set s = get_leaves(v); + a.insert(s.begin(), s.end()); + } + } else { + a.insert(vertex); + } + // a = non_empty(a); + return a; +} + +std::set TheorySetsPrivate::get_leaves(Node vertex1, Node vertex2) { + std::set s = get_leaves(vertex1); + std::set t = get_leaves(vertex2); + t.insert(s.begin(), s.end()); + return t; +} + +std::set TheorySetsPrivate::get_leaves(Node vertex1, Node vertex2, Node vertex3) { + std::set s = get_leaves(vertex1); + std::set t = get_leaves(vertex2); + std::set u = get_leaves(vertex3); + t.insert(s.begin(), s.end()); + t.insert(u.begin(), u.end()); + return t; +} + +Node TheorySetsPrivate::eqemptySoFar() { + std::vector V; + + for(typeof(d_V.begin()) it = d_V.begin(); it != d_V.end(); ++it) { + Node rep = d_equalityEngine.getRepresentative(*it); + if(rep.getKind() == kind::EMPTYSET) { + V.push_back(EQUAL(rep, (*it))); + } + } + + if(V.size() == 0) { + return d_trueNode; + } else if(V.size() == 1) { + return V[0]; + } else { + NodeManager* nm = NodeManager::currentNM(); + return nm->mkNode(kind::AND, V); + } +} + + +void TheorySetsPrivate::merge_nodes(std::set leaves1, std::set leaves2, Node reason) { + CodeTimer codeTimer(d_statistics.d_mergeTime); + + NodeManager* nm = NodeManager::currentNM(); + + // do non-empty reasoning stuff + std::vector leaves1_nonempty, leaves2_nonempty; + BOOST_FOREACH(TNode l, leaves1) { + Node emptySet = nm->mkConst(EmptySet(nm->toType(l.getType()))); + if(d_equalityEngine.getRepresentative(l).getKind() != kind::EMPTYSET) { + leaves1_nonempty.push_back(l); + } else { + // reason = nm->mkNode(kind::AND, reason, EQUAL(l, emptySet)); + } + } + BOOST_FOREACH(TNode l, leaves2) { + Node emptySet = nm->mkConst(EmptySet(nm->toType(l.getType()))); + if(d_equalityEngine.getRepresentative(l).getKind() != kind::EMPTYSET) { + leaves2_nonempty.push_back(l); + } else { + // reason = nm->mkNode(kind::AND, reason, EQUAL(l, emptySet)); + } + } + + // last minute stuff + reason = nm->mkNode(kind::AND, reason, eqemptySoFar()); + + Trace("sets-graph-merge") << "[sets-graph-merge] merge_nodes(..,.., " << reason << ")" + << std::endl; + print_graph(); + Trace("sets-graph") << std::endl; + + std::set leaves3, leaves4; + std::set_difference(leaves1_nonempty.begin(), leaves1_nonempty.end(), + leaves2_nonempty.begin(), leaves2_nonempty.end(), + std::inserter(leaves3, leaves3.begin())); + std::set_difference(leaves2_nonempty.begin(), leaves2_nonempty.end(), + leaves1_nonempty.begin(), leaves1_nonempty.end(), + std::inserter(leaves4, leaves4.begin())); + + if(leaves3.size() == 0) { + Trace("sets-graph-merge") << "[sets-graph-merge] Merge Equality 1" << std::endl; + // make everything in leaves4 empty + BOOST_FOREACH(TNode v , leaves4) { + Node zero = nm->mkConst(Rational(0)); + if(!d_equalityEngine.hasTerm(zero)) { + d_equalityEngine.addTerm(zero); + d_termInfoManager->addTerm(zero); + } + learnLiteral( /* atom = */ EQUAL(nm->mkNode(kind::CARD, v), zero), + /* polarity = */ true, + /* reason = */ reason); + } + ++d_statistics.d_numMergeEq1or2; + } else if(leaves4.size() == 0) { + Trace("sets-graph-merge") << "[sets-graph-merge] Merge Equality 2" << std::endl; + // make everything in leaves3 empty + BOOST_FOREACH(TNode v , leaves3) { + Node zero = nm->mkConst(Rational(0)); + if(!d_equalityEngine.hasTerm(zero)) { + d_equalityEngine.addTerm(zero); + d_termInfoManager->addTerm(zero); + } + learnLiteral( /* atom = */ EQUAL(nm->mkNode(kind::CARD, v), zero), + /* polarity = */ true, + /* reason = */ reason); + } + ++d_statistics.d_numMergeEq1or2; + } else { + Trace("sets-graph-merge") << "[sets-graph-merge] Merge Equality 3" << std::endl; + Trace("sets-graph-merge") << "[sets-graph-merge] #left= " << leaves1.size() + << " #right= " << leaves2.size() + << " #left non-empty= " << leaves1_nonempty.size() + << " #right non-empty= " << leaves2_nonempty.size() + << " #left-right= " << leaves3.size() + << " #right-left= " << leaves4.size() << std::endl; + + std::map > children; + + // Merge Equality 3 + BOOST_FOREACH(TNode l1 , leaves3) { + BOOST_FOREACH(TNode l2 , leaves4) { + Node l1_inter_l2 = nm->mkNode(kind::INTERSECTION, min(l1, l2), max(l1, l2)); + l1_inter_l2 = Rewriter::rewrite(l1_inter_l2); + add_node(l1_inter_l2); + children[l1].push_back(l1_inter_l2); + children[l2].push_back(l1_inter_l2); + // if(d_V.find(l1_inter_l2) != d_V.end()) { + // // This case needs to be handled, currently not + // Warning() << "This might create a loop. We need to handle this case. Probably merge the two nodes?" << std::endl; + // Unhandled(); + // } + } + ++d_statistics.d_numMergeEq3; + } + + for(std::map >::iterator it = children.begin(); + it != children.end(); ++it) { + add_edges(it->first, it->second); + Node rhs; + if(it->second.size() == 1) { + rhs = nm->mkNode(kind::CARD, it->second[0]); + } else { + NodeBuilder<> nb(kind::PLUS); + BOOST_FOREACH(TNode n , it->second) { + Node card_n = nm->mkNode(kind::CARD, n); + nb << card_n; + } + rhs = Node(nb); + } + Node lem; + lem = nm->mkNode(kind::EQUAL, + nm->mkNode(kind::CARD, it->first), + rhs); + lem = nm->mkNode(kind::IMPLIES, reason, lem); + lem = Rewriter::rewrite(lem); + d_external.d_out->lemma(lem); + } + } + + Trace("sets-graph") << std::endl; + print_graph(); + Trace("sets-graph") << std::endl; + +} + +void TheorySetsPrivate::print_graph() { + std::string tag = "sets-graph"; + if(Trace.isOn("sets-graph")) { + Trace(tag) << "[sets-graph] Graph : " << std::endl; + for(typeof(d_V.begin()) it = d_V.begin(); it != d_V.end(); ++it) { + TNode v = *it; + // BOOST_FOREACH(TNode v, d_V) { + Trace(tag) << "[" << tag << "] " << v << " : "; + // BOOST_FOREACH(TNode w, d_E[v].get()) { + if(d_E.find(v) != d_E.end()) { + BOOST_FOREACH(TNode w, d_E[v].get()) { + Trace(tag) << w << ", "; + } + } else { + Trace(tag) << " leaf. " ; + } + Trace(tag) << std::endl; + } + } + + if(Trace.isOn("sets-graph-dot")) { + std::ostringstream oss; + oss << "digraph G { "; + for(typeof(d_V.begin()) it = d_V.begin(); it != d_V.end(); ++it) { + TNode v = *it; + if(d_E.find(v) != d_E.end()) { + BOOST_FOREACH(TNode w, d_E[v].get()) { + //oss << v.getId() << " -> " << w.getId() << "; "; + oss << "\"" << v << "\" -> \"" << w << "\"; "; + } + } else { + oss << "\"" << v << "\";"; + } + } + oss << "}"; + Trace("sets-graph-dot") << "[sets-graph-dot] " << oss.str() << std::endl; + } +} + +Node TheorySetsPrivate::eqSoFar() { + std::vector V(d_allSetEqualitiesSoFar.begin(), d_allSetEqualitiesSoFar.end()); + if(V.size() == 0) { + return d_trueNode; + } else if(V.size() == 1) { + return V[0]; + } else { + NodeManager* nm = NodeManager::currentNM(); + return nm->mkNode(kind::AND, V); + } +} + + +void TheorySetsPrivate::guessLeavesEmptyLemmas() { + + // Guess leaf nodes being empty or non-empty + NodeManager* nm = NodeManager::currentNM(); + leaves.clear(); + for(typeof(d_V.begin()) it = d_V.begin(); it != d_V.end(); ++it) { + TNode v = *it; + if(d_E.find(v) == d_E.end()) { + leaves.insert(v); + } + } + d_statistics.d_numLeaves.setData(leaves.size()); + d_statistics.d_numLeavesMax.maxAssign(leaves.size()); + + int + numLeaves = leaves.size(), + numLemmasGenerated = 0, + numLeavesIsEmpty = 0, + numLeavesIsNonEmpty = 0, + numLeavesCurrentlyNonEmpty = 0, + numLemmaAlreadyExisted = 0; + + for(typeof(leaves.begin()) it = leaves.begin(); it != leaves.end(); ++it) { + bool generateLemma = true; + Node emptySet = nm->mkConst(EmptySet(nm->toType((*it).getType()))); + + if(d_equalityEngine.hasTerm(*it)) { + Node n = d_equalityEngine.getRepresentative(*it); + if(n.getKind() == kind::EMPTYSET) { + ++numLeavesIsEmpty; + continue; + } + if(d_termInfoManager->getMembers(n)->size() > 0) { + ++numLeavesCurrentlyNonEmpty; + continue; + } + if(!d_equalityEngine.hasTerm(emptySet)) { + d_equalityEngine.addTerm(emptySet); + } + if(d_equalityEngine.areDisequal(n, emptySet, false)) { + ++numLeavesIsNonEmpty; + generateLemma = false; + } + } + + if(generateLemma) { + Node n = nm->mkNode(kind::EQUAL, (*it), emptySet); + Node lem = nm->mkNode(kind::OR, n, nm->mkNode(kind::NOT, n)); + bool lemmaGenerated = + lemma(lem, SETS_LEMMA_GRAPH); + if(lemmaGenerated) { + ++numLemmasGenerated; + } else { + ++numLemmaAlreadyExisted; + } + n = d_external.d_valuation.ensureLiteral(n); + d_external.d_out->requirePhase(n, true); + } + + } + Trace("sets-guess-empty") + << "[sets-guess-empty] numLeaves = " << numLeaves << std::endl + << " numLemmasGenerated = " << numLemmasGenerated << std::endl + << " numLeavesIsEmpty = " << numLeavesIsEmpty << std::endl + << " numLeavesIsNonEmpty = " << numLeavesIsNonEmpty << std::endl + << " numLeavesCurrentlyNonEmpty = " << numLeavesCurrentlyNonEmpty << std::endl + << " numLemmaAlreadyExisted = " << numLemmaAlreadyExisted << std::endl; + +} + +void TheorySetsPrivate::processCard2(Theory::Effort level) { + CodeTimer codeTimer(d_statistics.d_processCard2Time); + + if(level != Theory::EFFORT_FULL) return; + + d_statistics.d_numVertices.setData(d_V.size()); + d_statistics.d_numVerticesMax.maxAssign(d_V.size()); + + Trace("sets-card") << "[sets-card] processCard( " << level << ")" << std::endl; + Trace("sets-card") << "[sets-card] # vertices = " << d_V.size() << std::endl; + + NodeManager* nm = NodeManager::currentNM(); + + // Introduce + for(typeof(d_cardTerms.begin()) it = d_cardTerms.begin(); + it != d_cardTerms.end(); ++it) { + + for(eq::EqClassIterator j(d_equalityEngine.getRepresentative((*it)[0]), &d_equalityEngine); + !j.isFinished(); ++j) { + + Node n = nm->mkNode(kind::CARD, (*j)); + + if(d_processedCardTerms.find(n) != d_processedCardTerms.end()) { + continue; + } + + if(d_relTerms.find(n[0]) == d_relTerms.end()) { + // not relevant, skip + continue; + } + + Trace("sets-graph") << std::endl; + print_graph(); + Trace("sets-graph") << std::endl; + + add_node(n[0]); + + Trace("sets-card") << "[sets-card] Processing " << n << " in eq cl of " << (*it) << std::endl; + + d_processedCardTerms.insert(n); + + Kind k = n[0].getKind(); + + if(k == kind::SINGLETON) { + Trace("sets-card") << "[sets-card] Introduce Singleton " << n[0] << std::endl; + continue; + } + + // rest of the processing is for compound terms + if(k != kind::UNION && k != kind::INTERSECTION && k != kind::SETMINUS) { + continue; + } + + Trace("sets-card") << "[sets-card] Introduce Term " << n[0] << std::endl; + + Node s = min(n[0][0], n[0][1]); + Node t = max(n[0][0], n[0][1]); + bool isUnion = (k == kind::UNION); + Assert(Rewriter::rewrite(s) == s); + Assert(Rewriter::rewrite(t) == t); + + Node sNt = nm->mkNode(kind::INTERSECTION, s, t); + sNt = Rewriter::rewrite(sNt); + Node sMt = nm->mkNode(kind::SETMINUS, s, t); + sMt = Rewriter::rewrite(sMt); + Node tMs = nm->mkNode(kind::SETMINUS, t, s); + tMs = Rewriter::rewrite(tMs); + + Node card_s = nm->mkNode(kind::CARD, s); + Node card_t = nm->mkNode(kind::CARD, t); + Node card_sNt = nm->mkNode(kind::CARD, sNt); + Node card_sMt = nm->mkNode(kind::CARD, sMt); + Node card_tMs = nm->mkNode(kind::CARD, tMs); + + Node lem; + + add_node(sMt); + add_node(sNt); + add_node(tMs); + + + // for union + if(isUnion) { + if(d_E.find(n[0]) != d_E.end()) { + // do a merge of current leaves of d_E with + // sNT sMT tMs + Trace("sets-card") << "[sets-card] Already found in the graph, merging " << n[0] << std::endl; + merge_nodes(get_leaves(n[0]), get_leaves(sMt, sNt, tMs), eqSoFar()); + } else { + add_node(n[0]); + + lem = nm->mkNode(kind::EQUAL, + n, // card(s union t) + nm->mkNode(kind::PLUS, card_sNt, card_sMt, card_tMs)); + lemma(lem, SETS_LEMMA_GRAPH); + + Assert(d_E.find(n[0]) == d_E.end()); + add_edges(n[0], sMt, sNt, tMs); + } + } + + // for s + if(d_E.find(s) == d_E.end()) { + add_node(s); + add_edges(s, sMt, sNt); + + lem = nm->mkNode(kind::EQUAL, + card_s, + nm->mkNode(kind::PLUS, card_sNt, card_sMt)); + lemma(lem, SETS_LEMMA_GRAPH); + } else { + if(find(d_E[s].get().begin(), d_E[s].get().end(), sMt) != d_E[s].get().end()) { + Assert( find(d_E[s].get().begin(), d_E[s].get().end(), sMt) != d_E[s].get().end() ); + Assert( find(d_E[s].get().begin(), d_E[s].get().end(), sNt) != d_E[s].get().end() ); + Assert( find(d_E[t].get().begin(), d_E[t].get().end(), tMs) != d_E[t].get().end() ); + Assert( find(d_E[t].get().begin(), d_E[t].get().end(), sNt) != d_E[t].get().end() ); + continue; + } + + Trace("sets-card") << "[sets-card] Already found in the graph, merging " << s << std::endl; + merge_nodes(get_leaves(s), get_leaves(sMt, sNt), eqSoFar()); + } + + // for t + if(d_E.find(t) == d_E.end()) { + Assert(d_E.find(t) == d_E.end()); + add_node(t); + add_edges(t, sNt, tMs); + + lem = nm->mkNode(kind::EQUAL, + card_t, + nm->mkNode(kind::PLUS, card_sNt, card_tMs)); + lemma(lem, SETS_LEMMA_GRAPH); + } else { + // Assert( find(d_E[s].get().begin(), d_E[s].get().end(), sMt) == d_E[s].get().end() ); + // Assert( find(d_E[s].get().begin(), d_E[s].get().end(), sNt) == d_E[s].get().end() ); + // Assert( find(d_E[t].get().begin(), d_E[t].get().end(), tMs) == d_E[t].get().end() ); + // Assert( find(d_E[t].get().begin(), d_E[t].get().end(), sNt) == d_E[t].get().end() ); + + Trace("sets-card") << "[sets-card] Already found in the graph, merging " << t << std::endl; + merge_nodes(get_leaves(t), get_leaves(sNt, tMs), eqSoFar()); + } + + if(options::setsSlowLemmas()) { + if(d_newLemmaGenerated) { + break; + } else if(options::setsGuessEmpty() == 0) { + guessLeavesEmptyLemmas(); + if(d_newLemmaGenerated) { + return; + } + } + } + + }//equivalence class loop + + if(options::setsSlowLemmas() && d_newLemmaGenerated) { + break; + } + + }//d_cardTerms loop + + print_graph(); + + if(d_newLemmaGenerated) { + Trace("sets-card") << "[sets-card] New introduce done. Returning." << std::endl; + return; + } + + if(options::setsGuessEmpty() == 1) { + guessLeavesEmptyLemmas(); + if(d_newLemmaGenerated) { + return; + } + } + + // Merge equalities from input assertions + + while(!d_graphMergesPending.empty()) { + std::pair np = d_graphMergesPending.front(); + d_graphMergesPending.pop(); + + Debug("sets-card") << "[sets-card] Equality " << np.first << " " << np.second << std::endl; + if(np.first.getKind() == kind::EMPTYSET || np.second.getKind() == kind::EMPTYSET) { + Debug("sets-card") << "[sets-card] skipping merge as one side is empty set" << std::endl; + continue; + } + + if(d_V.find(np.first) == d_V.end() || d_V.find(np.second) == d_V.end()) { + Assert((d_V.find(np.first) == d_V.end())); + Assert((d_V.find(np.second) == d_V.end())); + continue; + } + d_allSetEqualitiesSoFar.push_back(EQUAL(np.first, np.second)); + // merge_nodes(get_leaves(np.first), get_leaves(np.second), EQUAL(np.first, np.second)); + merge_nodes(get_leaves(np.first), get_leaves(np.second), eqSoFar()); + } + + if(d_newLemmaGenerated) { + Trace("sets-card") << "[sets-card] New merge done. Returning." << std::endl; + return; + } + + leaves.clear(); + for(typeof(d_V.begin()) it = d_V.begin(); it != d_V.end(); ++it) { + TNode v = *it; + if(d_E.find(v) == d_E.end()) { + leaves.insert(v); + } + } + Trace("sets-card") << "[sets-card] # leaves = " << leaves.size() << std::endl; + d_statistics.d_numLeaves.setData(leaves.size()); + d_statistics.d_numLeavesMax.maxAssign(leaves.size()); + + Assert(!d_newLemmaGenerated); + + + if(options::setsGuessEmpty() == 2) { + guessLeavesEmptyLemmas(); + if(d_newLemmaGenerated) { + return; + } + } + + // Elements being either equal or disequal [Members Arrangement rule] + Trace("sets-card") << "[sets-card] Processing elements equality/disequal to each other" << std::endl; + for(typeof(leaves.begin()) it = leaves.begin(); + it != leaves.end(); ++it) { + if(!d_equalityEngine.hasTerm(*it)) continue; + Node n = d_equalityEngine.getRepresentative(*it); + Assert(n.getKind() == kind::EMPTYSET || leaves.find(n) != leaves.end()); + if(n != *it) continue; + const CDTNodeList* l = d_termInfoManager->getMembers(*it); + std::set elems; + for(typeof(l->begin()) l_it = l->begin(); l_it != l->end(); ++l_it) { + elems.insert(d_equalityEngine.getRepresentative(*l_it)); + } + for(typeof(elems.begin()) e1_it = elems.begin(); e1_it != elems.end(); ++e1_it) { + for(typeof(elems.begin()) e2_it = elems.begin(); e2_it != elems.end(); ++e2_it) { + if(*e1_it == *e2_it) continue; + if(!d_equalityEngine.areDisequal(*e1_it, *e2_it, false)) { + Node lem = nm->mkNode(kind::EQUAL, *e1_it, *e2_it); + lem = nm->mkNode(kind::OR, lem, nm->mkNode(kind::NOT, lem)); + lemma(lem, SETS_LEMMA_GRAPH); + } + } + } + } + + if(d_newLemmaGenerated) { + Trace("sets-card") << "[sets-card] Members arrangments lemmas. Returning." << std::endl; + return; + } + + // Assert Lower bound + Trace("sets-card") << "[sets-card] Processing assert lower bound" << std::endl; + for(typeof(leaves.begin()) it = leaves.begin(); + it != leaves.end(); ++it) { + Trace("sets-cardlower") << "[sets-cardlower] Card Lower: " << *it << std::endl; + Assert(d_equalityEngine.hasTerm(*it)); + Node n = d_equalityEngine.getRepresentative(*it); + // Node n = (*it); + // if(!d_equalityEngine.hasTerm(n)) { + // Trace("sets-cardlower") << "[sets-cardlower] not in EE" << std::endl; + // continue; + // } + // Assert(n.getKind() == kind::EMPTYSET || leaves.find(n) != leaves.end()); // ???? + // if(n != *it) continue; + const CDTNodeList* l = d_termInfoManager->getMembers(n); + std::set elems; + for(typeof(l->begin()) l_it = l->begin(); l_it != l->end(); ++l_it) { + elems.insert(d_equalityEngine.getRepresentative(*l_it)); + } + if(elems.size() == 0) continue; + NodeBuilder<> nb(kind::OR); + nb << ( nm->mkNode(kind::LEQ, nm->mkConst(Rational(elems.size())), nm->mkNode(kind::CARD, *it)) ); + if(elems.size() > 1) { + for(typeof(elems.begin()) e1_it = elems.begin(); e1_it != elems.end(); ++e1_it) { + for(typeof(elems.begin()) e2_it = elems.begin(); e2_it != elems.end(); ++e2_it) { + if(*e1_it == *e2_it) continue; + nb << (nm->mkNode(kind::EQUAL, *e1_it, *e2_it)); + } + } + } + for(typeof(elems.begin()) e_it = elems.begin(); e_it != elems.end(); ++e_it) { + nb << nm->mkNode(kind::NOT, nm->mkNode(kind::MEMBER, *e_it, *it)); + } + Node lem = Node(nb); + // if(d_cardLowerLemmaCache.find(lem) == d_cardLowerLemmaCache.end()) { + Trace("sets-card") << "[sets-card] Card Lower: " << lem << std::endl; + lemma(lem, SETS_LEMMA_GRAPH); + // d_cardLowerLemmaCache.insert(lem); + // } + } +} + + + }/* CVC4::theory::sets namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/sets/theory_sets_private.h b/src/theory/sets/theory_sets_private.h index 2be77a831..e14efd7a4 100644 --- a/src/theory/sets/theory_sets_private.h +++ b/src/theory/sets/theory_sets_private.h @@ -66,6 +66,8 @@ public: void preRegisterTerm(TNode node); + void presolve(); + void propagate(Theory::Effort); private: @@ -74,8 +76,16 @@ private: class Statistics { public: TimerStat d_getModelValueTime; + TimerStat d_mergeTime; + TimerStat d_processCard2Time; IntStat d_memberLemmas; IntStat d_disequalityLemmas; + IntStat d_numVertices; + IntStat d_numVerticesMax; + IntStat d_numMergeEq1or2; + IntStat d_numMergeEq3; + IntStat d_numLeaves; + IntStat d_numLeavesMax; Statistics(); ~Statistics(); @@ -113,6 +123,20 @@ private: /** generate and send out conflict node */ void conflict(TNode, TNode); + /** send out a lemma */ + enum SetsLemmaTag { + SETS_LEMMA_DISEQUAL, + SETS_LEMMA_MEMBER, + SETS_LEMMA_GRAPH, + SETS_LEMMA_OTHER + }; + + /** + * returns true if a lemmas was generated + * returns false otherwise (found in cache) + */ + bool lemma(Node n, SetsLemmaTag t); + class TermInfoManager { TheorySetsPrivate& d_theory; context::Context* d_context; @@ -141,6 +165,25 @@ private: }; TermInfoManager* d_termInfoManager; + /****** + * Card Vars : + * + * mapping from set terms to correpsonding cardinality variable + * + * in the ::check function, when we get one of those cardinality + * variables to be assigned to 0, we will assert in equality engine + * to be equal to empty set. + * + * if required, we will add more filters so it doesn't leak to + * outside world + */ + Node getCardVar(TNode n); + Node newCardVar(TNode n); + bool isCardVar(TNode n); + typedef std::hash_map NodeNodeHashMap; + NodeNodeHashMap d_setTermToCardVar; + NodeNodeHashMap d_cardVarToSetTerm; + /** Assertions and helper functions */ bool present(TNode atom); bool holds(TNode lit) { @@ -199,6 +242,58 @@ private: friend class TheorySetsScrutinize; TheorySetsScrutinize* d_scrutinize; void dumpAssertionsHumanified() const; /** do some formatting to make them more readable */ + + + + /***** Cardinality handling *****/ + bool d_cardEnabled; + void enableCard(); + void cardCreateEmptysetSkolem(TypeNode t); + + CDNodeSet d_cardTerms; + std::set d_typesAdded; + CDNodeSet d_processedCardTerms; + std::map, bool> d_processedCardPairs; + CDNodeSet d_cardLowerLemmaCache; + void registerCard(TNode); + void processCard(Theory::Effort level); + + /* Graph handling */ + std::map > edgesFd; + std::map > edgesBk; + std::set< std::pair > disjoint; + std::set leaves; + void buildGraph(); + + /* For calculus as in paper */ + void processCard2(Theory::Effort level); + CDNodeSet d_V; + context::CDHashMap , TNodeHashFunction > d_E; + void add_edges(TNode source, TNode dest); + void add_edges(TNode source, TNode dest1, TNode dest2); + void add_edges(TNode source, TNode dest1, TNode dest2, TNode dest3); + void add_edges(TNode source, const std::vector& dests); + void add_node(TNode vertex); + void merge_nodes(std::set a, std::set b, Node reason); + std::set get_leaves(Node vertex); + std::set get_leaves(Node vertex1, Node vertex2); + std::set get_leaves(Node vertex1, Node vertex2, Node vertex3); + std::set non_empty(std::set vertices); + void print_graph(); + context::CDQueue < std::pair > d_graphMergesPending; + context::CDList d_allSetEqualitiesSoFar; + Node eqSoFar(); + Node eqemptySoFar(); + + std::set getNonEmptyLeaves(TNode); + CDNodeSet d_lemmasGenerated; + bool d_newLemmaGenerated; + + void guessLeavesEmptyLemmas(); + + + /** relevant terms */ + CDNodeSet d_relTerms; };/* class TheorySetsPrivate */ diff --git a/src/theory/sets/theory_sets_rewriter.cpp b/src/theory/sets/theory_sets_rewriter.cpp index 360957d05..8dbca1e73 100644 --- a/src/theory/sets/theory_sets_rewriter.cpp +++ b/src/theory/sets/theory_sets_rewriter.cpp @@ -16,6 +16,8 @@ #include "theory/sets/theory_sets_rewriter.h" #include "theory/sets/normal_form.h" +#include "expr/attribute.h" +#include "options/sets_options.h" namespace CVC4 { namespace theory { @@ -24,6 +26,101 @@ namespace sets { typedef std::set Elements; typedef std::hash_map SettermElementsMap; +struct FlattenedNodeTag {}; +typedef expr::Attribute flattened; + + +/** + * flattenNode looks for children of same kind, and if found merges + * them into the parent. + * + * It simultaneously handles a couple of other optimizations: + * - trivialNode - if found during exploration, return that node itself + * (like in case of OR, if "true" is found, makes sense to replace + * whole formula with "true") + * - skipNode - as name suggests, skip them + * (like in case of OR, you may want to skip any "false" nodes found) + * + * Use a null node if you want to ignore any of the optimizations. + */ +RewriteResponse flattenNode(TNode n, TNode trivialNode, TNode skipNode) +{ + if(n.hasAttribute(flattened()) && n.getAttribute(flattened())) { + return RewriteResponse(REWRITE_DONE, n); + } + + typedef std::hash_set node_set; + + node_set visited; + visited.insert(skipNode); + + std::vector toProcess; + toProcess.push_back(n); + + Kind k = n.getKind(); + typedef std::vector ChildList; + ChildList childList; //TNode should be fine, since 'n' is still there + + Debug("sets-rewrite-flatten") << "[sets-rewrite-flatten] " << n << std::endl; + for (unsigned i = 0; i < toProcess.size(); ++ i) { + TNode current = toProcess[i]; + Debug("sets-rewrite-flatten") << "[sets-rewrite-flatten] > Processing " << current << std::endl; + for(unsigned j = 0, j_end = current.getNumChildren(); j < j_end; ++ j) { + TNode child = current[j]; + if(visited.find(child) != visited.end()) { + continue; + } else if(child == trivialNode) { + return RewriteResponse(REWRITE_DONE, trivialNode); + } else { + visited.insert(child); + if(child.getKind() == k) { + toProcess.push_back(child); + } else { + childList.push_back(child); + } + } + } + } + if (childList.size() == 0) return RewriteResponse(REWRITE_DONE, skipNode); + if (childList.size() == 1) return RewriteResponse(REWRITE_AGAIN, childList[0]); + + sort(childList.begin(), childList.end()); + + /* Make sure we are under number of children possible in a node */ + NodeManager* nodeManager = NodeManager::currentNM(); + static const unsigned MAX_CHILDREN = (1u << __CVC4__EXPR__NODE_VALUE__NBITS__NCHILDREN ) - 1; + AlwaysAssert(childList.size() < MAX_CHILDREN, "do not support formulas this big"); + + ChildList::iterator cur = childList.begin(), next, en = childList.end(); + Node ret = (*cur); + ++cur; + while( cur != en ) { + ret = nodeManager->mkNode(k, ret, *cur); + ret.setAttribute(flattened(), true); + ++cur; + } + Trace("sets-postrewrite") << "flatten Sets::postRewrite returning " << ret << std::endl; + if(ret != n) { + return RewriteResponse(REWRITE_AGAIN, ret); // again for constants + } else { + return RewriteResponse(REWRITE_DONE, ret); + } + // if (childList.size() < MAX_CHILDREN) { + // Node retNode = nodeManager->mkNode(k, childList); + // return RewriteResponse(REWRITE_DONE, retNode); + // } else { + // Assert(childList.size() < size_t(MAX_CHILDREN) * size_t(MAX_CHILDREN) ); + // NodeBuilder<> nb(k); + // ChildList::iterator cur = childList.begin(), next, en = childList.end(); + // while( cur != en ) { + // next = min(cur + MAX_CHILDREN, en); + // nb << (nodeManager->mkNode(k, ChildList(cur, next) )); + // cur = next; + // } + // return RewriteResponse(REWRITE_DONE, nb.constructNode()); + // } +} + bool checkConstantMembership(TNode elementTerm, TNode setTerm) { if(setTerm.getKind() == kind::EMPTYSET) { @@ -101,57 +198,92 @@ RewriteResponse TheorySetsRewriter::postRewrite(TNode node) { }//kind::IFF case kind::SETMINUS: { - if(node[0] == node[1]) { - Node newNode = nm->mkConst(EmptySet(nm->toType(node[0].getType()))); - Trace("sets-postrewrite") << "Sets::postRewrite returning " << newNode << std::endl; - return RewriteResponse(REWRITE_DONE, newNode); - } else if(node[0].getKind() == kind::EMPTYSET || - node[1].getKind() == kind::EMPTYSET) { - Trace("sets-postrewrite") << "Sets::postRewrite returning " << node[0] << std::endl; - return RewriteResponse(REWRITE_DONE, node[0]); - } else if(node[0].isConst() && node[1].isConst()) { - std::set left = NormalForm::getElementsFromNormalConstant(node[0]); - std::set right = NormalForm::getElementsFromNormalConstant(node[1]); - std::set newSet; - std::set_difference(left.begin(), left.end(), right.begin(), right.end(), - std::inserter(newSet, newSet.begin())); - Node newNode = NormalForm::elementsToSet(newSet, node.getType()); - Assert(newNode.isConst()); - Trace("sets-postrewrite") << "Sets::postRewrite returning " << newNode << std::endl; - return RewriteResponse(REWRITE_DONE, newNode); + if( options::setsAggRewrite() ){ + Node newNode = rewriteSet( node ); + if( newNode!=node ){ + return RewriteResponse(REWRITE_DONE, newNode); + } + }else{ + if(node[0] == node[1]) { + Node newNode = nm->mkConst(EmptySet(nm->toType(node[0].getType()))); + Trace("sets-postrewrite") << "Sets::postRewrite returning " << newNode << std::endl; + return RewriteResponse(REWRITE_DONE, newNode); + } else if(node[0].getKind() == kind::EMPTYSET || + node[1].getKind() == kind::EMPTYSET) { + Trace("sets-postrewrite") << "Sets::postRewrite returning " << node[0] << std::endl; + return RewriteResponse(REWRITE_DONE, node[0]); + } else if(node[0].isConst() && node[1].isConst()) { + std::set left = NormalForm::getElementsFromNormalConstant(node[0]); + std::set right = NormalForm::getElementsFromNormalConstant(node[1]); + std::set newSet; + std::set_difference(left.begin(), left.end(), right.begin(), right.end(), + std::inserter(newSet, newSet.begin())); + Node newNode = NormalForm::elementsToSet(newSet, node.getType()); + Assert(newNode.isConst()); + Trace("sets-postrewrite") << "Sets::postRewrite returning " << newNode << std::endl; + return RewriteResponse(REWRITE_DONE, newNode); + } } break; - }//kind::INTERSECION + }//kind::SETMINUS case kind::INTERSECTION: { - if(node[0] == node[1]) { - Trace("sets-postrewrite") << "Sets::postRewrite returning " << node[0] << std::endl; - return RewriteResponse(REWRITE_DONE, node[0]); - } else if(node[0].getKind() == kind::EMPTYSET) { - return RewriteResponse(REWRITE_DONE, node[0]); - } else if(node[1].getKind() == kind::EMPTYSET) { - return RewriteResponse(REWRITE_DONE, node[1]); - } else if(node[0].isConst() && node[1].isConst()) { - std::set left = NormalForm::getElementsFromNormalConstant(node[0]); - std::set right = NormalForm::getElementsFromNormalConstant(node[1]); - std::set newSet; - std::set_intersection(left.begin(), left.end(), right.begin(), right.end(), - std::inserter(newSet, newSet.begin())); - Node newNode = NormalForm::elementsToSet(newSet, node.getType()); - Assert(newNode.isConst()); - Trace("sets-postrewrite") << "Sets::postRewrite returning " << newNode << std::endl; - return RewriteResponse(REWRITE_DONE, newNode); - } else if (node[0] > node[1]) { - Node newNode = nm->mkNode(node.getKind(), node[1], node[0]); - Trace("sets-postrewrite") << "Sets::postRewrite returning " << newNode << std::endl; - return RewriteResponse(REWRITE_DONE, newNode); + if( options::setsAggRewrite() ){ + Node newNode = rewriteSet( node ); + if( newNode!=node ){ + return RewriteResponse(REWRITE_DONE, newNode); + } + // }else{ + // Node emptySet = nm->mkConst(EmptySet(nm->toType(node[0].getType()))); + // if(node[0].isConst() && node[1].isConst()) { + // std::set left = NormalForm::getElementsFromNormalConstant(node[0]); + // std::set right = NormalForm::getElementsFromNormalConstant(node[1]); + // std::set newSet; + // std::set_intersection(left.begin(), left.end(), right.begin(), right.end(), + // std::inserter(newSet, newSet.begin())); + // Node newNode = NormalForm::elementsToSet(newSet, node.getType()); + // Assert(newNode.isConst()); + // Trace("sets-postrewrite") << "Sets::postRewrite returning " << newNode << std::endl; + // return RewriteResponse(REWRITE_DONE, newNode); + // } else { + // return flattenNode(node, /* trivialNode = */ emptySet, /* skipNode = */ Node()); + // } + // } + }else{ + if(node[0] == node[1]) { + Trace("sets-postrewrite") << "Sets::postRewrite returning " << node[0] << std::endl; + return RewriteResponse(REWRITE_DONE, node[0]); + } else if(node[0].getKind() == kind::EMPTYSET) { + return RewriteResponse(REWRITE_DONE, node[0]); + } else if(node[1].getKind() == kind::EMPTYSET) { + return RewriteResponse(REWRITE_DONE, node[1]); + } else if(node[0].isConst() && node[1].isConst()) { + std::set left = NormalForm::getElementsFromNormalConstant(node[0]); + std::set right = NormalForm::getElementsFromNormalConstant(node[1]); + std::set newSet; + std::set_intersection(left.begin(), left.end(), right.begin(), right.end(), + std::inserter(newSet, newSet.begin())); + Node newNode = NormalForm::elementsToSet(newSet, node.getType()); + Assert(newNode.isConst()); + Trace("sets-postrewrite") << "Sets::postRewrite returning " << newNode << std::endl; + return RewriteResponse(REWRITE_DONE, newNode); + } else if (node[0] > node[1]) { + Node newNode = nm->mkNode(node.getKind(), node[1], node[0]); + Trace("sets-postrewrite") << "Sets::postRewrite returning " << newNode << std::endl; + return RewriteResponse(REWRITE_DONE, newNode); + } } break; }//kind::INTERSECION case kind::UNION: { // NOTE: case where it is CONST is taken care of at the top - if(node[0] == node[1]) { + if( options::setsAggRewrite() ){ + Node newNode = rewriteSet( node ); + if( newNode!=node ){ + return RewriteResponse(REWRITE_DONE, newNode); + } + }else if(node[0] == node[1]) { Trace("sets-postrewrite") << "Sets::postRewrite returning " << node[0] << std::endl; return RewriteResponse(REWRITE_DONE, node[0]); } else if(node[0].getKind() == kind::EMPTYSET) { @@ -168,7 +300,7 @@ RewriteResponse TheorySetsRewriter::postRewrite(TNode node) { Assert(newNode.isConst()); Trace("sets-postrewrite") << "Sets::postRewrite returning " << newNode << std::endl; return RewriteResponse(REWRITE_DONE, newNode); - } else if (node[0] > node[1]) { + }else if (node[0] > node[1]) { Node newNode = nm->mkNode(node.getKind(), node[1], node[0]); Trace("sets-postrewrite") << "Sets::postRewrite returning " << newNode << std::endl; return RewriteResponse(REWRITE_DONE, newNode); @@ -176,6 +308,13 @@ RewriteResponse TheorySetsRewriter::postRewrite(TNode node) { break; }//kind::UNION + case kind::CARD: { + if(node[0].isConst()) { + std::set elements = NormalForm::getElementsFromNormalConstant(node[0]); + return RewriteResponse(REWRITE_DONE, nm->mkConst(Rational(elements.size()))); + } + } + default: break; }//switch(node.getKind()) @@ -224,6 +363,180 @@ RewriteResponse TheorySetsRewriter::preRewrite(TNode node) { return RewriteResponse(REWRITE_DONE, node); } +Node TheorySetsRewriter::rewriteSet( Node s ) { + Trace("sets-rewrite-debug") << "Rewrite set : " << s << std::endl; + Node empSet = NodeManager::currentNM()->mkConst(EmptySet(NodeManager::currentNM()->toType(s.getType()))); + bool success; + do{ + success = false; + std::map< Node, bool > ca; + Node ss = rewriteSet( s, ca, empSet ); + if( ss!=s ){ + Assert( !ss.isNull() ); + Trace("sets-rewrite") << "Rewrite set : " << s << std::endl; + Trace("sets-rewrite") << "........got : " << ss << std::endl; + success = true; + s = ss; + } + }while( success ); + return s; +} + +Node TheorySetsRewriter::rewriteSet( Node s, std::map< Node, bool >& ca, Node empSet ) { + if( s.getKind()!=kind::UNION && s.getKind()!=kind::INTERSECTION && s.getKind()!=kind::SETMINUS ){ + std::map< Node, bool >::iterator it = ca.find( s ); + if( it==ca.end() ){ + return s; + }else if( it->second ){ + return Node::null(); + }else{ + return empSet; + } + }else{ + Trace("sets-rewrite-debug") << "Get components : " << s << std::endl; + std::map< Node, bool > c; + bool pol = s.getKind()!=kind::UNION; + if( pol ){ + //copy current components + for( std::map< Node, bool >::iterator it = ca.begin(); it != ca.end(); ++it ){ + c[it->first] = it->second; + } + } + if( collectSetComponents( s, c, pol ) ){ + if( Trace.isOn("sets-rewrite-debug") ){ + Trace("sets-rewrite-debug") << " got components : " << std::endl; + for( std::map< Node, bool >::iterator it = c.begin(); it != c.end(); ++it ){ + Trace("sets-rewrite-debug") << " " << it->first << " -> " << it->second << std::endl; + } + } + + //simplify components based on what is asserted in ca, recursively + std::map< Node, bool > nc; + if( pol ){ + //copy map + for( std::map< Node, bool >::iterator it = c.begin(); it != c.end(); ++it ){ + nc[it->first] = it->second; + } + //rewrite each new component based on current assertions + for( std::map< Node, bool >::iterator it = c.begin(); it != c.end(); ++it ){ + if( ca.find( it->first )==ca.end() ){ + nc.erase( it->first ); + Node prev = it->first; + //only rewrite positive components here + Node ss = it->second ? rewriteSet( it->first, nc, empSet ) : it->first; + if( prev!=ss ){ + Trace("sets-rewrite-debug") << " simplify component : " << prev << "..." << ss << std::endl; + } + if( ss==empSet ){ + Trace("sets-rewrite-debug") << " return singularity " << ss << std::endl; + return ss; + }else if( !ss.isNull() ){ + std::map< Node, bool >::iterator itc = nc.find( ss ); + if( itc==nc.end() ){ + nc[ss] = it->second; + }else if( it->second!=itc->second ){ + Trace("sets-rewrite-debug") << "...conflict, return empty set." << std::endl; + return empSet; + } + } + } + } + }else{ + for( std::map< Node, bool >::iterator it = c.begin(); it != c.end(); ++it ){ + Node prev = it->first; + Node ss = rewriteSet( it->first, ca, empSet ); + if( prev!=ss ){ + Trace("sets-rewrite-debug") << " simplify component : " << prev << "..." << ss << std::endl; + } + if( ss.isNull() ){ + Trace("sets-rewrite-debug") << " return singularity " << ss << std::endl; + return ss; + }else if( ss!=empSet ){ + std::map< Node, bool >::iterator itc = nc.find( ss ); + if( itc==nc.end() ){ + nc[ss] = it->second; + }else if( it->second!=itc->second ){ + Trace("sets-rewrite-debug") << "...conflict, return complete set." << std::endl; + return Node::null(); + } + } + } + } + + + //construct sorted lists of positive, negative components + std::vector< Node > comp[2]; + for( std::map< Node, bool >::iterator it = nc.begin(); it != nc.end(); ++it ){ + if( !pol || ca.find( it->first )==ca.end() ){ + comp[ ( it->second==pol ) ? 0 : 1 ].push_back( it->first ); + } + } + //construct normalized set + Node curr; + for( unsigned i=0; i<2; i++ ){ + if( comp[i].size()>1 ){ + std::sort( comp[i].begin(), comp[i].end() ); + } + if( i==0 ){ + if( comp[i].empty() ){ + Trace("sets-rewrite-debug") << "...return trivial set (no components)." << std::endl; + if( pol ){ + return Node::null(); + }else{ + return empSet; + } + }else{ + curr = comp[i][0]; + for( unsigned j=1; jmkNode( pol ? kind::INTERSECTION : kind::UNION, curr, comp[i][j] ); + } + } + }else if( i==1 ){ + if( !comp[i].empty() ){ + Assert( pol ); + Node rem = comp[i][0]; + for( unsigned j=1; jmkNode( kind::UNION, rem, comp[i][j] ); + } + curr = NodeManager::currentNM()->mkNode( kind::SETMINUS, curr, rem ); + } + } + } + Trace("sets-rewrite-debug") << "...return " << curr << std::endl; + return curr; + }else{ + if( pol ){ + Trace("sets-rewrite-debug") << "...return empty set." << std::endl; + return NodeManager::currentNM()->mkConst(EmptySet(NodeManager::currentNM()->toType(s.getType()))); + }else{ + Trace("sets-rewrite-debug") << "...return complete set." << std::endl; + return Node::null(); + } + } + } +} + +bool TheorySetsRewriter::collectSetComponents( Node n, std::map< Node, bool >& c, bool pol ) { + std::map< Node, bool >::iterator itc = c.find( n ); + if( itc!=c.end() ){ + if( itc->second!=pol ){ + return false; + } + }else{ + if( ( pol && ( n.getKind()==kind::INTERSECTION || n.getKind()==kind::SETMINUS ) ) || ( !pol && n.getKind()==kind::UNION ) ){ + for( unsigned i=0; i& c, bool pol ); + static Node rewriteSet( Node s, std::map< Node, bool >& ca, Node empSet ); + static Node rewriteSet( Node s ); public: /** diff --git a/src/theory/sets/theory_sets_type_rules.h b/src/theory/sets/theory_sets_type_rules.h index 8c5f37591..7a8d7eed4 100644 --- a/src/theory/sets/theory_sets_type_rules.h +++ b/src/theory/sets/theory_sets_type_rules.h @@ -136,6 +136,25 @@ struct EmptySetTypeRule { } };/* struct EmptySetTypeRule */ +struct CardTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + Assert(n.getKind() == kind::CARD); + TypeNode setType = n[0].getType(check); + if( check ) { + if(!setType.isSet()) { + throw TypeCheckingExceptionPrivate(n, "cardinality operates on a set, non-set object found"); + } + } + return nodeManager->integerType(); + } + + inline static bool computeIsConst(NodeManager* nodeManager, TNode n) { + Assert(n.getKind() == kind::CARD); + return false; + } +};/* struct CardTypeRule */ + struct InsertTypeRule { inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) throw (TypeCheckingExceptionPrivate, AssertionException) { @@ -164,7 +183,6 @@ struct InsertTypeRule { } };/* struct InsertTypeRule */ - struct SetsProperties { inline static Cardinality computeCardinality(TypeNode type) { Assert(type.getKind() == kind::SET_TYPE); diff --git a/src/theory/theory.cpp b/src/theory/theory.cpp index e05363bf2..7e2b6df55 100644 --- a/src/theory/theory.cpp +++ b/src/theory/theory.cpp @@ -231,7 +231,7 @@ void Theory::collectTerms(TNode n, set& termSet) const } -void Theory::computeRelevantTerms(set& termSet) const +void Theory::computeRelevantTerms(set& termSet, bool includeShared) const { // Collect all terms appearing in assertions context::CDList::const_iterator assert_it = facts_begin(), assert_it_end = facts_end(); @@ -239,6 +239,8 @@ void Theory::computeRelevantTerms(set& termSet) const collectTerms(*assert_it, termSet); } + if (!includeShared) return; + // Add terms that are shared terms context::CDList::const_iterator shared_it = shared_terms_begin(), shared_it_end = shared_terms_end(); for (; shared_it != shared_it_end; ++shared_it) { diff --git a/src/theory/theory.h b/src/theory/theory.h index a08463b24..382d4cf65 100644 --- a/src/theory/theory.h +++ b/src/theory/theory.h @@ -237,7 +237,7 @@ protected: * termSet. This is used by collectModelInfo to delimit the set of * terms that should be used when constructing a model */ - void computeRelevantTerms(std::set& termSet) const; + void computeRelevantTerms(std::set& termSet, bool includeShared = true) const; /** * Construct a Theory. diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index 11f736e83..f2231ff7a 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -347,6 +347,7 @@ void TheoryEngine::check(Theory::Effort effort) { if (theory::TheoryTraits::hasCheck && d_logicInfo.isTheoryEnabled(THEORY)) { \ theoryOf(THEORY)->check(effort); \ if (d_inConflict) { \ + Debug("conflict") << THEORY << " in conflict. " << std::endl; \ break; \ } \ } diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index e675af3ec..db94edd7c 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -297,8 +297,15 @@ class TheoryEngine { return d_engine->lemma(lemma, rule, false, removable, preprocess, sendAtoms ? d_theory : theory::THEORY_LAST, d_theory); } + /*theory::LemmaStatus preservedLemma(TNode lemma, bool removable = false, bool preprocess = false) throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException, LogicException) { + Trace("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::preservedLemma(" << lemma << ")" << std::endl; + ++ d_statistics.lemmas; + d_engine->d_outputChannelUsed = true; + return d_engine->lemma(lemma, false, removable, preprocess, d_theory); + }*/ + theory::LemmaStatus splitLemma(TNode lemma, bool removable = false) throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException) { - Trace("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::lemma(" << lemma << ")" << std::endl; + Trace("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::splitLemma(" << lemma << ")" << std::endl; ++ d_statistics.lemmas; d_engine->d_outputChannelUsed = true; return d_engine->lemma(lemma, RULE_SPLIT, false, removable, false, d_theory, d_theory); diff --git a/test/regress/regress0/sets/card-2.smt2 b/test/regress/regress0/sets/card-2.smt2 new file mode 100644 index 000000000..cb414dbef --- /dev/null +++ b/test/regress/regress0/sets/card-2.smt2 @@ -0,0 +1,10 @@ +(set-logic QF_UFLIAFS) +(declare-sort E 0) +(declare-fun s () (Set E)) +(declare-fun t () (Set E)) +(declare-fun u () (Set E)) +(assert (>= (card s) 5)) +(assert (>= (card t) 5)) +(assert (<= (card u) 6)) +(assert (= u (union s t))) +(check-sat) diff --git a/test/regress/regress0/sets/card-3.smt2 b/test/regress/regress0/sets/card-3.smt2 new file mode 100644 index 000000000..949ed3457 --- /dev/null +++ b/test/regress/regress0/sets/card-3.smt2 @@ -0,0 +1,11 @@ +(set-logic QF_UFLIAFS) +(declare-sort E 0) +(declare-fun s () (Set E)) +(declare-fun t () (Set E)) +(declare-fun u () (Set E)) +(assert (>= (card (union s t)) 8)) +(assert (>= (card (union s u)) 8)) +(assert (<= (card (union t u)) 5)) +(assert (<= (card s) 5)) +(assert (= (as emptyset (Set E)) (intersection t u))) +(check-sat) diff --git a/test/regress/regress0/sets/card-4.smt2 b/test/regress/regress0/sets/card-4.smt2 new file mode 100644 index 000000000..ea7fe42f3 --- /dev/null +++ b/test/regress/regress0/sets/card-4.smt2 @@ -0,0 +1,23 @@ +(set-logic QF_UFLIAFS) +(declare-sort E 0) +(declare-fun s () (Set E)) +(declare-fun t () (Set E)) +(declare-fun u () (Set E)) +(assert (>= (card (union s t)) 8)) +(assert (>= (card (union s u)) 8)) +;(assert (<= (card (union t u)) 5)) +(assert (<= (card s) 5)) +(assert (= (as emptyset (Set E)) (intersection t u))) +(declare-fun x1 () E) +(declare-fun x2 () E) +(declare-fun x3 () E) +(declare-fun x4 () E) +(declare-fun x5 () E) +(declare-fun x6 () E) +(assert (member x1 s)) +(assert (member x2 s)) +(assert (member x3 s)) +(assert (member x4 s)) +(assert (member x5 s)) +(assert (member x6 s)) +(check-sat) diff --git a/test/regress/regress0/sets/card-5.smt2 b/test/regress/regress0/sets/card-5.smt2 new file mode 100644 index 000000000..65135e7b4 --- /dev/null +++ b/test/regress/regress0/sets/card-5.smt2 @@ -0,0 +1,24 @@ +(set-logic QF_UFLIAFS) +(declare-sort E 0) +(declare-fun s () (Set E)) +(declare-fun t () (Set E)) +(declare-fun u () (Set E)) +(assert (>= (card (union s t)) 8)) +(assert (>= (card (union s u)) 8)) +;(assert (<= (card (union t u)) 5)) +(assert (<= (card s) 5)) +(assert (= (as emptyset (Set E)) (intersection t u))) +(declare-fun x1 () E) +(declare-fun x2 () E) +(declare-fun x3 () E) +(declare-fun x4 () E) +(declare-fun x5 () E) +(declare-fun x6 () E) +(assert (member x1 s)) +(assert (member x2 s)) +(assert (member x3 s)) +(assert (member x4 s)) +(assert (member x5 s)) +(assert (member x6 s)) +(assert (distinct x1 x2 x3 x4 x5 x6)) +(check-sat) diff --git a/test/regress/regress0/sets/card-6.smt2 b/test/regress/regress0/sets/card-6.smt2 new file mode 100644 index 000000000..1611952b7 --- /dev/null +++ b/test/regress/regress0/sets/card-6.smt2 @@ -0,0 +1,16 @@ +(set-logic QF_UFLIAFS) +(declare-sort E 0) +(declare-fun A () (Set E)) +(declare-fun B () (Set E)) +(declare-fun C () (Set E)) +(assert + (and + (= (as emptyset (Set E)) + (intersection A B)) + (subset C (union A B)) + (>= (card C) 5) + (<= (card A) 2) + (<= (card B) 2) + ) +) +(check-sat) diff --git a/test/regress/regress0/sets/card-7.smt2 b/test/regress/regress0/sets/card-7.smt2 new file mode 100644 index 000000000..94468dc57 --- /dev/null +++ b/test/regress/regress0/sets/card-7.smt2 @@ -0,0 +1,46 @@ +(set-logic QF_UFLIAFS) +(declare-sort E 0) +(declare-fun A1 () (Set E)) +(declare-fun A2 () (Set E)) +(declare-fun A3 () (Set E)) +(declare-fun A4 () (Set E)) +(declare-fun A5 () (Set E)) +(declare-fun A6 () (Set E)) +(declare-fun A7 () (Set E)) +(declare-fun A8 () (Set E)) +(declare-fun A9 () (Set E)) +(declare-fun A10 () (Set E)) +(declare-fun A11 () (Set E)) +(declare-fun A12 () (Set E)) +(declare-fun A13 () (Set E)) +(declare-fun A14 () (Set E)) +(declare-fun A15 () (Set E)) +(declare-fun A16 () (Set E)) +(declare-fun A17 () (Set E)) +(declare-fun A18 () (Set E)) +(declare-fun A19 () (Set E)) +(declare-fun A20 () (Set E)) +(assert (and + (= (card A1) 1) + (= (card A2) 1) + (= (card A3) 1) + (= (card A4) 1) + (= (card A5) 1) + (= (card A6) 1) + (= (card A7) 1) + (= (card A8) 1) + (= (card A9) 1) + (= (card A10) 1) + (= (card A11) 1) + (= (card A12) 1) + (= (card A13) 1) + (= (card A14) 1) + (= (card A15) 1) + (= (card A16) 1) + (= (card A17) 1) + (= (card A18) 1) + (= (card A19) 1) + (= (card A20) 1) +)) +(assert (= 20 (+ (card A1) (card A2) (card A3) (card A4) (card A5) (card A6) (card A7) (card A8) (card A9) (card A10) (card A11) (card A12) (card A13) (card A14) (card A15) (card A16) (card A17) (card A18) (card A19) (card A20)))) +(check-sat) diff --git a/test/regress/regress0/sets/card.smt2 b/test/regress/regress0/sets/card.smt2 new file mode 100644 index 000000000..6b8c536d5 --- /dev/null +++ b/test/regress/regress0/sets/card.smt2 @@ -0,0 +1,8 @@ +(set-logic QF_UFLIAFS) +(declare-sort E 0) +(declare-fun s () (Set E)) +(declare-fun t () (Set E)) +(assert (>= (card s) 5)) +(assert (>= (card t) 5)) +(assert (<= (card (union s t)) 4)) +(check-sat) -- 2.30.2