In [1]:

```
import proveit
from proveit import ExprRange, InstantiationFailure, ProofFailure, used_vars, free_vars
from proveit import a, b, c, d, e, i, l, m, n, x, y
from proveit import A, B, C, D, E
from proveit.logic import Boolean, TRUE, FALSE
from proveit.logic import (Equals, Forall, InSet, NotEquals, NotInSet,
Or, ProperSubset, Set, SubsetEq)
from proveit.numbers import zero, one, two, three, four, five, six, seven, Add
%begin demonstrations
```

Introduction

Simple Expressions involving the enumerated Set class

Common Attributes of an enumerated Set

Axioms

Theorems & Conjectures

Further Demonstrations

Simple Expressions involving the enumerated Set class

Common Attributes of an enumerated Set

Axioms

Theorems & Conjectures

Further Demonstrations

- Deducing Set memberships such as $\vdash b\in\{a, b, c\}$, $\vdash 1\in\{1, 2, 3\}$, and $\vdash \{a, b\}\in\{\{\}, \{a\}, \{b\}, \{a, b\}\}$
- Deducing Set non-memberships such as $\vdash 4\notin\{1, 2, 3\}$ and $\vdash d\notin\{a, b, c\}$
- Deducing subset relationships between enumerated Sets: $\vdash\{2, 4, 6\}\subseteq\{1, 2, 3, 4, 5, 6, 7\}$ and $\vdash\{1, 3, 5, 1, 5\}\subseteq\{1, 2, 3, 4, 5, 6, 7\}$
- Instantiating the proper_subset_of_superset conjecture to deduce $\{1, 2, 3\}\subset\{1, 2, 3, 4, 5, 6\}$
- Deducing that $\{2, 4, 6\}\subset\{1, 2, 3, 4, 5, 6\}$ and $\{c, b, a\} \subset \{a, b, c, d, e\}$
- Deducing that $\{1, a, 1\}\subset\{1, a, 1, b, 2, a\}$ when $a = 1$

Finite, explicitly enumerated sets (*i.e.*, sets defined by an explicit listing of elements) are ubiquitous in logic and mathematics, and regularly arise in theorems and proofs. For example, one might need to consider elements in the Boolean set: $\mathbb{B}=\{\mathtt{TRUE}, \mathtt{FALSE}\} = \{\top, \bot\}$ or the integer equivalents $\{1, 0\}$, or perhaps a set of variables $\{a, b, c\}$ or that set's power set $\{\{\}, \{a\}, \{b\}, \{c\}, \{a, b\}, \{b, c\}, \{a, c\}, \{a, b, c\} \}$. Often, one needs to consider a finite but unspecified-size set such as $\{a, b, \ldots, n\}$.

In Prove-It, we construct such finite enumerated sets with the `Set`

class, which takes an arbitrary number of Prove-It expressions as arguments (including zero arguments for an empty set), like this: $\mathtt{Set}(e_1, e_2, \ldots, e_n)$. The result is a set of the elements $e_1$, $e_2$, $\ldots$, $e_n$, where the initial order specified is preserved for display purposes but the order itself has no inherent meaning.

Interestingly, you can also create redundantly populated sets such as $\{a, b, a\}$, which as we'll see below, are not considered multi-sets but instead are simply alternative representations of the reduced “support set” without the multiplicities — *e.g.*, $\{a, b, a\} = \{a, b\}$. Until one actively requests that a set such as $\{a, b, a\}$ be reduced to its support, Prove-It will continue to encode and display the set with the seeming multiplicities.

Enumerated sets are easy to construct using the Set class, and such Sets are easily incorporated into other expressions. Various Set construction examples are shown below, including small Sets of literals and variables, a Set of Sets (including an empty set), and a finite Set of unspecified length:

In [2]:

```
# A 3-element set of (literal) integers:
set_123 = Set(one, two, three)
```

In [3]:

```
# A 3-element set of variables:
set_abc = Set(a, b, c)
```

In [4]:

```
# A 6-element set of combining literals and variables:
set_123_abc = Set(one, two, three, a, b, c)
```

In [5]:

```
# A 4-element set of enumerated sets, including an empty set:
power_set_ab = Set(Set(), Set(a), Set(b), Set(a, b))
```

In [6]:

```
# a set of the first n positive integers
# set_1_to_n = Set(Iter(i, i, one, n))
set_1_to_n = Set(ExprRange(i, i, one, n))
```

In [7]:

```
# Claim that x is a member of the set {a, b, c}
InSet(x, set_123)
```

In [8]:

```
# A more elaborate claim about what it means for x to be a member
# of the set {1, 2, 3}. Notice the set appears as a domain specification:
Forall(x, Or(Equals(x, one), Equals(x, two), Equals(x, three)), domain=set_123)
```

Let's look at some simple examples of enumerated sets and their common attributes.

In [9]:

```
# Recall some enumerated sets defined earlier:
display(set_123)
display(set_abc)
display(power_set_ab)
```

We can look at the construction of such expressions by calling expr_info() to see the table version of the expression's underlying directed acyclic graph (DAG) representation:

In [10]:

```
# See how an enumerated set expression is constructed:
set_123.expr_info()
```

We can access the elements of an enumerated Set as operands of the Set class or somewhat more intuitively as elements of the Set, either of which return a tuple of the elements:

In [11]:

```
# the elements of the enumerated, as a tuple of elements
set_123.operands
```

In [12]:

```
# or more intuitively, using the elements attribute:
set_123.elements
```

Of course, we can also access each of the elements individually:

In [13]:

```
# and we can grab any single element:
e1, e2, e3 = set_123.elements[0], set_123.elements[1], set_123.elements[2]
```

And we can grab a slice of the elements:

In [14]:

```
# grab a slice of the elements:
set_123.operands[1:3]
```

We can get the set of all variables appearing in a Set, which returns an empty set if there no variables, and returns a reduced set if there are multiple instances of any variables:

In [15]:

```
# the set of variables used in the enumerated set
# (in this case an empty set because the set contains only literals)
used_vars(set_123)
```

In [16]:

```
# the set of variables used in the enumerated set
used_vars(set_abc)
```

In [17]:

```
# define a set with two instances of the same variable
set_aba = Set(a, b, a)
```

In [18]:

```
# notice that the operands include multiplicities
set_aba_operands = set_aba.operands
```

In [19]:

```
# and the elements attribute includes multiplicities
set_aba_elements = set_aba.elements
```

In [20]:

```
# but the tuple of variables used is just (a, b)
# and the order of appearance is not guaranteed
set_aba_vars = used_vars(set_aba)
```

We can also dig into a Set in terms of its sub-expressions:

In [21]:

```
# grab subexpressions from the Set construction
subexpr0, subexpr_1, subexpr_11 = (
set_abc.sub_expr(0),
set_abc.sub_expr(1),
set_abc.sub_expr(1).sub_expr(1))
```

Right now we have a single axiom in the logic.sets/enumeration theory, defining what it means to be a member of an enumerated set. If $x\in\{y_1,y_2, \ldots,y_n\}$, then $x=y_1 \lor x=y_2 \lor \ldots \lor x=y_n$:

In [22]:

```
from proveit.logic.sets.enumeration import enum_set_def
enum_set_def
```

As an exercise, we could, for example, manually instantiate that axiom for the case of $x\in\mathbb{B}$, by first instantiating for $x\in\{\top, \bot\}$:

In [23]:

```
enum_set_def_spec = enum_set_def.instantiate(
{n:two, x:x, y:(TRUE, FALSE)})
```

… and then bring in our axiomatic definition of the Boolean as $\mathbb{B}=\{\top, \bot\}$ and perform the appropriate substitution:

In [24]:

```
from proveit.logic.booleans import bools_def
bools_def
```

In [25]:

```
bools_def.sub_left_side_into(enum_set_def_spec)
```

The `logic.sets/enumeration`

theory already has a substantial number of related theorems and conjectures established, most of which would typically be used implicitly behind-the-scenes when utilizing the various related Set class methods instead of being used directly and explicitly in theorem form. Some illustrative examples of the theorems are shown below, and the remainder can be found in the enumeration theorems Jupyter Python notebook.

In [26]:

```
from proveit.logic.sets.enumeration import (
fold, fold_singleton, leftward_permutation,
reduction_right, subset_eq_of_superset)
```

In [27]:

```
# If x=y1 or x=y2 or … or x=yn, then x is in the
# set containing all those options
fold
```

In [28]:

```
# if x=y, then x is in the set {y}
fold_singleton
```

In [29]:

```
# An enumerated set is equal to a version of itself in which
# an element is moved to the left in the "list" of elements.
# Notice the set element 'c' below:
leftward_permutation
```

In [30]:

```
# An enumerated set is equal to a version of itself in which a 2nd
# occurrence of an element is removed. Notice the element(s) x in
# the set below:
reduction_right
```

In [31]:

```
# An enumerated set is always an improper subset of
# any set containing the original enumerated set
subset_eq_of_superset
```

Many simple enumerated set membership claims can be deduced automatically or relatively easily.

Let's start with a few claims about set membership:

In [32]:

```
# Some (as yet) unproven claims
b_in_set_abc, three_in_set_123, set_ab_in_its_power_set = (
InSet(b, set_abc), InSet(three, set_123), InSet(Set(a, b), power_set_ab))
```

Prove-It can prove each of these claims automatically, without any extra nudging from us:

In [33]:

```
# prove the claim that b is an elem of {a, b, c}
b_in_set_abc.prove()
```

In [34]:

```
# prove the claim that 3 is an elem of {1, 2, 3}
three_in_set_123.prove()
```

In [35]:

```
# prove the claim that {a,b} is an elem of its own power set
set_ab_in_its_power_set.prove()
```

The proofs generated by Prove-It for those three judgments established above (click on the turnstile in an expression to go to the proof page), are somewhat complicated and opaque, relying on the very basic `fold`

theorem each time for how membership is defined for an enumerated set. As an alternative, consider a manual application of the `in_enumerated_set`

theorem:

In [36]:

```
from proveit.logic.sets.enumeration import in_enumerated_set
in_enumerated_set
```

In [37]:

```
# Instantiate the theorem for the case of {a,b} being in its own power set:
set_ab_in_its_power_set_kt = in_enumerated_set.instantiate(
{m:three, n:zero, a:(Set(), Set(a), Set(b)), b:Set(a,b), c:()})
```

Now the proof of such a claim is relatively straightforward, and we will have moved much of the heavy-lifting of the previous proofs to a single underlying proof of the `in_enumerated_set`

theorem itself:

In [38]:

```
set_ab_in_its_power_set_kt.proof()
```

Many simple enumerated set non-membership claims can be deduced automatically or relatively easily.

First, recall a some enumerated Sets defined earlier:

In [39]:

```
display(set_123)
display(set_abc)
```

Prove-It can automatically prove some simple non-membership claims:

In [40]:

```
NotInSet(four, set_123).prove()
```

If the sets involve variables with unassigned values, however, things are more complicated. For example, Prove-It will rightly refuse to automatically deduce that $d\notin \{a, b, c\}$, because we don't know what the variables might be equal to:

In [41]:

```
try:
NotInSet(d, set_abc).prove()
assert False, "Should not make it to this point."
except ProofFailure as e:
print("Proof Failure: {}".format(e))
```

If we can provide Prove-It with the appropriate assumptions, however, we're OK:

In [42]:

```
NotInSet(d, set_abc).prove(assumptions=[NotEquals(a,d), NotEquals(b,d), NotEquals(c,d)])
```

In real-life operations, somewhere along the way we might have already proven that set of inequalities $a\ne d$, $b\ne d$, $c\ne d$, and Prove-It would then be able to automatically prove that $d\notin\{a, b, c\}$.

Although Prove-It might not be able to automatically prove that any arbitrary enumerated Set is a subset of another, the enumerated Set class has some machinery to help us out.

Consider the following sets, which include the set $\{1, 3, 5, 1, 5\}$ with redundant elements (recall that the enumerated Set class allows us to create a set that appears to hold multiple copies of the same element, but the set is not actually a multi-set):

In [43]:

```
# define some enumerated sets
set_1234567, set_evens, set_odds = (
Set(one, two, three, four, five, six, seven),
Set(two, four, six),
Set(one, three, five, one, five))
```

Prove-It can automatically prove that $\{2, 4, 6\}\subseteq\{1, 2, 3, 4, 5, 6, 7\}$:

In [44]:

```
SubsetEq(set_evens, set_1234567).prove()
```

This can produce an odd-looking result, because an enumerated Set might look like a multi-set but is in fact equal to its underlying support. For example, because $\{1, 3, 5, 1, 5\} = \{1, 3, 5\}$, we have the following result, even though the subset *appears* to have multiple copies of elements $1$ and $5$:

In [45]:

```
SubsetEq(set_odds, set_1234567).prove()
```

So, consider the enumerated Sets $\{1, 3, 5\}$ and $\{1, 2, 3, 4, 5, 6\}$ defined below:

In [46]:

```
set_135, set_123456 = (
Set(one, three, five),
Set(one, two, three, four, five, six))
```

In [47]:

```
ProperSubset(set_135, set_123456).prove()
```

But we have an established theorem (really a conjecture) in the `enumeration`

theory that we can use to deduce the subset relationship:

In [48]:

```
from proveit.logic.sets.enumeration import proper_subset_of_superset
proper_subset_of_superset
```

Our goal here is to instantiate the proper_subset_of_superset theorem with the details of our desired case, where we will take:

- $m=3$ to be the size of our subset Set $\{1, 3, 5\}$;
- $n=2$ to be the size of $c_1, \ldots, c_n$ portion of our superset Set $\{1, 2, 3, 4, 5, 6\}$;
- $\{a_1, \ldots, a_m\}$ to be our subset $\{1, 3, 5\}$;
- and $\{a_1, \ldots, a_m, b, c_1, \ldots, c_n\}$ to be our superset $\{1, 2, 3, 4, 5, 6\}$ after being suitably permuted to something like $\{1, 3, 5, 2, 4, 6\}$, which then requires $b$ to be the element $2$ and $\{c_1, \ldots, c_n\}$ to be the set $\{4, 6\}$.

In [49]:

```
superset_permuted_kt = set_123456.permutation(new_order=[0, 2, 4, 1, 3, 5])
```

Notice that we eventually want to replace $b$ with our superset element $2$ (although we could have chosen any superset element that isn't in the subset and chosen a different permutation as necessary), and so we'll want to know that $2\notin\{1, 3, 5\}$. The instantiation machinery might handle that part for us automatically, but for this example we'll ask Prove-It to first do it for us explicitly:

In [50]:

```
NotInSet(two, set_135).prove()
```

Now we should be ready to instantiate the proper_subset_of_superset theorem as shown below:

In [51]:

```
proper_subset_of_superset_spec = proper_subset_of_superset.instantiate(
{m:three, n:two, a:set_135.elements, b:two, c:(four, six)})
```

And finally we use our earlier-derived permutation equivalence to back substitute and get the desired subset relationship:

In [52]:

```
superset_permuted_kt.sub_left_side_into(proper_subset_of_superset_spec)
```

The enumerated Set class has machinery to help automate the deduction of proper subset relationships such as $\{1, 3, 5\}\subset\{1, 2, 3, 4, 5, 6\}$ derived manually in the previous demonstration. That process of set reduction, permutation, theorem application, and back substitution, has been encapsulated in the `Set.deduce_enum_proper_subset()`

method. For example, we can derive that $\{2, 4, 6\}\subset\{1, 2, 3, 4, 5, 6\}$ in a single step:

In [53]:

```
# define the subset {2, 4, 6}
set_246 = Set(two, four, six)
```

In [54]:

```
# deduce that {2, 4, 6} is a proper subset of {1, 2, 3, 4, 5, 6}
proper_subset_jdgmt = set_123456.deduce_enum_proper_subset(subset=set_246)
```

That process works without any special assumptions included in the call of the `deduce_enum_proper_subset()`

method because Prove-It recognizes the elements $1, 2, \ldots, 6$ as distinct literals and thus that (e.g.) $1\notin\{2, 4, 6\}$ (which allows Prove-It to recognize that the set $\{1, 2, 3, 4, 5, 6\}$ not only contains the set $\{2, 4, 6\}$ but also has elements not in that subset).

If we are dealing with *variables*, though, the process is not so clear-cut. Consider, for example, the claim that $\{c, b, a\}\subset\{a, b, c, d, e\}$. If one interprets the letter elements as literals, then it's clear that $d\notin\{c, b, a\}$ and we can easily understand that $\{c, b, a\}$ is a proper subset of $\{a, b, c, d, e\}$. But if $a, b, \ldots, e$ are variables, it's not at all clear that $d\ne a$ and thus it's not clear that $d\notin\{c, b, a\}$.

Thus, without some additional information or additional assumptions, the `deduce_enum_proper_subset()`

method will fail to deduce the desired relationship:

In [55]:

```
from proveit import e
set_abcde, set_cba = Set(a, b, c, d, e), Set(c, b, a)
try:
set_abcde.deduce_enum_proper_subset(subset=set_cba)
assert False, "Should not make it to this point."
except ValueError as e:
print("Proof Failure: {}".format(e))
```

If we supply the additional assumptions, however, that $d\ne a$, $d\ne b$, and $d\ne c$, the derivation works fine:

In [56]:

```
set_abcde.deduce_enum_proper_subset(subset=set_cba,
assumptions=[NotEquals(d, a), NotEquals(d, b), NotEquals(d, c)])
```

Alternatively we could have supplied the equivalent assumption that $d\notin \{c, b, a\}$:

In [57]:

```
set_abcde.deduce_enum_proper_subset(subset=set_cba,
assumptions=[NotInSet(d, Set(c, b, a))])
```

Variables and multiplicities can combine to produce some tricky cases when trying to establish subset relationships between enumerated Sets. Notice that when considering the possibility that $\{1, a, 1\}\subset\{1, a, 1, b, 2, a\}$, if it turns out that $a=b=2$, then the proper subset relation does not hold. Prove-It will not deduce the proper subset relationship without more information. Using the enumerated Set.deduce_enum_proper_subset() method, we get an error:

In [58]:

```
set_1a1 = Set(one, a, one)
set_1a1b2a = Set(one, a, one, b, two, a)
print("set_1a1 =")
display(set_1a1)
print("set_1a1b2a =")
display(set_1a1b2a)
try:
set_1a1b2a.deduce_enum_proper_subset(subset=set_1a1)
assert False, "Should not get to this point."
except ValueError as the_error:
print("Proof Failure (ValueError): {}".format(the_error))
```

If we provide a little more information, however, specifying how to interpret the variable 'a', Prove-It is able to proceed (in part because now it can be proven that 2 is in the superset but not in the subset):

In [59]:

```
set_1a1b2a.deduce_enum_proper_subset(subset=set_1a1, assumptions=[Equals(a, one)])
```

The material below was developed to test various enumerated Set and Set-related methods. Some of this material could be integrated into the `_demonstrations_`

page eventually and/or deleted as development continues.

`Sets`

For Testing¶In [60]:

```
# some standard enumerated sets
from proveit import e
set_12345, set_abcde, set_1a2b3c, set_a2c13b, set_3c1ab2, power_set_of_ab, empty_set = (
Set(one, two, three, four, five),
Set(a, b, c, d, e),
Set(one, a, two, b, three, c),
Set(a, two, c, one, three, b),
Set(three, c, one, a, b, two),
Set(Set(), Set(a), Set(b), Set(a,b)),
Set())
```

In [61]:

```
# some non-standard enumerated sets pretending to be multi-sets (but not!)
set_12131, set_1a1b2a = (
Set(one, two, one, three, one),
Set(one, a, one, b, two, a))
```

`permutation_move()`

method¶The permutation_move() method has the following format:

permutation_move(self, init_idx=None, final_idx=None, assumptions=USE_DEFAULTS):

and moves the element at index init_idx to position final_idx (a “pluck and insert” process).
In [62]:

```
# permuting an element in a Set of literals
Set(one, two, three, four, five).permutation_move(0, 1)
```

In [63]:

```
# permuting an element in a Set of variables
Set(a, b, c, d, e).permutation_move(1, 3)
```

In [64]:

```
# permuting an element in a heterogeneous Set
Set(one, a, two, b, three, c).permutation_move(3, 1)
```

In [65]:

```
# permuting an element in a Set of Sets
power_set_of_ab.permutation_move(0, 3)
```

In [66]:

```
# permuting an element FROM a non-existent location
# in the Set should not work
try:
set_1a2b3c.permutation_move(6, 3)
except IndexError as the_error:
print("Index Error: {}".format(the_error))
```

In [67]:

```
# permuting an element TO a non-existent location
# in the Set should not work
try:
set_1a2b3c.permutation_move(4, 6)
except IndexError as the_error:
print("Index Error: {}".format(the_error))
```

`permutation_swap()`

method¶The permutation_swap() method has the following format:

permutation_swap(self, idx01=None, idx02=None, assumptions=USE_DEFAULTS):

and attempts to deduce that the Set expression is equal to a Set in which the elements at index locations idx01 and idx02 have swapped locations.
In [68]:

```
# basic swap permutation
set_1a2b3c.permutation_swap(1, 4)
```

In [69]:

```
# basic swap with irrelevant assumptions
set_1a2b3c.permutation_swap(1, 4, assumptions=[Equals(a, one)])
```

In [70]:

```
# if idx01 > idx02
set_123.permutation_swap(1, 0)
```

In [71]:

```
# empty swap: idx01 = idx02
set_123.permutation_swap(2, 2)
```

In [72]:

```
# if indices not valid:
try:
set_12345.permutation_swap(2, 6)
assert False, "Should not make it to this point."
except IndexError as the_error:
print("Index Error: {}".format(the_error))
```

`permutation()`

method¶The permutation() method has the following format:

permutation(self, new_order=None, cycles=None, assumptions=USE_DEFAULTS)

where new_order is a list of the indices in the desired order, and cycles is a list of tuples designating index-based cycles, either of which derive an equivalence to a permuted version of the Set. A new_order designation must include all indices; a cycles designation need only specify the indices involved in a cycle.
In [73]:

```
# permuting an element in a Set of literals
set_12345.permutation(new_order=[1, 2, 3, 4, 0])
```

In [74]:

```
# permuting an element in a Set of variables
set_abcde.permutation(new_order=[4, 3, 1, 0, 2])
```

In [75]:

```
# permuting an element in a heterogeneous Set
# using cycle notation
set_1a2b3c.permutation(cycles=[(0,1),(2, 3)])
```

In [76]:

```
# permuting an element in a Set of Sets
# using cycle notation
power_set_of_ab.permutation(cycles=[(0,2,3)])
```

In [77]:

```
# supplying incorrect (out-of-bound) indices for the new_order
# should give an informative error
try:
set_1a2b3c.permutation(new_order=[2, 3, 1, 5, 4, 6])
except IndexError as the_error:
print("Index Error: {}".format(the_error))
```

In [78]:

```
# supplying incomplete indices for the new_order
# should give an informative error
try:
set_1a2b3c.permutation(new_order=[2, 0, 5, 4])
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

In [79]:

```
# supplying incorrect (out-of-bound) indices for cycles
# should give an informative error
try:
set_1a2b3c.permutation(cycles=[(1, 3, 2),(0, 6)])
assert False, "Should not get to this line!"
except IndexError as the_error:
print("Index Error: {}".format(the_error))
```

In [80]:

```
# supplying indices more than once in cycles
# notation should give an informative an error
try:
set_1a2b3c.permutation(cycles=[(1, 3, 2),(3, 4)])
assert False, "Should not get to this line!"
except IndexError as the_error:
print("Index Error: {}".format(the_error))
```

In [81]:

```
# supplying both new_order and cycles args
# should give an informative an error
try:
set_1a2b3c.permutation(new_order=[5,4,3,2,1,0], cycles=[(1, 3, 2)])
assert False, "Should not get to this line!"
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

In [82]:

```
Equals(set_1a2b3c, set_a2c13b).prove()
```

In [83]:

```
Equals(set_1a2b3c, set_3c1ab2).prove()
```

In [84]:

```
Equals(set_a2c13b, set_3c1ab2).prove()
```

`deduce_as_superset_eq_of()`

method¶The deduce_as_superset_eq_of() method has the following format:

deduce_as_superset_eq_of(self, subset_indices=None, subset=None, **defaults_config)

where one supplies either the subset_indices OR a subset, but not both. subset_indices is a list of indices of desired subset elements of the original Set, and subset provides the desired subset as an actual enumerated Set. In using subset_indices or subset, either one, the proffered order of subset elements is preserved in the derived subset relationship.
In [85]:

```
# some non-standard enumerated sets pretending to be multi-sets (but not!)
set_12131, set_1a1b2a = (
Set(one, two, one, three, one),
Set(one, a, one, b, two, a))
```

In [86]:

```
# deducing as a superset_eq of a set of literals
set_12345.deduce_as_superset_eq_of(subset_indices=[1, 4, 3])
```

In [87]:

```
# deducing a subset of a heterogeneous Set using subset notation
set_1a2b3c.deduce_as_superset_eq_of(subset=Set(a, two, c))
```

`deduce_enum_subset_eq()`

method¶The deduce_enum_subset_eq() method has the following format:

deduce_enum_subset_eq(self, subset_indices=None, subset=None, assumptions=USE_DEFAULTS)

where one supplies either the subset_indices OR a subset, but not both. subset_indices is a list of indices of desired subset elements of the original Set, and subset provides the desired subset as an actual enumerated Set. In using subset_indices or subset, either one, the proffered order of subset elements is preserved in the derived subset relationship.
In [88]:

```
# deducing a subset of a Set of literals
set_12345.deduce_enum_subset_eq(subset_indices=[1, 4, 3])
```

In [89]:

```
# deducing a subset of a Set of variables
set_abcde.deduce_enum_subset_eq(subset_indices=[2, 1, 0])
```

In [90]:

```
# deducing a subset of a heterogeneous Set
# using subset notation
set_1a2b3c.deduce_enum_subset_eq(subset=Set(a, two, c))
```

In [91]:

```
# deducing a subset of a Set of Sets
# using subset notation
power_set_of_ab.deduce_enum_subset_eq(subset=Set(Set(a), Set(b)))
```

In [92]:

```
# deducing a subset of a Set
# with redundant subset elements specified
set_1a1b2a.deduce_enum_subset_eq(subset_indices=[0, 1, 2])
```

In [93]:

```
# what if we have a variable in the supposed subset and the variable
# is equal to a value in the supposed superset?
set_12a = Set(one, two, a)
set_12345.deduce_enum_subset_eq(subset=Set(one, two, a), assumptions=[Equals(a, one)])
```

In [94]:

```
# Works when subset = empty set
set_12131.deduce_enum_subset_eq(subset=empty_set)
```

In [95]:

```
# Work when subset = superset = empty set
empty_set.deduce_enum_subset_eq(subset=empty_set)
```

In [96]:

```
# supplying incorrect (out-of-bound) indices for subset
# should give an informative error
try:
set_1a2b3c.deduce_enum_subset_eq(subset_indices=[2, 6, 0])
except IndexError as the_error:
print("Index Error: {}".format(the_error))
```

In [97]:

```
# supplying incorrect elements for subset
# should give an informative error
try:
set_1a2b3c.deduce_enum_subset_eq(subset=Set(a, B, two, d))
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

`deduce_enum_proper_subset()`

method¶The deduce_enum_proper_subset() method has the following format:

deduce_enum_proper_subset(self, subset_indices=None, subset=None, assumptions=USE_DEFAULTS)

where one supplies either the subset_indices OR a subset, but not both. subset_indices is a list of indices of desired subset elements of the original Set, and subset provides the desired subset as an actual enumerated Set. In using subset_indices or subset, either one, the proffered order of subset elements is preserved in the derived subset relationship.
In [98]:

```
# deducing a subset of a Set of literals
set_12345.deduce_enum_proper_subset(subset_indices=[1, 4, 3])
```

In [99]:

```
# deducing a subset of a Set of variables
set_abcde.deduce_enum_proper_subset(
subset_indices=[2, 1, 0],
assumptions=[NotEquals(d,a), NotEquals(d,b), NotEquals(d,c)])
```

In [100]:

```
# deducing a proper subset of a Set of variables
# with no additional information about the variables
try:
set_abcde.deduce_enum_proper_subset(subset_indices=[2, 1, 0])
assert False, "Should not make it to this point."
except ValueError as the_error:
print("Proof Failure: {}".format(the_error))
```

In [101]:

```
# deducing a proper subset of a heterogeneous Set
# Will often need more info, depending on the arrangement specified:
try:
set_1a2b3c.deduce_enum_proper_subset(subset=Set(a, two, c))
assert False, "Should not make it to this point."
except ValueError as the_error:
print("Proof Failure: {}".format(the_error))
```

In [102]:

```
# but we can supply enough info to help:
set_1a2b3c.deduce_enum_proper_subset(
subset=Set(a, two, c), assumptions=[NotInSet(one, Set(a, two, c))])
```

In [103]:

```
# deducing a subset of a Set with redundant subset elements specified
# the variables will initially be a problem
try:
set_1a1b2a.deduce_enum_proper_subset(subset_indices=[0, 1, 2])
assert False, "Should not get to this point."
except ValueError as the_error:
print("Proof Failure: {}".format(the_error))
```

In [104]:

```
# but if we supply a single foothold, it will work; notice here that
# we supplied the info in terms of the unreduced set, but could also
# supply the info in terms of the reduced set (see next cell)
set_1a1b2a.deduce_enum_proper_subset(
subset_indices=[0, 1, 2], assumptions=[NotInSet(b, Set(one, a, one))])
```

In [105]:

```
# Compare to previous cell. If we supply a single foothold, it will work;
# notice here that we supplied the info in terms of the reduced set, but could also
# supply the info in terms of the un-reduced set (see previous cell)
set_1a1b2a.deduce_enum_proper_subset(
subset_indices=[0, 1, 2], assumptions=[NotInSet(b, Set(one, a))])
```

In [106]:

```
# Here we have a variable in the supposed proper subset and the variable
# is equal to a value in the supposed superset:
set_12345.deduce_enum_proper_subset(subset=Set(one, two, a), assumptions=[Equals(a, one)])
```

In [107]:

```
# If we have the same variable appearing in both the subset and superset,
# even if we then assume the variable has a specific value, we can
# run into difficulties. The following works correctly though:
set_1a1b2a.deduce_enum_proper_subset(
subset_indices=[0, 1, 2], assumptions=[Equals(a, one)])
```

In [108]:

```
NotEquals(six, a).prove(assumptions=[Equals(a, one)])
```

In [109]:

```
example_deduction = NotInSet(five, Set(a, two)).prove(assumptions=[Equals(a, one)])
```

In [110]:

```
set_1a1b2a.deduce_enum_proper_subset(
subset_indices=[0, 1, 2], assumptions=[NotEquals(a, two)])
```

In [111]:

```
# Related: note that Prove-It will automatically deduce various
# non-membership claims:
NotInSet(two, Set(one, a, one)).prove(assumptions=[NotInSet(two, Set(one, a))])
```

In [112]:

```
# Related: note that Prove-It will automatically deduce various
# non-membership claims:
NotInSet(two, Set(one, a)).prove(
assumptions=[NotInSet(two, Set(one, a, one))])
```

In [113]:

```
# deducing a proper subset of a Set with redundant subset elements specified
set_12131.deduce_enum_proper_subset(subset=Set(one, two, two))
```

In [114]:

```
# Also works when the subset is the empty set
set_12131.deduce_enum_proper_subset(subset=empty_set)
```

In [115]:

```
# Although the empty set {} is an improper subset of itself
# and the empty set is a proper subset of any non-empty set,
# the empty set is not a proper subset of itself
try:
empty_set.deduce_enum_proper_subset(subset=empty_set)
assert False, "Should not make it to the point."
except ValueError as the_error:
print("Proof Failure: {}".format(the_error))
```

In [116]:

```
# supplying incorrect (out-of-bound) indices for subset
# should give an informative error
try:
set_1a2b3c.deduce_enum_proper_subset(subset_indices=[2, 6, 0])
except IndexError as the_error:
print("Index Error: {}".format(the_error))
```

In [117]:

```
# supplying incorrect elements for subset
# should give an informative error
try:
set_1a2b3c.deduce_enum_proper_subset(subset=Set(a, B, two, d))
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

In [118]:

```
# Switching the subset and superset should give an error
try:
set_123.deduce_enum_proper_subset(subset=set_123456)
assert False, "Should not make it to this point!"
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

`elem_multiplicity_reduction()`

method¶The `Set.elem_multiplicity_reduction()`

method has the following format:

elem_multiplicity_reduction(self, elem=None, idx=None, **defaults_config)

and attempts to deduce that the Set expression is equal to the same Set in which the multiply-occurring set element specified by `elem`

or by the 0-based position `idx`

has been removed from the set. If `elem`

specified, method attempts to delete the 2nd occurrence of `elem`

from the set. If neither `elem`

nor `idx`

specified, method attempts to delete the first repeated element of the Set. If both `elem`

and `idx`

are specified, the `elem`

param is ignored.
In [119]:

```
# A reminder of some Sets with (apparent) multiplicities
display(Set(one, two, one, three, one))
display(Set(one, a, one, b, two, a))
```

In [120]:

```
# remove the 2nd occurrence of the element 1
Set(one, two, one, three, one).elem_multiplicity_reduction(elem=one)
```

In [121]:

```
# remove the element 1 occurring at 0-based index position 4
Set(one, two, one, three, one).elem_multiplicity_reduction(idx=4)
```

In [122]:

```
# remove the element '1' occurring at 0-based index position 4
# ignoring the elem argument referring to element '3'
Set(one, two, one, three, one, three).elem_multiplicity_reduction(elem=three, idx=4)
```

In [123]:

```
# remove the 1st occurrence of the multiply-occurring element 'a'
Set(one, a, one, b, two, a).elem_multiplicity_reduction(idx=1)
```

In [124]:

```
# remove the 1st repetition of an element (using default args)
Set(one, a, one, b, two, a).elem_multiplicity_reduction()
```

In [125]:

```
# reduce set by removing a non-trivially-defined multiply-occurring element
Set(one, Add(one, one), one, Add(one, one)).elem_multiplicity_reduction(elem=Add(one, one))
```

In [126]:

```
# try reducing with default args a set with no multiplicities
try:
Set(one, two, three).elem_multiplicity_reduction()
assert False, "Should not make it to this point!"
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

In [127]:

```
# try reducing a specific element with no multiplicities
try:
Set(one, a, one, b, two, a).elem_multiplicity_reduction(elem=b)
assert False, "Should not make it to this point!"
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

In [128]:

```
# try reducing a specific element that does not appear in the set
try:
Set(one, a, one, b, two, a).elem_multiplicity_reduction(elem=c)
assert False, "Should not make it to this point!"
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

In [129]:

```
# try reducing an element by idx when the elem occurs only once
try:
Set(one, a, one, b, two, a).elem_multiplicity_reduction(idx=3)
assert False, "Should not make it to this point!"
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

`reduction()`

method¶The Set.reduction() method has the following format:

reduction(self, assumptions=USE_DEFAULTS)

and attempts to deduce that the Set expression is equal to the Set's support -- i.e. equal to a Set with all multiplicities reduced to 1. The resulting support Set maintains the original order of all 1st-appearing elements in the Set.
In [130]:

```
# A reminder of some Sets with (apparent) multiplicities
display(Set(one, two, one, three, one))
display(Set(one, a, one, b, two, a))
```

In [131]:

```
Set(one, two, one, three, one).reduction()
```

In [132]:

```
Set(one, a, one, b, two, a).reduction()
```

In [133]:

```
Set(one, two, three).reduction()
```

In [134]:

```
Set().reduction()
```

In [135]:

```
Set(one, Add(one, one), one).elem_multiplicity_reduction(idx=0)
```

`shallow_simplification()`

method¶Currently, `shallow_simplification`

eliminates multiplicities (see `reduction`

) and also, if all of the elements are literal integers, puts them into a sorted order.

In [136]:

```
unsimplified_set = Set(five, two, two, six, three, one, four, five)
```

In [137]:

```
unsimplified_set.simplification()
```

Either of these may be disabled via changing the `Set`

simplification directives.

In [138]:

```
Set.change_simplification_directives(eliminate_duplicates=False)
```

In [139]:

```
unsimplified_set.simplification()
```

In [140]:

```
Set.change_simplification_directives(
eliminate_duplicates=True, sort_if_literal_integers=False)
```

In [141]:

```
unsimplified_set.simplification()
```

`single_elem_substitution()`

method¶The Set.single_elem_substitution() method has the following format:

single_elem_substitution(self, elem=None, idx=None, sub_elem=None, assumptions=USE_DEFAULTS)

and attempts to deduce that the Set expression is equal to the Set obtained when the element specified by elem or idx is replaced by sub_elem, which requires that elem = sub_elem.
In [142]:

```
# |– Replace the 1st 'a' (elem at index 1) in {1, a, 1, b, 2, a} with 2
# using idx arg
set_1a1b2a.single_elem_substitution(idx=1, sub_elem=two, assumptions=[Equals(a, two)])
```

In [143]:

```
# |– Replace the 1st 'a' (elem at index 1) in {1, a, 1, b, 2, a} with 2
# using elem arg
set_1a1b2a.single_elem_substitution(elem=a, sub_elem=two, assumptions=[Equals(a, two)])
```

In [144]:

```
# |– Replace the 2nd 'a' (elem at index 5) in {1, a, 1, b, 2, a} with 2
# using compound elem arg
set_1a1b2a.single_elem_substitution(elem=[a, 2], sub_elem=two, assumptions=[Equals(a, two)])
```

In [145]:

```
# |– Replace the 1st 'e' (elem at index 4) in {a, b, c, d, e} with 4
# using idx arg
from proveit import e
set_abcde.single_elem_substitution(idx=4, sub_elem=four, assumptions=[Equals(e, four)])
```

In [146]:

```
# We can go the other way as well, replacing a literal by a variable. Yay!
Set(one, two, three, four, five).single_elem_substitution(elem=three, sub_elem=c, assumptions=[Equals(c, three)])
```

In [147]:

```
# And we can replace a variable with a variable. Yay!
from proveit import e
set_abcde.single_elem_substitution(elem=c, sub_elem=e, assumptions=[Equals(c, e)])
```

In [148]:

```
# should get meaningful error message when trying to substitute for an
# element but picking a multiplicity that doesn't exist. Here there is no 2nd 'b':
try:
set_1a1b2a.single_elem_substitution(elem=[b, 2], sub_elem=two, assumptions=[Equals(a, two)])
assert False, "Should not make it to this point."
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

In [149]:

```
# should get meaningful error message when trying to substitute for an
# element but picking a multiplicity that doesn't make sense:
try:
set_1a1b2a.single_elem_substitution(elem=[b, 0], sub_elem=two, assumptions=[Equals(a, two)])
assert False, "Should not make it to this point."
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

In [150]:

```
# should get a meaningful error message when omitting the sub_elem argument
try:
set_1a1b2a.single_elem_substitution(idx=1, assumptions=[Equals(a, two)])
assert False, "Should not make it to this point."
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

In [151]:

```
# should get meaningful error message when trying to substitute for an
# element that doesn't exist in the original set:
try:
set_1a1b2a.single_elem_substitution(elem=c, sub_elem=two, assumptions=[Equals(c, two)])
assert False, "Should not make it to this point."
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

`elem_substitution()`

method¶The Set.elem_substitution() method has the following format:

elem_substitution(self, elem=None, sub_elem=None, assumptions=USE_DEFAULTS)

and attempts to deduce that the Set expression is equal to the Set obtained when all instances of the element specified by elem are replaced by sub_elem, which requires that elem = sub_elem.
In [152]:

```
# |– Replace all instances of 'a' in {1, a, 1, b, 2, a} with 2
set_1a1b2a.elem_substitution(elem=a, sub_elem=two, assumptions=[Equals(a, two)])
```

In [153]:

```
# |– Replace all instances of 'b' in {1, a, 1, b, 2, a} with 3
set_1a1b2a.elem_substitution(elem=b, sub_elem=three, assumptions=[Equals(b, three)])
```

In [154]:

```
Set(a, b, a, b, a).elem_substitution(elem=a, sub_elem=two, assumptions=[Equals(a, two)])
```

In [155]:

```
# |– Replace all instances of 'e' in {a, b, c, d, e} with 4
from proveit import e
set_abcde.elem_substitution(elem=e, sub_elem=four, assumptions=[Equals(e, four)])
```

In [156]:

```
# We can go the other way as well, replacing all instances of a literal by a variable. Yay!
# Here we replace all instances of 3 with the variable c
Set(one, two, three, four, five).elem_substitution(elem=three, sub_elem=c, assumptions=[Equals(c, three)])
```

In [157]:

```
# And we can replace a variable with a variable. Yay!
# Here we replace all instances of c with the variable e
from proveit import e
set_abcde.elem_substitution(elem=c, sub_elem=e, assumptions=[Equals(c, e)])
```

In [158]:

```
# What if we do a sequence of these, and then call deduce_enum_subset_eq? Is it possible to
# get back to where we began?
# Here we show that {a, b, c, d, e} \subset_eq {1, 2, 3, d, e} when a=2, b=2, c=3.
temp_assumptions = [Equals(a, two), Equals(b, two), Equals(c, three)]
elems_to_replace = [a, b, c]
elems_sub = [two, two, three]
num_elems_to_replace = len(elems_to_replace)
from proveit import TransRelUpdater
expr = set_abcde
eq = TransRelUpdater(expr, temp_assumptions)
for i in range(0, len(elems_to_replace)):
expr = eq.update(expr.elem_substitution(
elem=elems_to_replace[i], sub_elem=elems_sub[i],
assumptions=temp_assumptions, preserve_all=True))
temp_subset_eq_kt = Set(one, two, three, d, e).deduce_enum_subset_eq(subset = expr)
eq.relation.sub_left_side_into(temp_subset_eq_kt)
```

In [159]:

```
# Should get meaningful error message when trying to substitute for an
# element that doesn't exist in the original set:
try:
set_1a1b2a.elem_substitution(elem=c, sub_elem=two, assumptions=[Equals(c, two)])
assert False, "Should not make it to this point."
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

In [160]:

```
# Should get meaningful error message when omitting the elem argument
try:
set_1a1b2a.elem_substitution(sub_elem=two, assumptions=[Equals(c, two)])
assert False, "Should not make it to this point."
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

In [161]:

```
# Should get meaningful error message when omitting the sub_elem argument
try:
set_1a1b2a.elem_substitution(elem=a, assumptions=[Equals(a, two)])
assert False, "Should not make it to this point."
except ValueError as the_error:
print("Value Error: {}".format(the_error))
```

`membership_object()`

method¶The Set.membership_object() method has the following format:

membership_object(self, element)

and produces an EnumMembership object of the form (element $\in$ self), which then has a number of related methods such as side_effects(), definition(), In [162]:

```
example_membership_obj_01 = Set(B,C,D).membership_object(C)
```

In [163]:

```
# to see the explicit membership object, use the expr attribute
example_membership_obj_01.expr
```

In [164]:

```
example_membership_obj_02 = Set(B,C,D).membership_object(E)
```

In [165]:

```
example_membership_obj_02.expr
```

`membership_object().definition()`

¶In [166]:

```
# With variables
Set(B,C,D).membership_object(C).definition()
```

In [167]:

```
Set(B,C,D).membership_object(E).definition()
```

In [168]:

```
# Works easily with literals
Set(one, two, three).membership_object(three).definition()
```

In [169]:

```
# Works easily with literals
Set(one, two, three).membership_object(four).definition()
```

In [170]:

```
# Also appears to work OK with single-element sets
Set(one).membership_object(four).definition()
```

In [171]:

```
# Also works with single-element sets
Set(y).membership_object(x).definition()
```

`membership_object().conclude()`

¶In [172]:

```
Set(B,C,D).membership_object(C).conclude()
```

In [173]:

```
# Will fail to conclude without suffient info. In particular, needs to
# know the element in question is equal to one of the set elements
try:
Set(B,C,D).membership_object(A).conclude()
assert False, "Should not make it to this point."
except InstantiationFailure as the_error:
print("InstantiationFailure: {}".format(the_error))
```

In [174]:

```
# Works easily with literals
Set(one, two, three).membership_object(three).conclude()
```

In [175]:

```
# Will fail as it should when trying to conclude a false membership claim
try:
Set(one, two, three).membership_object(four).conclude()
assert False, "Should not make it to this point."
except InstantiationFailure as the_error:
print("InstantiationFailure: {}".format(the_error))
```

In [176]:

```
# Notice that the conclude() method will not automatically
# simplify expressions within the list:
temp_test_set = Set(one, Add(one, one), three)
temp_test_mem = temp_test_set.membership_object(three).expr
```

In [177]:

```
temp_test_mem.conclude()
```

In [178]:

```
# Notice that the conclude() method will not automatically
# simplify expressions within the list, but can still check for equality
# (Here, 2 = 1+1, and thus the value 2 is indeed in the list)
temp_test_set_02 = Set(one, Add(one, one), three)
temp_test_mem_02 = temp_test_set.membership_object(two).expr
```

In [179]:

```
temp_test_mem_02.conclude()
```

`membership_object().unfold()`

¶In [180]:

```
Set(B,C,D).membership_object(C).unfold()
```

In [181]:

```
Set(one, two, three).membership_object(three).unfold()
```

In [182]:

```
# The unfolding will only work if the membership_object is true:
try:
Set(one, two, three).membership_object(four).unfold()
assert False, "Should not get to this point."
except InstantiationFailure as the_error:
print("InstantiationFailure: {}".format(the_error))
```

`membership_object().derive_in_singleton()`

¶In [183]:

```
# The purpose of the derive_in_singleton() method is very mysterious.
Set(one, two, three).membership_object(three).derive_in_singleton(Equals(InSet(two, Set(two)), TRUE))
```

In [184]:

```
Set(y).membership_object(x).derive_in_singleton(Equals(InSet(x, Set(y)), FALSE), assumptions=[NotEquals(x,y)])
```

In [185]:

```
# Notice that the derived singleton membership claim can be
# completely independent of the original membership_object
Set(one, two, three).membership_object(three).derive_in_singleton(
Equals(InSet(two, Set(two)), TRUE))
```

`membership_object().deduce_in_bool()`

¶In [186]:

```
Set(A, B, C).membership_object(B).deduce_in_bool()
```

In [187]:

```
Set(one, two, three).membership_object(three).deduce_in_bool()
```

In [188]:

```
# And of course a false membership claim is also Boolean
Set(one, two, three).membership_object(four).deduce_in_bool()
```

`nonmembership_object()`

method¶The Set.nonmembership_object() method has the following format:

nonmembership_object(self, element)

and produces an EnumNonmmembership object of the form (element $\notin$ self), along with a number of related methods such as side_effects(), equivalence(), In [189]:

```
example_nonmembership_object_01 = Set(B,C,D).nonmembership_object(A)
```

In [190]:

```
example_nonmembership_object_01.expr
```

`nonmembership_object().definition()`

¶In [191]:

```
Set(B,C,D).nonmembership_object(A).definition()
```

In [192]:

```
set_123.nonmembership_object(four).definition()
```

In [193]:

```
# notice the equivalence doesn't depend on the
# nonmembership_object being true
set_123.nonmembership_object(two).definition()
```

In [194]:

```
Set(one, two).nonmembership_object(two).definition()
```

In [195]:

```
# Works with singletons as well
Set(one,).nonmembership_object(two).definition()
```

In [196]:

```
# Works with singletons as well
Set(y).nonmembership_object(x).definition()
```

`nonmembership_object().conclude()`

¶In [197]:

```
# Will fail to conclude without suffient info. In particular,
# needs to know the element in question is not equal to
# any of the set elements
try:
Set(B,C,D).nonmembership_object(A).conclude()
assert False, "Should not make it to this point."
except InstantiationFailure as the_error:
print("Proof/Instantiation Failure: {}".format(the_error))
```

In [198]:

```
# But given sufficient information, conclude() method works just fine
Set(B,C,D).nonmembership_object(A).conclude(
assumptions=[NotEquals(A, B), NotEquals(A, C), NotEquals(A, D)])
```

In [199]:

```
# Works easily with literals
Set(one, two, three).nonmembership_object(four).conclude()
```

In [200]:

```
# A singleton example -- requires explicit assumptions
Set(y).nonmembership_object(x).conclude(assumptions=[NotEquals(x, y)])
```

In [201]:

```
# The conclude does not automatically simplify the expressions in the list
Set(one, Add(one, one), three).nonmembership_object(four).conclude()
```

In [202]:

```
# # The conclude does not automatically simplify the expressions in the list
# but can still check for equalities --- in this case, the nonmembership
# is false (since 2 = 1 + 1), and thus the conclude() should fail because
# the value 2 is in {1, 1+1, 3}
try:
Set(one, Add(one, one), three).nonmembership_object(two).conclude()
assert False, "Expecting a ProofFailure (InstantiationFailure); should not make it this far!"
except InstantiationFailure as the_error:
print("InstantiationFailure: {}".format(the_error))
```

`nonmembership_object().unfold()`

¶In [203]:

```
Set(B,C,D).nonmembership_object(A).unfold(assumptions=[NotInSet(A, Set(B, C, D))])
```

In [204]:

```
Set(one, two).nonmembership_object(A).unfold(assumptions=[NotInSet(A, Set(one, two))])
```

In [205]:

```
Set(one, two, three).nonmembership_object(four).unfold()
```

In [206]:

```
# Will fail to conclude if non-membership is simply known to be False.
try:
Set(one, two, three, four).nonmembership_object(four).unfold()
assert False, "Should not make it to this point."
except InstantiationFailure as the_error:
print("Proof/Instantiation Failure: {}".format(the_error))
```

In [207]:

```
# Will fail to conclude if non-membership is simply unknown to be True or not.
try:
Set(A, B, C).nonmembership_object(D).unfold()
assert False, "Should not make it to this point."
except InstantiationFailure as the_error:
print("Proof/Instantiation Failure: {}".format(the_error))
```

`nonmembership_object().deduce_in_bool()`

¶In [208]:

```
Set(A, B, C).nonmembership_object(B).deduce_in_bool()
```

In [209]:

```
Set(one, two, three).nonmembership_object(four).deduce_in_bool()
```

In [210]:

```
Set(one, Add(one, one), three).nonmembership_object(four).deduce_in_bool()
```

In [211]:

```
%end demonstrations
```