cardinality operation for finite sets (based on my thesis / ijcar16 paper)
authorKshitij Bansal <kshitij@cs.nyu.edu>
Tue, 3 Nov 2015 09:47:30 +0000 (04:47 -0500)
committerKshitij Bansal <kshitij@cs.nyu.edu>
Sat, 9 Apr 2016 18:35:28 +0000 (14:35 -0400)
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
* ...

30 files changed:
src/lib/clock_gettime.c
src/options/options_public_functions.cpp
src/options/sets_options
src/parser/cvc/Cvc.g
src/parser/smt2/smt2.cpp
src/printer/cvc/cvc_printer.cpp
src/printer/smt2/smt2_printer.cpp
src/smt/smt_engine_check_proof.cpp
src/theory/output_channel.h
src/theory/sets/card_unused_implementation.cpp [new file with mode: 0644]
src/theory/sets/expr_patterns.h
src/theory/sets/kinds
src/theory/sets/theory_sets.cpp
src/theory/sets/theory_sets.h
src/theory/sets/theory_sets_private.cpp
src/theory/sets/theory_sets_private.h
src/theory/sets/theory_sets_rewriter.cpp
src/theory/sets/theory_sets_rewriter.h
src/theory/sets/theory_sets_type_rules.h
src/theory/theory.cpp
src/theory/theory.h
src/theory/theory_engine.cpp
src/theory/theory_engine.h
test/regress/regress0/sets/card-2.smt2 [new file with mode: 0644]
test/regress/regress0/sets/card-3.smt2 [new file with mode: 0644]
test/regress/regress0/sets/card-4.smt2 [new file with mode: 0644]
test/regress/regress0/sets/card-5.smt2 [new file with mode: 0644]
test/regress/regress0/sets/card-6.smt2 [new file with mode: 0644]
test/regress/regress0/sets/card-7.smt2 [new file with mode: 0644]
test/regress/regress0/sets/card.smt2 [new file with mode: 0644]

index d9143a14fa3aa23f134fd4e3a9142a1bd407fb0a..9bd4eafe89f08608bdde239d56290f61e7195c0d 100644 (file)
@@ -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"
 
index 0ee9cc21a573778a91cdb60478688d68712503f6..8f3a63175fd534b32aae4d595b8c93e7985529cf 100644 (file)
@@ -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];
 }
 
index 67bed5fe785b82098aa6b3e4ca1f1660b2a5af39..be945e4c9e507124d4bb0af65f7959d4fe2cf4ea 100644 (file)
@@ -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
index 4a8f7eb7b6607d775a933ebcda49fd74911399b2..470fc5253c504598def4a6f7d76f408bea484c61 100644 (file)
@@ -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]
index 55fb4f60d4fdf3247ba0ac76c750f41b14b1c2d2..ebad905832e68a9bcbc4963412b143883f3df93c 100644 (file)
@@ -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:
index 55e19510b3ce14f32456921dfbe6ed0f0bc6f69d..bc59e37ba9b1aeb0d15bea49465dd17263107199 100644 (file)
@@ -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:
index 62153571cdbf4236a9a004c23eb898ac3d32a9e3..4defc7691998472cf66457e8111ee748cc05802b 100644 (file)
@@ -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());
index a58102d86a63bdda2effdb1eb9c67cdd904bc594..5634a4651b52eda36ead39aaa9317699f2fda4af 100644 (file)
@@ -22,7 +22,7 @@
 #include <fstream>
 #include <string>
 
-#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"
index da0b92ae8dce001225be6312cfd73aff62fca28b..639793c7fa9e4a44d0a8db2d4e2d1519f0c3e994 100644 (file)
@@ -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 (file)
index 0000000..488ee10
--- /dev/null
@@ -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>(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<TNode> l1_ancestors = getReachable(edgesBk, l1);
+//       std::set<TNode> 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<TNode> 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>(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<TNode> 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;
+//     }
+//   }  
+// }
+
index 768797ecff3a60b07a49f1f76632a4e611d329bc..32e77d8b8a0ea45fe96815f92efc31948ef984ea 100644 (file)
@@ -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 */
index a43902b1ba71ebabbd89d7be343126854404cba6..14c87a947c6be49f52124071ac69732932a3f376 100644 (file)
@@ -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
index 36265134faa041d35e91644b14b039e5e873666f..bdbc964c6bafc683874fbdc392334c1eb5bb0a4d 100644 (file)
@@ -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);
 }
index d5a9657990e4da0067058f18655f7663540845d5..bbeaf4a4cb51982ea342eda89f422cc7231166bf 100644 (file)
@@ -63,6 +63,8 @@ public:
 
   void preRegisterTerm(TNode node);
 
+  void presolve();
+
   void propagate(Effort);
 
   void setMasterEqualityEngine(eq::EqualityEngine* eq);
index dbdec0cdca2477f2ee705c2ba71cdc0d96388bd3..a16e857dd550ab59484a93c14700ed2cdbf48449 100644 (file)
@@ -14,6 +14,7 @@
  ** Sets theory implementation.
  **/
 
+#include <algorithm>
 #include "theory/sets/theory_sets_private.h"
 
 #include <boost/foreach.hpp>
@@ -36,12 +37,15 @@ namespace sets {
 
 const char* element_of_str = " \u2208 ";
 
+// Declaration of functions defined later in this CPP file
+const std::set<TNode> getLeaves(map<TNode, set<TNode> >& 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>(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<Node> 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<Node> 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<TNode> explanation;
-      d_equalityEngine.explainPredicate(n, true, explanation);
       Debug("sets-model-details")
         << "[sets-model-details]  >  node: " << n << ", explanation:" << std::endl;
+      vector<TNode> 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<TNode> 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<TNode, std::vector<TNode>, 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<Rational>();
+    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<TNode>& 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<TNode> 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<TNode>& 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<bool>(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>(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<TNode> getReachable(map<TNode, set<TNode> >& edges, TNode node) {
+  Debug("sets-getreachable-debug") << "[sets-getreachable-debug] " << node << ":" << std::endl;
+  queue<TNode> Q;
+  std::set<TNode> 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<TNode>::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<TNode> getLeaves(map<TNode, set<TNode> >& edges, TNode node) {
+  Debug("sets-getreachable-debug") << "[sets-getreachable-debug] " << node << ":" << std::endl;
+  queue<TNode> Q;
+  std::set<TNode> ret;
+  std::set<TNode> 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<TNode>::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<int> a, vector<int> b)
+ * get non empty leaves
+ * of a & b, for each internal node, there will be two parent nodes
+ *
+ * Introduce
+ *   <If a node already exists, merge with it>
+ */
+
+void TheorySetsPrivate::add_edges(TNode source, TNode dest) {
+  vector<TNode> V;
+  V.push_back(dest);
+  add_edges(source, V);
+}
+
+void TheorySetsPrivate::add_edges(TNode source, TNode dest1, TNode dest2) {
+  vector<TNode> 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<TNode> 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<TNode>& 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<TNode> TheorySetsPrivate::non_empty(std::set<TNode> vertices)
+{
+  std::set<TNode> ret;
+  NodeManager* nm = NodeManager::currentNM();
+  BOOST_FOREACH(TNode vertex, vertices) {
+    Node emptySet = nm->mkConst<EmptySet>(EmptySet(nm->toType(vertex.getType())));
+    if(!d_equalityEngine.areEqual(vertex, emptySet)) {
+      ret.insert(vertex);
+    }
+  }
+  return ret;
+}
+
+std::set<TNode> TheorySetsPrivate::get_leaves(Node vertex) {
+  Debug("sets-graph-details") << "[sets-graph-details] get_leaves " << vertex << std::endl;
+  std::set<TNode> 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<TNode> s = get_leaves(v);
+      a.insert(s.begin(), s.end());
+    }
+  } else {
+    a.insert(vertex);
+  }
+  // a = non_empty(a);
+  return a;
+}
+
+std::set<TNode> TheorySetsPrivate::get_leaves(Node vertex1, Node vertex2) {
+  std::set<TNode> s = get_leaves(vertex1);
+  std::set<TNode> t = get_leaves(vertex2);
+  t.insert(s.begin(), s.end());
+  return t;
+}
+
+std::set<TNode> TheorySetsPrivate::get_leaves(Node vertex1, Node vertex2, Node vertex3) {
+  std::set<TNode> s = get_leaves(vertex1);
+  std::set<TNode> t = get_leaves(vertex2);
+  std::set<TNode> u = get_leaves(vertex3);
+  t.insert(s.begin(), s.end());
+  t.insert(u.begin(), u.end());
+  return t;
+}
+
+Node TheorySetsPrivate::eqemptySoFar() {
+  std::vector<Node> 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<TNode> leaves1, std::set<TNode> leaves2, Node reason) {
+  CodeTimer codeTimer(d_statistics.d_mergeTime);
+
+  NodeManager* nm = NodeManager::currentNM();
+
+  // do non-empty reasoning stuff
+  std::vector<TNode> leaves1_nonempty, leaves2_nonempty;
+  BOOST_FOREACH(TNode l, leaves1) {
+    Node emptySet = nm->mkConst<EmptySet>(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>(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<TNode> 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<TNode, vector<TNode> > 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<TNode, vector<TNode> >::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<Node> 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>(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<TNode,TNode> 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<TNode> 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<TNode> 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 */
index 2be77a831166eb8bec670adb681c7adbc766c52f..e14efd7a4b19cee7c80b40da5f9044c34e19b2b7 100644 (file)
@@ -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 <Node, Node, NodeHashFunction> 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<TypeNode> d_typesAdded;
+  CDNodeSet d_processedCardTerms;
+  std::map<std::pair<Node, Node>, bool> d_processedCardPairs;
+  CDNodeSet d_cardLowerLemmaCache;
+  void registerCard(TNode);
+  void processCard(Theory::Effort level);
+
+  /* Graph handling */
+  std::map<TNode, std::set<TNode> > edgesFd;
+  std::map<TNode, std::set<TNode> > edgesBk;
+  std::set< std::pair<TNode, TNode> > disjoint;
+  std::set<TNode> leaves;
+  void buildGraph();
+
+  /* For calculus as in paper */
+  void processCard2(Theory::Effort level);
+  CDNodeSet d_V;
+  context::CDHashMap <TNode, std::vector<TNode>, 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<TNode>& dests);
+  void add_node(TNode vertex);
+  void merge_nodes(std::set<TNode> a, std::set<TNode> b, Node reason);
+  std::set<TNode> get_leaves(Node vertex);
+  std::set<TNode> get_leaves(Node vertex1, Node vertex2);
+  std::set<TNode> get_leaves(Node vertex1, Node vertex2, Node vertex3);
+  std::set<TNode> non_empty(std::set<TNode> vertices);
+  void print_graph();
+  context::CDQueue < std::pair<TNode, TNode> > d_graphMergesPending;
+  context::CDList<Node> d_allSetEqualitiesSoFar;
+  Node eqSoFar();
+  Node eqemptySoFar();
+
+  std::set<TNode> getNonEmptyLeaves(TNode);
+  CDNodeSet d_lemmasGenerated;
+  bool d_newLemmaGenerated;
+
+  void guessLeavesEmptyLemmas();
+
+
+  /** relevant terms */
+  CDNodeSet d_relTerms;
 };/* class TheorySetsPrivate */
 
 
index 360957d0546518b26dd442b6e7033b6b9c18a695..8dbca1e7369a51aad702fb534d20bd960816a363 100644 (file)
@@ -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<TNode> Elements;
 typedef std::hash_map<TNode, Elements, TNodeHashFunction> SettermElementsMap;
 
+struct FlattenedNodeTag {};
+typedef expr::Attribute<FlattenedNodeTag, bool> 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<TNode, TNodeHashFunction> node_set;
+
+  node_set visited;
+  visited.insert(skipNode);
+
+  std::vector<TNode> toProcess;
+  toProcess.push_back(n);
+
+  Kind k = n.getKind();
+  typedef std::vector<TNode> 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<Node> left = NormalForm::getElementsFromNormalConstant(node[0]);
-      std::set<Node> right = NormalForm::getElementsFromNormalConstant(node[1]);
-      std::set<Node> 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<Node> left = NormalForm::getElementsFromNormalConstant(node[0]);
+        std::set<Node> right = NormalForm::getElementsFromNormalConstant(node[1]);
+        std::set<Node> 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<Node> left = NormalForm::getElementsFromNormalConstant(node[0]);
-      std::set<Node> right = NormalForm::getElementsFromNormalConstant(node[1]);
-      std::set<Node> 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<Node> left = NormalForm::getElementsFromNormalConstant(node[0]);
+    //     std::set<Node> right = NormalForm::getElementsFromNormalConstant(node[1]);
+    //     std::set<Node> 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<Node> left = NormalForm::getElementsFromNormalConstant(node[0]);
+        std::set<Node> right = NormalForm::getElementsFromNormalConstant(node[1]);
+        std::set<Node> 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<Node> 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; j<comp[i].size(); j++ ){
+              curr = NodeManager::currentNM()->mkNode( 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; j<comp[i].size(); j++ ){
+              rem = NodeManager::currentNM()->mkNode( 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<n.getNumChildren(); i++ ){
+        bool newPol = ( i==1 && n.getKind()==kind::SETMINUS ) ? !pol : pol;
+        if( !collectSetComponents( n[i], c, newPol ) ){
+          return false;
+        }
+      }
+    }else{
+      c[n] = pol;
+    }
+  }
+  return true;
+}
+
 }/* CVC4::theory::sets namespace */
 }/* CVC4::theory namespace */
 }/* CVC4 namespace */
index 50128b63d59daa601841e1400beadd2cc3a09a4c..97c89520aa0dc3bce9d91aa67a5d36c771c7f9fb 100644 (file)
@@ -26,6 +26,10 @@ namespace theory {
 namespace sets {
 
 class TheorySetsRewriter {
+private:
+  static bool collectSetComponents( Node n, std::map< Node, bool >& c, bool pol );
+  static Node rewriteSet( Node s, std::map< Node, bool >& ca, Node empSet );
+  static Node rewriteSet( Node s );
 public:
 
   /**
index 8c5f375916978741b4798a318b5acc5401450af4..7a8d7eed4168e865db517a8975c42736c1f7e04c 100644 (file)
@@ -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);
index e05363bf25653ee3c6c348543af6837c38b7349b..7e2b6df55360274566bd13d8f4999c5dc8f38e98 100644 (file)
@@ -231,7 +231,7 @@ void Theory::collectTerms(TNode n, set<Node>& termSet) const
 }
 
 
-void Theory::computeRelevantTerms(set<Node>& termSet) const
+void Theory::computeRelevantTerms(set<Node>& termSet, bool includeShared) const
 {
   // Collect all terms appearing in assertions
   context::CDList<Assertion>::const_iterator assert_it = facts_begin(), assert_it_end = facts_end();
@@ -239,6 +239,8 @@ void Theory::computeRelevantTerms(set<Node>& termSet) const
     collectTerms(*assert_it, termSet);
   }
 
+  if (!includeShared) return;
+
   // Add terms that are shared terms
   context::CDList<TNode>::const_iterator shared_it = shared_terms_begin(), shared_it_end = shared_terms_end();
   for (; shared_it != shared_it_end; ++shared_it) {
index a08463b2405d661d7c07eda819abe26d61a6fdba..382d4cf65441c8d61a6c1ea94ceea93191c33a9a 100644 (file)
@@ -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<Node>& termSet) const;
+  void computeRelevantTerms(std::set<Node>& termSet, bool includeShared = true) const;
 
   /**
    * Construct a Theory.
index 11f736e83fc62860d9430cc22f5ed4295241f84b..f2231ff7a01e4c214c7a47c343b8b06aab0b0687 100644 (file)
@@ -347,6 +347,7 @@ void TheoryEngine::check(Theory::Effort effort) {
     if (theory::TheoryTraits<THEORY>::hasCheck && d_logicInfo.isTheoryEnabled(THEORY)) { \
        theoryOf(THEORY)->check(effort); \
        if (d_inConflict) { \
+         Debug("conflict") << THEORY << " in conflict. " << std::endl; \
          break; \
        } \
     }
index e675af3ecee6f7244ce22f18b7908270d0bd9fa3..db94edd7c68141a008d019e7038cb400c2ed22a7 100644 (file)
@@ -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 (file)
index 0000000..cb414db
--- /dev/null
@@ -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 (file)
index 0000000..949ed34
--- /dev/null
@@ -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 (file)
index 0000000..ea7fe42
--- /dev/null
@@ -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 (file)
index 0000000..65135e7
--- /dev/null
@@ -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 (file)
index 0000000..1611952
--- /dev/null
@@ -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 (file)
index 0000000..94468dc
--- /dev/null
@@ -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 (file)
index 0000000..6b8c536
--- /dev/null
@@ -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)