This PR adds the predicate bag.member to be analogous to predicate set.member.
The PR is motivated by converting regressions for sets to bags, which avoids defining a predicate for each set type
(define-fun bag.member ((e E) (B (Bag E))) Bool (>= (bag.count e B) 1))
{BAG_DIFFERENCE_REMOVE, cvc5::Kind::BAG_DIFFERENCE_REMOVE},
{BAG_SUBBAG, cvc5::Kind::BAG_SUBBAG},
{BAG_COUNT, cvc5::Kind::BAG_COUNT},
+ {BAG_MEMBER, cvc5::Kind::BAG_MEMBER},
{BAG_DUPLICATE_REMOVAL, cvc5::Kind::BAG_DUPLICATE_REMOVAL},
{BAG_MAKE, cvc5::Kind::BAG_MAKE},
{BAG_EMPTY, cvc5::Kind::BAG_EMPTY},
{cvc5::Kind::BAG_DIFFERENCE_REMOVE, BAG_DIFFERENCE_REMOVE},
{cvc5::Kind::BAG_SUBBAG, BAG_SUBBAG},
{cvc5::Kind::BAG_COUNT, BAG_COUNT},
+ {cvc5::Kind::BAG_MEMBER, BAG_MEMBER},
{cvc5::Kind::BAG_DUPLICATE_REMOVAL, BAG_DUPLICATE_REMOVAL},
{cvc5::Kind::BAG_MAKE, BAG_MAKE},
{cvc5::Kind::BAG_EMPTY, BAG_EMPTY},
* - `Solver::mkTerm(Kind kind, const std::vector<Term>& children) const`
*/
BAG_COUNT,
+ /**
+ * Bag membership predicate.
+ *
+ * Parameters:
+ * - 1..2: Terms of bag sort (Bag E), is [1] of type E an element of [2]
+ *
+ * Create with:
+ * - `Solver::mkTerm(Kind kind, const Term& child1, const Term& child2) const`
+ * - `Solver::mkTerm(Kind kind, const std::vector<Term>& children) const`
+ */
+ BAG_MEMBER,
/**
* Eliminate duplicates in a given bag. The returned bag contains exactly the
* same elements in the given bag, but with multiplicity one.
addOperator(api::BAG_DIFFERENCE_REMOVE, "bag.difference_remove");
addOperator(api::BAG_SUBBAG, "bag.subbag");
addOperator(api::BAG_COUNT, "bag.count");
+ addOperator(api::BAG_MEMBER, "bag.member");
addOperator(api::BAG_DUPLICATE_REMOVAL, "bag.duplicate_removal");
addOperator(api::BAG_MAKE, "bag");
addOperator(api::BAG_CARD, "bag.card");
case kind::BAG_DIFFERENCE_REMOVE: return "bag.difference_remove";
case kind::BAG_SUBBAG: return "bag.subbag";
case kind::BAG_COUNT: return "bag.count";
+ case kind::BAG_MEMBER: return "bag.member";
case kind::BAG_DUPLICATE_REMOVAL: return "bag.duplicate_removal";
case kind::BAG_MAKE: return "bag";
case kind::BAG_CARD: return "bag.card";
{
case EQUAL: response = preRewriteEqual(n); break;
case BAG_SUBBAG: response = rewriteSubBag(n); break;
+ case BAG_MEMBER: response = rewriteMember(n); break;
default: response = BagsRewriteResponse(n, Rewrite::NONE);
}
return BagsRewriteResponse(equal, Rewrite::SUB_BAG);
}
+BagsRewriteResponse BagsRewriter::rewriteMember(const TNode& n) const
+{
+ Assert(n.getKind() == BAG_MEMBER);
+
+ // - (bag.member x A) = (>= (bag.count x A) 1)
+ Node count = d_nm->mkNode(BAG_COUNT, n[0], n[1]);
+ Node geq = d_nm->mkNode(GEQ, count, d_one);
+ return BagsRewriteResponse(geq, Rewrite::MEMBER);
+}
+
BagsRewriteResponse BagsRewriter::rewriteMakeBag(const TNode& n) const
{
Assert(n.getKind() == BAG_MAKE);
*/
RewriteResponse postRewrite(TNode n) override;
/**
- * preRewrite nodes with kinds: EQUAL, BGA_SUBBAG.
+ * preRewrite nodes with kinds: EQUAL, BAG_SUBBAG, BAG_MEMBER.
* See the rewrite rules for these kinds below.
*/
RewriteResponse preRewrite(TNode n) override;
*/
BagsRewriteResponse rewriteSubBag(const TNode& n) const;
+ /**
+ * rewrites for n include:
+ * - (bag.member x A) = (>= (bag.count x A) 1)
+ */
+ BagsRewriteResponse rewriteMember(const TNode& n) const;
+
/**
* rewrites for n include:
* - (bag x 0) = (bag.empty T) where T is the type of x
operator BAG_SUBBAG 2 "inclusion predicate for bags (less than or equal multiplicities)"
operator BAG_COUNT 2 "multiplicity of an element in a bag"
+operator BAG_MEMBER 2 "bag membership predicate; is first parameter a member of second?"
operator BAG_DUPLICATE_REMOVAL 1 "eliminate duplicates in a bag (also known as the delta operator,or the squash operator)"
constant BAG_MAKE_OP \
typerule BAG_DIFFERENCE_REMOVE ::cvc5::theory::bags::BinaryOperatorTypeRule
typerule BAG_SUBBAG ::cvc5::theory::bags::SubBagTypeRule
typerule BAG_COUNT ::cvc5::theory::bags::CountTypeRule
+typerule BAG_MEMBER ::cvc5::theory::bags::MemberTypeRule
typerule BAG_DUPLICATE_REMOVAL ::cvc5::theory::bags::DuplicateRemovalTypeRule
typerule BAG_MAKE_OP "SimpleTypeRule<RBuiltinOperator>"
typerule BAG_MAKE ::cvc5::theory::bags::BagMakeTypeRule
switch (r)
{
case Rewrite::NONE: return "NONE";
+ case Rewrite::BAG_MAKE_COUNT_NEGATIVE: return "BAG_MAKE_COUNT_NEGATIVE";
case Rewrite::CARD_DISJOINT: return "CARD_DISJOINT";
case Rewrite::CARD_BAG_MAKE: return "CARD_BAG_MAKE";
case Rewrite::CHOOSE_BAG_MAKE: return "CHOOSE_BAG_MAKE";
case Rewrite::MAP_CONST: return "MAP_CONST";
case Rewrite::MAP_BAG_MAKE: return "MAP_BAG_MAKE";
case Rewrite::MAP_UNION_DISJOINT: return "MAP_UNION_DISJOINT";
- case Rewrite::BAG_MAKE_COUNT_NEGATIVE: return "BAG_MAKE_COUNT_NEGATIVE";
+ case Rewrite::MEMBER: return "MEMBER";
case Rewrite::REMOVE_FROM_UNION: return "REMOVE_FROM_UNION";
case Rewrite::REMOVE_MIN: return "REMOVE_MIN";
case Rewrite::REMOVE_RETURN_LEFT: return "REMOVE_RETURN_LEFT";
enum class Rewrite : uint32_t
{
NONE, // no rewrite happened
+ BAG_MAKE_COUNT_NEGATIVE,
CARD_DISJOINT,
CARD_BAG_MAKE,
CHOOSE_BAG_MAKE,
MAP_CONST,
MAP_BAG_MAKE,
MAP_UNION_DISJOINT,
- BAG_MAKE_COUNT_NEGATIVE,
+ MEMBER,
REMOVE_FROM_UNION,
REMOVE_MIN,
REMOVE_RETURN_LEFT,
return nodeManager->integerType();
}
+TypeNode MemberTypeRule::computeType(NodeManager* nodeManager,
+ TNode n,
+ bool check)
+{
+ Assert(n.getKind() == kind::BAG_MEMBER);
+ TypeNode bagType = n[1].getType(check);
+ if (check)
+ {
+ if (!bagType.isBag())
+ {
+ throw TypeCheckingExceptionPrivate(
+ n, "checking for membership in a non-bag");
+ }
+ TypeNode elementType = n[0].getType(check);
+ // e.g. (bag.member 1 (bag 1.0 1)) is true whereas
+ // (bag.member 1.0 (bag 1 1)) throws a typing error
+ if (!elementType.isSubtypeOf(bagType.getBagElementType()))
+ {
+ std::stringstream ss;
+ ss << "member operating on bags of different types:\n"
+ << "child type: " << elementType << "\n"
+ << "not subtype: " << bagType.getBagElementType() << "\n"
+ << "in term : " << n;
+ throw TypeCheckingExceptionPrivate(n, ss.str());
+ }
+ }
+ return nodeManager->booleanType();
+}
+
TypeNode DuplicateRemovalTypeRule::computeType(NodeManager* nodeManager,
TNode n,
bool check)
static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check);
}; /* struct CountTypeRule */
+/**
+ * Type rule for binary operator bag.member to check the sort of the first
+ * argument matches the element sort of the given bag.
+ */
+struct MemberTypeRule
+{
+ static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check);
+};
+
/**
* Type rule for bag.duplicate_removal to check the argument is of a bag.
*/
regress1/bug681.smt2
regress1/bug694-Unapply1.scala-0.smt2
regress1/bug800.smt2
+ regress1/bags/bag_member.smt2
regress1/bags/bags-of-bags-subtypes.smt2
regress1/bags/card1.smt2
regress1/bags/card2.smt2
--- /dev/null
+(set-logic ALL)
+(set-info :status sat)
+(declare-fun B () (Bag String))
+(assert (bag.member "x" B))
+(check-sat)