diff --git a/packages/proveit/_core_/expression/composite/_sub_theories_.txt b/packages/proveit/_core_/expression/composite/_sub_theories_.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/_core_/expression/conditional/_sub_theories_.txt b/packages/proveit/_core_/expression/conditional/_sub_theories_.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/_core_/expression/label/_sub_theories_.txt b/packages/proveit/_core_/expression/label/_sub_theories_.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/_core_/expression/lambda_expr/_sub_theories_.txt b/packages/proveit/_core_/expression/lambda_expr/_sub_theories_.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/_core_/expression/operation/_sub_theories_.txt b/packages/proveit/_core_/expression/operation/_sub_theories_.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/_sub_theories_.txt b/packages/proveit/_sub_theories_.txt
index 37abe2ca19c6..29f2a16b4cbe 100644
--- a/packages/proveit/_sub_theories_.txt
+++ b/packages/proveit/_sub_theories_.txt
@@ -4,5 +4,6 @@ numbers
 trigonometry
 abstract_algebra
 linear_algebra
+graphs
 statistics
 physics
diff --git a/packages/proveit/graphs/__init__.py b/packages/proveit/graphs/__init__.py
new file mode 100644
index 000000000000..8beff63a88b5
--- /dev/null
+++ b/packages/proveit/graphs/__init__.py
@@ -0,0 +1,25 @@
+from .graph import (Connected, FiniteGraphsLiteral, Graph,
+          GraphsLiteral, HasEulerianCircuit, HasEulerianTrail,
+          Order, Size)
+from .edges import Edges
+from .inclusion import NotSubgraph, Subgraph
+from .membership import (
+      GraphMembership, GraphNonmembership, InGraph, NotInGraph)
+# from .paths import Path, Paths
+from .paths_of import PathsOf
+from .union import GraphUnion
+from .vertices import AdjacentVertices, Degree, Vertices
+from .walks import (BeginningVertex, Circuits, ClosedTrails,
+          ClosedWalk, ClosedWalks, Cycles,
+          EdgeSequence, EdgeSet, EndingVertex, EndVertices,
+          EulerianCircuits, EulerianTrails, Paths, Trails,
+          WalkLength, Walks)
+
+
+# KEEP THE FOLLOWING IN __init__.py FOR THEORY PACKAGES.
+#  Make additions above, or add to sys.modules[__name__].__dict__ below.
+# This allows us to import common expression, axioms, and theorems of
+# the theory package directly from the package.
+import sys
+from proveit._core_.theory import TheoryPackage
+sys.modules[__name__] = TheoryPackage(__name__, __file__, locals())
diff --git a/packages/proveit/graphs/_brief_description_.txt b/packages/proveit/graphs/_brief_description_.txt
new file mode 100644
index 000000000000..976dda64228c
--- /dev/null
+++ b/packages/proveit/graphs/_brief_description_.txt
@@ -0,0 +1 @@
+basic graph theory concepts for simple graphs
diff --git a/packages/proveit/graphs/_sub_theories_.txt b/packages/proveit/graphs/_sub_theories_.txt
new file mode 100644
index 000000000000..92fc48186f08
--- /dev/null
+++ b/packages/proveit/graphs/_sub_theories_.txt
@@ -0,0 +1,6 @@
+inclusion
+union
+membership
+paths
+paths_of
+walks
diff --git a/packages/proveit/graphs/_theory_nbs_/__init__.py b/packages/proveit/graphs/_theory_nbs_/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/_theory_nbs_/axioms.ipynb b/packages/proveit/graphs/_theory_nbs_/axioms.ipynb
new file mode 100644
index 000000000000..e1fba9d73111
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/axioms.ipynb
@@ -0,0 +1,297 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Axioms for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the axioms of a theory:\n",
+    "%axioms_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "from proveit import k, u, v, E, G, P, V, W\n",
+    "from proveit.logic import And, Equals, Exists, Forall, InClass, InSet\n",
+    "from proveit.logic.sets import Card, Set, KPowerSet, SetOfAll, SubsetEq\n",
+    "from proveit.numbers import two, Natural\n",
+    "from proveit.graphs import (AdjacentVertices, ClosedWalks, Connected,\n",
+    "        Degree, EdgeSequence, EdgeSet, Edges, EndVertices, Graph, Graphs,\n",
+    "        HasEulerianCircuit, HasEulerianTrail, Order, Paths, Size, Vertices,\n",
+    "        WalkLength, Walks)\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin axioms"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "graph_v_e_in_graphs = (\n",
+    "    Forall(V,\n",
+    "    Forall(E, InClass(Graph(V, E), Graphs), conditions = [SubsetEq(E, KPowerSet(V, two))])\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "graph_order_def = Forall(G, \n",
+    "       Equals(Order(G), Card(Vertices(G))),\n",
+    "       domain = Graphs\n",
+    "      )"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "graph_size_def = Forall(G, \n",
+    "       Equals(Size(G), Card(Edges(G))),\n",
+    "       domain = Graphs\n",
+    "      )"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "connected_def = (\n",
+    "    Forall(G,\n",
+    "          Equals(Connected(G),\n",
+    "                 Forall((u, v),\n",
+    "                         Exists((P, k), Equals(EndVertices(P), Set(u, v)), domains = [Paths(k, G), Natural]),\n",
+    "                         domain = Vertices(G))),\n",
+    "          domain = Graphs\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "vertices_def = (\n",
+    "    Forall(V,\n",
+    "    Forall(E, Equals(Vertices(Graph(V, E)), V), conditions = [SubsetEq(E, KPowerSet(V, two))])\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "edges_def = (\n",
+    "    Forall(V,\n",
+    "    Forall(E, Equals(Edges(Graph(V, E)), E), conditions = [SubsetEq(E, KPowerSet(V, two))])\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "##### Characterizing an edge: An edge in a graph $G(V, E)$ is a 2-element subset of the vertex set $V$. The set of 2-element subsets of $V$ is denoted by $[V]^{2}$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "edges_def_by_vertices = Forall(G, SubsetEq(Edges(G), KPowerSet(Vertices(G), two)), domain = Graphs)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "##### The _degree_ of a vertex $v$ in a graph $G$, denoted $\\text{deg}_{G}(v)$, is defined as the number of edges incident with the vertex $v$. We can formulate this in different ways, but one fairly efficient formulation looks like this:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "vertex_deg_def = (\n",
+    "        Forall(G, Forall(v,\n",
+    "                    Equals(\n",
+    "                        Degree(v, G),\n",
+    "                        Card(SetOfAll(E, E, condition = InSet(v, E), domain = Edges(G)))),\n",
+    "        domain = Vertices(G)), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Two vertices $u$ and $v$ in a graph $G$ are __adjacent__ if there is an edge directly connecting them — _i.e._, $u$ and $v$ are adjacent in graph G if $\\{u, v\\}$ is an edge of $G$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "adjacent_vertices_def = (\n",
+    "    Forall(G,\n",
+    "           Forall((u, v),\n",
+    "                  Equals(AdjacentVertices(u, v, G), InSet(Set(u, v), Edges(G))),\n",
+    "                  domain = Vertices(G)),\n",
+    "           domain = Graphs\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The following three items might be an elegant way to simply use a broader class called Adjacent, which then is axiomatically defined to Or or XOr over the AdjacentVertices and AdjacentEdges classes (discussed w/WW on 20250627):"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Forall(G,\n",
+    "#            Forall((u, v),\n",
+    "#                   Equals(AdjacentVertices(u, v, G), InSet(Set(u, v), Edges(G))),\n",
+    "#                   ),\n",
+    "#            domain = Graphs()\n",
+    "#     )"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Forall(G,\n",
+    "#            Forall((u, v),\n",
+    "#                   Equals(Adjacent(u, v, G), Or(AdjacentVertices(u, v, G), AdjacentEdges(u, v, G))),\n",
+    "#                   ),\n",
+    "#            domain = Graphs()\n",
+    "#     )"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# class Adjacent(x, y, G):\n",
+    "#     '''\n",
+    "#     Adjacent(x, y, G) is the claim that x and y are adjacent in G.\n",
+    "#     x, y could be vertices, or they could be edges ...\n",
+    "#     '''\n",
+    "#     then define the operator, etc"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Characterizing what it means for a graph $G$ to have an Eulerian circuit: this means that there exists a closed walk $W$ in graph $G$ such that $W$ uses each and every edge of $G$ exactly once. That can be challenging to express literally in _Prove-It_, but an equivalent definition involves a walk whose edge set is the same as the graph's edge set (capturing the idea that we use all of the edges of $G$), while the length of the walk (i.e. the number of edges) is the same as the cardinality of the graph's edge set (_i.e._, the “size” of the graph) (capturing the idea that we have no repeated edges). The cardinality constraint is captured by taking $W$ to be in the set of $\\texttt{Walks}(||G||, G)$, _i.e._ walks of exactly the desired length $||G||$. "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "has_eulerian_circuit_def = Forall(G,\n",
+    "    Equals(\n",
+    "    HasEulerianCircuit(G),\n",
+    "    Exists(W, Equals(EdgeSet(W), Edges(G)),\n",
+    "       domain = ClosedWalks(Size(G), G))\n",
+    "    ),\n",
+    "conditions = [Connected(G)],\n",
+    "domain = Graphs\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Characterizing what it means for a graph $G$ to have an Eulerian trail: this means that there exists a walk $W$ in graph $G$ such that $W$ uses each and every edge of $G$ exactly once. If the walk is closed, then the Eulerian trail is an Eulerian circuit (see above). As in the case of the Eulerian circuit, the definition can be difficult to express literally in _Prove-It_, but an equivalent definition involves a walk whose edge set is the same as the graph's edge set (capturing the idea that we use all of the edges of $G$), while the length of the walk (i.e. the number of edges) is the same as the cardinality of the graph's edge set (_i.e._, the “size” of the graph) (capturing the idea that we have no repeated edges)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "has_eulerian_trail_def = Forall(G,\n",
+    "    Equals(\n",
+    "    HasEulerianTrail(G),\n",
+    "    Exists(W, Equals(EdgeSet(W), Edges(G)),\n",
+    "       domain = Walks(Size(G), G))\n",
+    "    ),\n",
+    "conditions = [Connected(G)],\n",
+    "domain = Graphs\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end axioms"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/common.ipynb b/packages/proveit/graphs/_theory_nbs_/common.ipynb
new file mode 100644
index 000000000000..d846ba23e451
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/common.ipynb
@@ -0,0 +1,114 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Common expressions for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the common expressions of a theory:\n",
+    "%common_expressions_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "from proveit import i, n, x, G, ExprRange, IndexedVar, var_range, Variable\n",
+    "from proveit.logic import And, Or\n",
+    "from proveit.logic.sets import InSet\n",
+    "from proveit.numbers import one\n",
+    "from proveit.graphs import FiniteGraphsLiteral, GraphsLiteral, InGraph, NotInGraph"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin common"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Graphs = GraphsLiteral()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "FiniteGraphs = FiniteGraphsLiteral()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_prime, V_prime, E_prime = (\n",
+    "        Variable(\"G'\", r\"G'\", fence_when_forced=True),\n",
+    "        Variable(\"V'\", r\"V'\", fence_when_forced=True),\n",
+    "        Variable(\"E'\", r\"E'\", fence_when_forced=True)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_1_to_n = var_range(G, one, n)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "x_in_any_G_1_to_n = Or(ExprRange(i, InGraph(x, IndexedVar(G, i)), one, n))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "x_not_in_any_G_1_to_n = And(ExprRange(i, NotInGraph(x, IndexedVar(G, i)), one, n))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end common"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/demonstrations.ipynb b/packages/proveit/graphs/_theory_nbs_/demonstrations.ipynb
new file mode 100644
index 000000000000..e1a2e9396cb2
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/demonstrations.ipynb
@@ -0,0 +1,662 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Demonstrations for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "from proveit import e, u, v, x, E, G, P, V\n",
+    "from proveit.logic import InClass, InSet, KPowerSet, NotInSet, SubsetEq\n",
+    "from proveit.numbers import two, Natural\n",
+    "from proveit.graphs import (\n",
+    "        AdjacentVertices, Connected, Degree, Edges, Graph, Graphs,\n",
+    "        HasEulerianCircuit, HasEulerianTrail, Order, Size, Vertices)\n",
+    "%begin demonstrations"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Graphs, Vertices, Edges, and Related Constructions & Methods $G = \\textrm{Graph}(V, E)$"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Miscellaneous Testing\n",
+    "Testing various classes and methods related to the class of graphs and the representation of a graph $G = (V, E)$ consisting of a set $V$ of vertices and a set $E$ of edges. Some of this material could eventually be integrated into the `_demonstrations_` page and/or deleted as development continues."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "example_graph = Graph(V, E)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InClass(example_graph, Graphs)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# the order of a graph G is the number of vertices in G\n",
+    "Order(G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Order(G).definition(assumptions = [InClass(G, Graphs)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# the size of a graph G is the number of edges in G\n",
+    "Size(G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Size(G).definition(assumptions = [InClass(G, Graphs)])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "$\\texttt{Size}(G) \\in \\mathbb{N}$ is derived automatically as an incidental/side-effect:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(Size(G), Natural).prove(assumptions = [InClass(G, Graphs)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# assert that the example graph is connected\n",
+    "Connected(G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Connected(G).definition(assumptions = [InClass(G, Graphs)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# assert that G has an Eulerian trail\n",
+    "HasEulerianTrail(G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# assert that G has an Eulerian circuit\n",
+    "HasEulerianCircuit(G)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Vertices of a Graph $G$: $\\mathrm{Vertices}(\\mathrm{Graph}(V, E)) = V$"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### General Constructions"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G = Graph(V, E)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Vertices(G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(v, Vertices(G))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(v, Vertices(G))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Degree(v, G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Vertices(P)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(v, Vertices(P))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "AdjacentVertices(u, v, G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "AdjacentVertices(u, v, G).definition(assumptions = [InClass(G, Graphs), InSet(u, Vertices(G)), InSet(v, Vertices(G))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### VerticesMembership methods"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`VerticesMembership.definition()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(v, Vertices(Graph(V, E))).definition(assumptions = [SubsetEq(E, KPowerSet(V, two))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(v, Vertices(G)).definition(assumptions = [SubsetEq(E, KPowerSet(V, two))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`VerticesMembership.as_defined()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(v, Vertices(G)).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# the domain's argument must be an explicit Graph with specified vertex set\n",
+    "from proveit import H\n",
+    "try:\n",
+    "    InSet(v, Vertices(H)).as_defined()\n",
+    "except Exception as the_exception:\n",
+    "    print(f\"Exception: {the_exception}\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`VerticesMembership.unfold()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(v, Vertices(G)).unfold(assumptions = [SubsetEq(E, KPowerSet(V, two)), InSet(v, Vertices(G))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`VerticesMembership.conclude()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(v, Vertices(G)).conclude(assumptions = [SubsetEq(E, KPowerSet(V, two)), InSet(v, V)])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Vertices Nonmembership methods"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`VerticesNonmembership.definition()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(v, Vertices(G)).definition(assumptions = [SubsetEq(E, KPowerSet(V, two))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`VerticesNonmembership.as_defined()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(v, Vertices(G)).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# the domain's argument must be an explicit Graph with specified vertex set\n",
+    "from proveit import H\n",
+    "try:\n",
+    "    NotInSet(v, Vertices(H)).as_defined()\n",
+    "except Exception as the_exception:\n",
+    "    print(f\"Exception: {the_exception}\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`VerticesNonmembership.unfold()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(v, Vertices(G)).unfold(assumptions = [SubsetEq(E, KPowerSet(V, two)), NotInSet(v, Vertices(G))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`VerticesNonmembership.conclude()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(v, Vertices(G)).conclude(assumptions = [SubsetEq(E, KPowerSet(V, two)), NotInSet(v, V)])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Edges of a Graph $G$: $\\mathrm{Edges}(\\mathrm{G}(V, E)) = E$"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Basic Constructions"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G = Graph(V, E)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Edges(G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(e, Edges(G))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(e, Edges(G))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### EdgesMembership methods"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`EdgesMembership.definition()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(e, Edges(G)).definition(assumptions = [SubsetEq(E, KPowerSet(V, two))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`EdgesMembership.as_defined()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(e, Edges(G)).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "try:\n",
+    "    InSet(e, Edges(H)).as_defined()\n",
+    "except Exception as the_exception:\n",
+    "    print(f\"Exception: {the_exception}\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`EdgesMembership.unfold()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(e, Edges(G)).unfold(assumptions = [SubsetEq(E, KPowerSet(V, two)), InSet(e, Edges(G))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`EdgesMembership.conclude()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(e, Edges(G)).conclude(assumptions = [SubsetEq(E, KPowerSet(V, two)), InSet(e, E)])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Nonmembership methods"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`EdgesNonmembership.definition()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(e, Edges(G)).definition(assumptions = [SubsetEq(E, KPowerSet(V, two))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`EdgesNonmembership.as_defined()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(e, Edges(G)).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "try:\n",
+    "    NotInSet(e, Edges(H)).as_defined()\n",
+    "except Exception as the_exception:\n",
+    "    print(f\"Exception: {the_exception}\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`EdgesNonmembership.unfold()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(e, Edges(G)).unfold(assumptions = [SubsetEq(E, KPowerSet(V, two)), NotInSet(e, Edges(G))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`EdgesNonmembership.conclude()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(e, Edges(G)).conclude(assumptions = [SubsetEq(E, KPowerSet(V, two)), NotInSet(e, E)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end demonstrations"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/all_even_vertices_imp_euler_circuit/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/all_even_vertices_imp_euler_circuit/thm_proof.ipynb
new file mode 100644
index 000000000000..9540934f9311
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/all_even_vertices_imp_euler_circuit/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#all_even_vertices_imp_euler_circuit\">all_even_vertices_imp_euler_circuit</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving all_even_vertices_imp_euler_circuit"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/deg_is_natural/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/deg_is_natural/thm_proof.ipynb
new file mode 100644
index 000000000000..4b11317ab94f
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/deg_is_natural/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#deg_is_natural\">deg_is_natural</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving deg_is_natural"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/edges_membership_def/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/edges_membership_def/thm_proof.ipynb
new file mode 100644
index 000000000000..25120d639192
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/edges_membership_def/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#edges_membership_def\">edges_membership_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving edges_membership_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/edges_membership_folding/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/edges_membership_folding/thm_proof.ipynb
new file mode 100644
index 000000000000..2047bcc97c7e
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/edges_membership_folding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#edges_membership_folding\">edges_membership_folding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving edges_membership_folding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/edges_membership_unfolding/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/edges_membership_unfolding/thm_proof.ipynb
new file mode 100644
index 000000000000..80df877c10b8
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/edges_membership_unfolding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#edges_membership_unfolding\">edges_membership_unfolding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving edges_membership_unfolding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/edges_nonmembership_def/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/edges_nonmembership_def/thm_proof.ipynb
new file mode 100644
index 000000000000..e2668d8490a7
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/edges_nonmembership_def/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#edges_nonmembership_def\">edges_nonmembership_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving edges_nonmembership_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/edges_nonmembership_folding/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/edges_nonmembership_folding/thm_proof.ipynb
new file mode 100644
index 000000000000..6a0e43aa29db
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/edges_nonmembership_folding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#edges_nonmembership_folding\">edges_nonmembership_folding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving edges_nonmembership_folding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/edges_nonmembership_unfolding/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/edges_nonmembership_unfolding/thm_proof.ipynb
new file mode 100644
index 000000000000..24900f7ddd6a
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/edges_nonmembership_unfolding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#edges_nonmembership_unfolding\">edges_nonmembership_unfolding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving edges_nonmembership_unfolding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/euler_circuit_iff_all_even_vertices/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/euler_circuit_iff_all_even_vertices/thm_proof.ipynb
new file mode 100644
index 000000000000..bcad989b0c12
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/euler_circuit_iff_all_even_vertices/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#euler_circuit_iff_all_even_vertices\">euler_circuit_iff_all_even_vertices</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving euler_circuit_iff_all_even_vertices"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/euler_circuit_imp_all_even_vertices/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/euler_circuit_imp_all_even_vertices/thm_proof.ipynb
new file mode 100644
index 000000000000..d76f11d6c5ae
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/euler_circuit_imp_all_even_vertices/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#euler_circuit_imp_all_even_vertices\">euler_circuit_imp_all_even_vertices</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving euler_circuit_imp_all_even_vertices"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/euler_trail_between_odd_vertices/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/euler_trail_between_odd_vertices/thm_proof.ipynb
new file mode 100644
index 000000000000..0a5d84a88929
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/euler_trail_between_odd_vertices/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#euler_trail_between_odd_vertices\">euler_trail_between_odd_vertices</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving euler_trail_between_odd_vertices"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/euler_trail_guarantee/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/euler_trail_guarantee/thm_proof.ipynb
new file mode 100644
index 000000000000..dc957abbb4b2
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/euler_trail_guarantee/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#euler_trail_guarantee\">euler_trail_guarantee</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving euler_trail_guarantee"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/euler_trail_imp_two_odd_vertices/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/euler_trail_imp_two_odd_vertices/thm_proof.ipynb
new file mode 100644
index 000000000000..c027d60ffb47
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/euler_trail_imp_two_odd_vertices/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#euler_trail_imp_two_odd_vertices\">euler_trail_imp_two_odd_vertices</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving euler_trail_imp_two_odd_vertices"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/even_number_of_odd_vertices/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/even_number_of_odd_vertices/thm_proof.ipynb
new file mode 100644
index 000000000000..f92a06557af2
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/even_number_of_odd_vertices/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#even_number_of_odd_vertices\">even_number_of_odd_vertices</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving even_number_of_odd_vertices"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/graph_size_in_natural/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/graph_size_in_natural/thm_proof.ipynb
new file mode 100644
index 000000000000..cf568b63eec0
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/graph_size_in_natural/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#graph_size_in_natural\">graph_size_in_natural</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving graph_size_in_natural"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/total_deg_is_even/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/total_deg_is_even/thm_proof.ipynb
new file mode 100644
index 000000000000..cc93b5b2c789
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/total_deg_is_even/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#total_deg_is_even\">total_deg_is_even</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving total_deg_is_even"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/total_deg_is_twice_num_of_edges/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/total_deg_is_twice_num_of_edges/thm_proof.ipynb
new file mode 100644
index 000000000000..99fc3622f1b4
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/total_deg_is_twice_num_of_edges/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#total_deg_is_twice_num_of_edges\">total_deg_is_twice_num_of_edges</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving total_deg_is_twice_num_of_edges"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/two_odd_vertices_imp_euler_trail/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/two_odd_vertices_imp_euler_trail/thm_proof.ipynb
new file mode 100644
index 000000000000..d191dd692b5c
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/two_odd_vertices_imp_euler_trail/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#two_odd_vertices_imp_euler_trail\">two_odd_vertices_imp_euler_trail</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving two_odd_vertices_imp_euler_trail"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/vertices_membership_def/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/vertices_membership_def/thm_proof.ipynb
new file mode 100644
index 000000000000..445e5a13ce5d
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/vertices_membership_def/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#vertices_membership_def\">vertices_membership_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving vertices_membership_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/vertices_membership_folding/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/vertices_membership_folding/thm_proof.ipynb
new file mode 100644
index 000000000000..3beb72dc1de4
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/vertices_membership_folding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#vertices_membership_folding\">vertices_membership_folding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving vertices_membership_folding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/vertices_membership_unfolding/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/vertices_membership_unfolding/thm_proof.ipynb
new file mode 100644
index 000000000000..280371a5d8f4
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/vertices_membership_unfolding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#vertices_membership_unfolding\">vertices_membership_unfolding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving vertices_membership_unfolding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/vertices_nonmembership_def/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/vertices_nonmembership_def/thm_proof.ipynb
new file mode 100644
index 000000000000..eae92b761ae7
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/vertices_nonmembership_def/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#vertices_nonmembership_def\">vertices_nonmembership_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving vertices_nonmembership_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/vertices_nonmembership_folding/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/vertices_nonmembership_folding/thm_proof.ipynb
new file mode 100644
index 000000000000..fa0c232fbc7a
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/vertices_nonmembership_folding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#vertices_nonmembership_folding\">vertices_nonmembership_folding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving vertices_nonmembership_folding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/proofs/vertices_nonmembership_unfolding/thm_proof.ipynb b/packages/proveit/graphs/_theory_nbs_/proofs/vertices_nonmembership_unfolding/thm_proof.ipynb
new file mode 100644
index 000000000000..30759e7d2b3f
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/proofs/vertices_nonmembership_unfolding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#vertices_nonmembership_unfolding\">vertices_nonmembership_unfolding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving vertices_nonmembership_unfolding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/theorems.ipynb b/packages/proveit/graphs/_theory_nbs_/theorems.ipynb
new file mode 100644
index 000000000000..b59c4465fef9
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/theorems.ipynb
@@ -0,0 +1,452 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theorems (or conjectures) for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the theorems of a theory:\n",
+    "%theorems_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "from proveit import e, v, E, G, V, W\n",
+    "from proveit.logic import Equals, Forall, Iff, Implies, InSet, InClass, NotInSet\n",
+    "from proveit.logic.sets import Card, KPowerSet, SetOfAll, SubsetEq\n",
+    "from proveit.numbers import (zero, two, greater_eq, IntegerEven, IntegerOdd,\n",
+    "        Mult, Natural, Sum)\n",
+    "from proveit.graphs import (\n",
+    "        Connected, Degree, Edges, EndVertices, EulerianTrails, Graph, Graphs,\n",
+    "        HasEulerianCircuit, HasEulerianTrail, Size, Vertices)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin theorems"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Some Basic Facts About Vertices and Degree\n",
+    "\n",
+    "Later, when XOr, IntegerEven, and IntegerOdd classes are available, update theorems with the fact that $\\texttt{deg}(v)$ is either even or odd (but not both). Currently XOr class is in a separate branch, and the even and odd classes are their own separate branch."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "deg_is_natural = (\n",
+    "Forall(G,\n",
+    "Forall(v,\n",
+    "       InSet(Degree(v, G), Natural),\n",
+    "domain = Vertices(G)),\n",
+    "domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Total Degree of Graph $G$.\n",
+    "##### The _total degree_ of a graph $G$ is twice the number of edges in $G$ (this is what Chartrand & Zhang (2012, pg 32) call “The First Theorem of Graph Theory” and what is more generally called the “degree sum formula”) . "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "total_deg_is_twice_num_of_edges = (\n",
+    "        Forall(G, Equals(Sum(v, Degree(v, G), domain = Vertices(G)),\n",
+    "                         Mult(two, Card(Edges(G)))),\n",
+    "        domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "##### An immediate corollary to the First Theorem is that the total degree is alway even. This is often referred to as the “Handshake Lemma” or “Handshaking Lemma” (although that name is sometimes applied to the degree sum formula above, and sometimes even to the next theorem about there being an even number of odd vertices)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "total_deg_is_even = (\n",
+    "        Forall(G, InSet(Sum(v, Degree(v, G), domain = Vertices(G)),\n",
+    "                         IntegerEven),\n",
+    "        domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "##### And because the total degree is always even, there must be an even number of odd-degree vertices."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "even_number_of_odd_vertices = (\n",
+    "        Forall(G,\n",
+    "        InSet(Card(SetOfAll(v, v, condition = InSet(Degree(v, G), IntegerOdd), domain = Vertices(G))),\n",
+    "              IntegerEven),\n",
+    "        domain = Graphs\n",
+    "        )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "graph_size_in_natural = Forall(G, InSet(Size(G), Natural), domain = Graphs)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Membership Theorems for Vertex and Edge sets $V$ and $E$"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "vertices_membership_def = (\n",
+    "    Forall((v, V),\n",
+    "    Forall(E, Equals(InSet(v, Vertices(Graph(V, E))), InSet(v, V)), conditions = [SubsetEq(E, KPowerSet(V, two))])\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "vertices_membership_unfolding = (\n",
+    "    Forall(V,\n",
+    "        Forall((v, E), InSet(v, V),\n",
+    "               conditions = [SubsetEq(E, KPowerSet(V, two)), InSet(v, Vertices(Graph(V, E)))])\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "vertices_membership_folding = (\n",
+    "    Forall(V,\n",
+    "        Forall((v, E), InSet(v, Vertices(Graph(V, E))),\n",
+    "               conditions = [SubsetEq(E, KPowerSet(V, two)), InSet(v, V)])\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "vertices_nonmembership_def = (\n",
+    "    Forall(V,\n",
+    "        Forall((v, E), Equals(NotInSet(v, Vertices(Graph(V, E))), NotInSet(v, V)),\n",
+    "               conditions = [SubsetEq(E, KPowerSet(V, two))])\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "vertices_nonmembership_unfolding = (\n",
+    "    Forall(V,\n",
+    "        Forall((v, E), NotInSet(v, V),\n",
+    "               conditions = [SubsetEq(E, KPowerSet(V, two)), NotInSet(v, Vertices(Graph(V, E)))])\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "vertices_nonmembership_folding = (\n",
+    "    Forall(V,\n",
+    "        Forall((v, E), NotInSet(v, Vertices(Graph(V, E))),\n",
+    "               conditions = [SubsetEq(E, KPowerSet(V, two)), NotInSet(v, V)])\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "edges_membership_def = (\n",
+    "    Forall(V,\n",
+    "    Forall((e, E), Equals(InSet(e, Edges(Graph(V, E))), InSet(e, E)), conditions = [SubsetEq(E, KPowerSet(V, two))])\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "edges_membership_unfolding = (\n",
+    "    Forall(V,\n",
+    "        Forall((e, E), InSet(e, E),\n",
+    "               conditions = [SubsetEq(E, KPowerSet(V, two)), InSet(e, Edges(Graph(V, E)))])\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "edges_membership_folding = (\n",
+    "    Forall(V,\n",
+    "        Forall((e, E), InSet(e, Edges(Graph(V, E))),\n",
+    "               conditions = [SubsetEq(E, KPowerSet(V, two)), InSet(e, E)])\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "edges_nonmembership_def = (\n",
+    "    Forall(V,\n",
+    "        Forall((e, E), Equals(NotInSet(e, Edges(Graph(V, E))), NotInSet(e, E)),\n",
+    "               conditions = [SubsetEq(E, KPowerSet(V, two))])\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "edges_nonmembership_unfolding = (\n",
+    "    Forall(V,\n",
+    "        Forall((e, E), NotInSet(e, E),\n",
+    "               conditions = [SubsetEq(E, KPowerSet(V, two)), NotInSet(e, Edges(Graph(V, E)))])\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "edges_nonmembership_folding = (\n",
+    "    Forall(V,\n",
+    "        Forall((e, E), NotInSet(e, Edges(Graph(V, E))),\n",
+    "               conditions = [SubsetEq(E, KPowerSet(V, two)), NotInSet(e, E)])\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Theorems about the existence of an Euler circuit or Euler trail in a simple connected graph $G$:\n",
+    "\n",
+    "(1) a connected simple graph $G$ has an Eulerian circuit if and only if $G$ has all even vertices;\n",
+    "\n",
+    "(2) a connected simple graph $G$ has an Eulerian trail if and only if $G$ has exactly 2 odd vertices (and every Eulerian trail in $G$ will start at one of the odd vertices and end at the other odd vertex)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "euler_circuit_iff_all_even_vertices = (\n",
+    "    Forall(G,\n",
+    "           Iff(HasEulerianCircuit(G),\n",
+    "               Forall(v, InSet(Degree(v, G), IntegerEven), domain = Vertices(G))),\n",
+    "    conditions = [Connected(G)],\n",
+    "    domain = Graphs\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "euler_circuit_imp_all_even_vertices = (\n",
+    "    Forall(G,\n",
+    "           Forall(v, InSet(Degree(v, G), IntegerEven), domain = Vertices(G)),\n",
+    "    conditions = [HasEulerianCircuit(G)],\n",
+    "    domain = Graphs\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "all_even_vertices_imp_euler_circuit = (\n",
+    "    Forall(G,\n",
+    "           HasEulerianCircuit(G),\n",
+    "    conditions = [Connected(G), Forall(v, InSet(Degree(v, G), IntegerEven), domain = Vertices(G))],\n",
+    "    domain = Graphs\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "euler_trail_guarantee = (\n",
+    "Forall(G,\n",
+    "    Iff(\n",
+    "    HasEulerianTrail(G),\n",
+    "    Equals(Card(SetOfAll(v, v, condition = InSet(Degree(v, G), IntegerOdd), domain = Vertices(G))), two)\n",
+    "    ),\n",
+    "conditions = [Connected(G)],\n",
+    "domain = Graphs\n",
+    ")\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "euler_trail_imp_two_odd_vertices = (\n",
+    "Forall(G,\n",
+    "    Equals(Card(SetOfAll(v, v, condition = InSet(Degree(v, G), IntegerOdd), domain = Vertices(G))), two),\n",
+    "conditions = [HasEulerianTrail(G)],\n",
+    "domain = Graphs\n",
+    ")\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "two_odd_vertices_imp_euler_trail = (\n",
+    "Forall(G,\n",
+    "    HasEulerianTrail(G),\n",
+    "conditions = [Connected(G), Equals(Card(SetOfAll(v, v, condition = InSet(Degree(v, G), IntegerOdd), domain = Vertices(G))), two)],\n",
+    "domain = Graphs\n",
+    ")\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "euler_trail_between_odd_vertices = (\n",
+    "Forall(G,\n",
+    "    Forall(W,\n",
+    "           Equals(EndVertices(W),\n",
+    "                  SetOfAll(v, v, condition = InSet(Degree(v, G), IntegerOdd),\n",
+    "                           domain = Vertices(G))),\n",
+    "           domain = EulerianTrails(G)),\n",
+    "conditions = [Connected(G), HasEulerianTrail(G)],\n",
+    "domain = Graphs\n",
+    ")\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end theorems"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/_theory_nbs_/theory.ipynb b/packages/proveit/graphs/_theory_nbs_/theory.ipynb
new file mode 100644
index 000000000000..116597aac7d5
--- /dev/null
+++ b/packages/proveit/graphs/_theory_nbs_/theory.ipynb
@@ -0,0 +1,40 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theory of <a class=\"ProveItLink\" href=\"../../_theory_nbs_/theory.ipynb\">proveit</a>.graphs\n",
+    "========\n",
+    "\n",
+    "Basic concepts in graph theory for undirected simple graphs (undirected graphs without loops and without parallel edges)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "%theory # toggles between interactive and static modes"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/edges.py b/packages/proveit/graphs/edges.py
new file mode 100644
index 000000000000..5620d95f9db4
--- /dev/null
+++ b/packages/proveit/graphs/edges.py
@@ -0,0 +1,30 @@
+from proveit import Function, Literal
+
+class Edges(Function):
+    '''
+    Given a graph G(V, E) with vertex set V and edge set E,
+    Edges(G(V, E)) represents the set E of edges ---
+    that is, Edges(G(V,E)) = E. The notation will use Edges(G),
+    in which case we might see things like Edges(G) = E.
+    '''
+
+    # the literal operator of the Vertices operation
+    _operator_ = Literal(string_format='Edges',
+                         latex_format=r'\mathrm{Edges}',
+                         theory=__file__)
+
+    def __init__(self, G, *, styles=None):
+        '''
+        Given a graph G = Graph(V,E), represent the edge set of G.
+        '''
+        self.graph = G
+        Function.__init__(
+                self, Edges._operator_, G, styles=styles)
+
+    def membership_object(self, element):
+        from .edges_membership import EdgesMembership
+        return EdgesMembership(element, self)
+
+    def nonmembership_object(self, element):
+        from .edges_membership import EdgesNonmembership
+        return EdgesNonmembership(element, self)
diff --git a/packages/proveit/graphs/edges_membership.py b/packages/proveit/graphs/edges_membership.py
new file mode 100644
index 000000000000..634c32fbea31
--- /dev/null
+++ b/packages/proveit/graphs/edges_membership.py
@@ -0,0 +1,152 @@
+from proveit import e, E, V, equality_prover, prover
+from proveit.logic import SetMembership, SetNonmembership
+from proveit.graphs import Graph
+
+
+class EdgesMembership(SetMembership):
+    '''
+    Defines methods that apply to membership in the set
+    Edges(G(V,E)) of the edges E of graph G.
+    '''
+
+    def __init__(self, element, domain):
+        SetMembership.__init__(self, element, domain)
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = [elem in Edges(Graph(V,E))], deduce and return:
+            [elem in Edges(Graph(V,E))] = [elem in E]
+        '''
+        from . import edges_membership_def
+        element = self.element
+        _V_sub  = self.domain.graph.vertex_set
+        _E_sub  = self.domain.graph.edge_set
+        return edges_membership_def.instantiate(
+                {e:element, V:_V_sub, E:_E_sub },auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self = [elem in Edges(Graph(V,E))], return: [elem in E]
+        (i.e. an expression, not a Judgment)
+        '''
+        if isinstance(self.domain.operand, Graph):
+            from proveit.logic import InSet
+            element = self.element
+            _E = self.domain.graph.edge_set
+            return InSet(element, _E)
+        else:
+            raise NotImplementedError(
+                "EdgesMembership.as_defined() was called on "
+                f"self = {self.expr} with domain = {self.expr.domain}, "
+                "but the method is implemented only for domains of the "
+                "form Edges(G) where G is an explicit Graph object "
+                "of the form G = Graph(V,E) with a named edge set E.")
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From self = [elem in Edges(Graph(V,E))],
+        derive and return [elem in E], knowing or assuming self.
+        '''
+        from . import edges_membership_unfolding
+        element = self.element
+        _V_sub  = self.domain.graph.vertex_set
+        _E_sub  = self.domain.graph.edge_set
+        return edges_membership_unfolding.instantiate(
+            {e:element, V:_V_sub, E:_E_sub}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on self = [elem in Edges(Graph(V,E))], and
+        knowing or assuming [elem in E] (and that E is a subset
+        of [V]^2, a subset of the set of 2-element subsets of V)
+        derive and return self.
+        '''
+        from . import edges_membership_folding
+        element = self.element
+        _V_sub  = self.domain.graph.vertex_set
+        _E_sub  = self.domain.graph.edge_set
+        return edges_membership_folding.instantiate(
+            {e:element, V:_V_sub, E:_E_sub}, auto_simplify=False)
+
+
+class EdgesNonmembership(SetNonmembership):
+    '''
+    Defines methods that apply to non-membership in the set
+    Edges(G(V,E)) of the edges E of graph G.
+    '''
+
+    def __init__(self, element, domain):
+        SetNonmembership.__init__(self, element, domain)
+
+    def side_effects(self, judgment):
+        '''
+        Currently no side-effects for EdgesNonmembership.
+        '''
+        return
+        yield
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = [elem not in Edges(Graph(V,E))], deduce and
+        return: [elem not in Edges(Graph(V,E))] = [elem not in E].
+        '''
+
+        from . import edges_nonmembership_def
+        element = self.element
+        _V_sub  = self.domain.graph.vertex_set
+        _E_sub  = self.domain.graph.edge_set
+        return edges_nonmembership_def.instantiate(
+                {e:element, V:_V_sub, E:_E_sub },auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self = [elem not in Edges(Graph(V,E))],
+        return: [elem not in E] (i.e. an expression, not a Judgment)
+        '''
+        if isinstance(self.domain.operand, Graph):
+            from proveit.logic import NotInSet
+            element = self.element
+            _E = self.domain.graph.edge_set
+            return NotInSet(element, _E)
+        else:
+            raise NotImplementedError(
+                "EdgesNonmembership.as_defined() was called on "
+                f"self = {self.expr} with domain = {self.expr.domain}, "
+                "but the method is implemented only for domains of the "
+                "form Edges(G) where G is an explicit Graph object "
+                "of the form G = Graph(V,E) with a named edge set E.")
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From self = [elem not in Edges(Graph(V,E))],
+        derive and return [elem not in E], knowing or assuming self,
+        (and that E is a subset of [V]^2, i.e., a subset of the set
+        of 2-element subsets of V).
+        '''
+        from . import edges_nonmembership_unfolding
+        element = self.element
+        _V_sub  = self.domain.graph.vertex_set
+        _E_sub  = self.domain.graph.edge_set
+        return edges_nonmembership_unfolding.instantiate(
+            {e:element, V:_V_sub, E:_E_sub}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on self = [elem not in Edges(Graph(V,E))], and
+        knowing or assuming [elem not in E] (and that E is a subset
+        of [V]^2, a subset of the set of 2-element subsets of V)
+        derive and return self.
+        '''
+        from . import edges_nonmembership_folding
+        element = self.element
+        _V_sub  = self.domain.graph.vertex_set
+        _E_sub  = self.domain.graph.edge_set
+        return edges_nonmembership_folding.instantiate(
+            {e:element, V:_V_sub, E:_E_sub}, auto_simplify=False)
+
diff --git a/packages/proveit/graphs/graph.py b/packages/proveit/graphs/graph.py
new file mode 100644
index 000000000000..b6d0f791c14f
--- /dev/null
+++ b/packages/proveit/graphs/graph.py
@@ -0,0 +1,322 @@
+from proveit import equality_prover, Function, Literal, prover
+from proveit import E, G, V
+from proveit.logic import ClassMembership
+
+
+class GraphsLiteral(Literal):
+    '''
+    Graphs represents the (mathematical) class of graphs, to which
+    any specific graph G = (V, E) of vertices and edges belongs.
+    This class might not be necessary, but is modeled on the
+    HilbertSpacesLiteral class in the linear_algebra/inner_products
+    theory package, and something similar involving the EmptySet in
+    logic/sets. 'Graphs' is then defined in the graphs/common nb
+    as Graphs = GraphsLiteral(). This class of graphs includes both
+    finite and infinite graphs, but is initially conceptualized as
+    the class of simple graphs (i.e. no loops and no parallel edges).
+    See FiniteGraphsLiteral() for the class of finite graphs.
+    '''
+
+    # the literal string for representing the class of Graphs
+    def __init__(self, *, styles=None):
+        Literal.__init__(self, string_format='Graphs', 
+                         latex_format=r'\textrm{Graphs}',
+                         styles=styles)
+
+    def membership_object(self, element):
+        return GraphsMembership(element, self)
+
+    @property
+    def is_proper_class(self):
+        '''
+        The class of Graphs is a proper class (i.e., instead of a set).
+        This indicates that InClass() should be used instead of
+        InSet() when this is a domain.
+        '''
+        return True
+
+
+class FiniteGraphsLiteral(Literal):
+    '''
+    FiniteGraphsLiteral() represents the (mathematical) class of
+    finite graphs (i.e., graphs with a finite set of vertices and
+    a finite set of edges), to which any specific finite graph
+    G = (V, E) of vertices and edges belongs. This class of graphs
+    is initially conceptualized as the class of simple finite graphs
+    (i.e. no loops and no parallel edges).
+    The string 'FiniteGraphs' is then defined in the graphs/common nb
+    as FiniteGraphs = FiniteGraphsLiteral(), and should be imported
+    from there.
+    '''
+
+    # the literal string for representing the class FiniteGraphs
+    def __init__(self, *, styles=None):
+        Literal.__init__(self, string_format='FiniteGraphs', 
+                         latex_format=r'\textrm{FiniteGraphs}',
+                         styles=styles)
+
+    def membership_object(self, element):
+        return FiniteGraphsMembership(element, self)
+
+    @property
+    def is_proper_class(self):
+        '''
+        The class of finite graphs is a proper class (i.e. instead
+        of a set). This method is used to indicates that InClass()
+        should be used instead of InSet() when FiniteGraphs is being
+        used as a domain.
+        '''
+        return True
+
+
+class GraphsMembership(ClassMembership):
+
+    def __init__(self, element, domain):
+        from . import Graphs
+        ClassMembership.__init__(self, element, domain)
+        if domain != Graphs:
+            raise TypeError("domain expected to be Graphs, not %s"
+                            %domain.__class__)
+
+    def side_effects(self, judgment):
+        '''
+        Yield side-effects when proving or assuming 'G in Graphs',
+        the main side-effect being that ||G|| in Natural.
+        This needs updated, since Graphs includes infinite graphs.
+        '''
+        yield self.derive_size_in_natural
+
+    # def conclude(): see if and when needed
+
+    @prover
+    def derive_size_in_natural(self, **defaults_config):
+        '''
+        From (G in Graphs), derive ||G|| in Natural, i.e. derive the
+        fact that the size of G (the number of edges in G) is a
+        Natural number. Called as a side-effect.
+        '''
+        from . import graph_size_in_natural
+        _G = self.element
+        return graph_size_in_natural.instantiate(
+                {G:_G}, auto_simplify=False)
+
+class FiniteGraphsMembership(ClassMembership):
+
+    def __init__(self, element, domain):
+        from . import FiniteGraphs
+        ClassMembership.__init__(self, element, domain)
+        if domain != FiniteGraphs:
+            raise TypeError(
+                f"Domain expected to be FiniteGraphs, not {domain.__class__}")
+
+    def side_effects(self, judgment):
+        '''
+        Yield side-effects when proving or assuming 'G in FiniteGraphs',
+        the main side-effect being that ||G|| in Natural.
+        Should extend this to also have |G| in Natural.
+        Should extend this to also have G in Graphs.
+        '''
+        yield self.derive_size_in_natural
+
+    # def conclude(): see if and when needed
+
+    @prover
+    def derive_size_in_natural(self, **defaults_config):
+        '''
+        From (G in FiniteGraphs), derive ||G|| in Natural, i.e.
+        derive the fact that the size of G (the number of edges in G)
+        is a Natural number. Called as a side-effect.
+        '''
+        from . import graph_size_in_natural
+        _G = self.element
+        return graph_size_in_natural.instantiate(
+                {G:_G}, auto_simplify=False)
+
+
+class Graph(Function):
+    '''
+    Graph(V, E) represents a graph with vertex set V and edge set E.
+    The vertex set V is conceptualized as a set of vertices such as
+    {x_1, ..., x_n} but might appear or be interpreted as a set of
+    geometric points such as {(x1, y1), (x2, y2), ..., (xn, yn)}.
+    The edge set E is conceptualized as a set of pairs of vertices,
+    such as {x1 x2, x2 x3, ..., xn x1}, which might eventually take
+    the form of a set of 2-element sets or a set of ordered pairs.
+    '''
+
+    # the literal operator of the Graph operation
+    _operator_ = Literal(string_format='Graph',
+                         latex_format=r'\textrm{Graph}',
+                         theory=__file__)
+
+    def __init__(self, V, E, *, styles=None):
+        '''
+        Create a graph G(V,E) with vertex set V and edge set E.
+        '''
+        self.vertex_set = V
+        self.edge_set   = E
+        Function.__init__(
+                self, Graph._operator_, (V, E), styles=styles)
+
+
+class Order(Function):
+    '''
+    Order(G), denoted |G|, represents the order of graph G, giving
+    the number of vertices in graph G. This will be equivalent to
+    |Vertices(G)|.
+    '''
+    # literal operator of the Order operation.
+    _operator_ = Literal(string_format='Order', theory=__file__)
+
+    def __init__(self, G, *, styles=None):
+        '''
+        Represent Order(G), the order of graph G, equivalent
+        to the number of vertices in G.
+        '''
+        self.graph = G
+        Function.__init__(
+                self, Order._operator_, G, styles=styles)
+
+    def string(self, **kwargs):
+        return '|' + self.operand.string() + '|'
+
+    def latex(self, **kwargs):
+        return r'\left|' + self.operand.latex() + r'\right|'
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = |G|, deduce and return the equality:
+        |G| = |Vertices(G)|.
+        '''
+        from . import graph_order_def
+        _G_sub = self.operand
+        return graph_order_def.instantiate({G:_G_sub}, auto_simplify=False)
+
+
+class Size(Function):
+    '''
+    Size(G), denoted ||G||, represents the size of graph G, meaning
+    the number of edges in graph G. This will be equivalent to
+    |Edges(G)|.
+    '''
+    # literal operator of the Size operation.
+    _operator_ = Literal(string_format='Size', theory=__file__)
+
+    def __init__(self, G, *, styles=None):
+        '''
+        Represent Size(G), the size of graph G, equivalent
+        to the number of edges in G.
+        '''
+        self.graph = G
+        Function.__init__(
+                self, Size._operator_, G, styles=styles)
+
+    def side_effects(self, judgment):
+        '''
+        Yield side-effects when representing 'Size(G)'.
+        '''
+        yield self.derive_size_in_natural
+
+    def string(self, **kwargs):
+        return '||' + self.operand.string() + '||'
+
+    def latex(self, **kwargs):
+        return r'\left\|' + self.operand.latex() + r'\right\|'
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = ||G||, deduce and return the equality:
+        ||G|| = |Edges(G)|.
+        '''
+        from . import graph_size_def
+        _G_sub = self.operand
+        return graph_size_def.instantiate({G:_G_sub}, auto_simplify=False)
+
+    @prover
+    def derive_size_in_natural(self, **defaults_config):
+        from . import graph_size_in_natural
+        _G      = self.operand
+        return (graph_size_in_natural.instantiate(
+            {G:_G}, auto_simplify=False))
+
+
+
+class Connected(Function):
+    '''
+    Connected(G) is a propositional function (or predicate)
+    representing the claim that graph G is connected (i.e., that
+    for every pair (u, v) of vertices in G, there exists a u-v path
+    in G).
+    '''
+
+    # the literal operator of the Connected operation
+    _operator_ = Literal(string_format='Connected',
+                         latex_format=r'\textrm{Connected}',
+                         theory=__file__)
+
+    def __init__(self, G, *, styles=None):
+        '''
+        Represent the propositional function Connected(G),
+        claiming graph G is connected.
+        '''
+        self.graph = G
+        Function.__init__(
+                self, Connected._operator_, G, styles=styles)
+    
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = Connected(G), deduce and return the equality:
+        Connected(G) = A, where A represents the statement that,
+        for all vertices u, v in G, there exists a u-v path in G.
+        '''
+        from . import connected_def
+        _G_sub = self.operand
+        return connected_def.instantiate({G:_G_sub}, auto_simplify=False)
+
+
+class HasEulerianTrail(Function):
+    '''
+    HasEulerTrail(G) is a propositional function (or predicate)
+    claiming that graph G has an Eulerian trail (i.e., G has a
+    walk that uses each and every edge of G exactly once).
+    '''
+
+    # the literal operator of the HasEulerTrail operation
+    _operator_ = Literal(string_format='HasEulerianTrail',
+                         latex_format=r'\textrm{HasEulerianTrail}',
+                         theory=__file__)
+
+    def __init__(self, G, *, styles=None):
+        '''
+        Represent the propositional function HasEulerianTrail(G),
+        claiming graph G has an Eulerian trail.
+        '''
+        self.graph = G
+        Function.__init__(
+                self, HasEulerianTrail._operator_, G, styles=styles)
+
+
+class HasEulerianCircuit(Function):
+    '''
+    HasEulerCircuit(G) is a propositional function (or predicate)
+    claiming that graph G has an Eulerian circuit (i.e., G has a
+    closed walk that uses each and every edge of G exactly once).
+    '''
+
+    # the literal operator of the HasEulerCircuit operation
+    _operator_ = Literal(string_format='HasEulerianCircuit',
+                         latex_format=r'\textrm{HasEulerianCircuit}',
+                         theory=__file__)
+
+    def __init__(self, G, *, styles=None):
+        '''
+        Represent the propositional function HasEulerianCircuit(G),
+        claiming graph G has an Eulerian circuit.
+        '''
+        self.graph = G
+        Function.__init__(
+                self, HasEulerianCircuit._operator_, G, styles=styles)
+
diff --git a/packages/proveit/graphs/inclusion/__init__.py b/packages/proveit/graphs/inclusion/__init__.py
new file mode 100644
index 000000000000..5842d690a754
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/__init__.py
@@ -0,0 +1,10 @@
+from .subgraph import Subgraph
+from .not_subgraph import NotSubgraph
+
+# KEEP THE FOLLOWING IN __init__.py FOR THEORY PACKAGES.
+#  Make additions above, or add to sys.modules[__name__].__dict__ below.
+# This allows us to import common expression, axioms, and theorems of
+# the theory package directly from the package.
+import sys
+from proveit._core_.theory import TheoryPackage
+sys.modules[__name__] = TheoryPackage(__name__, __file__, locals())
diff --git a/packages/proveit/graphs/inclusion/_brief_description_.txt b/packages/proveit/graphs/inclusion/_brief_description_.txt
new file mode 100644
index 000000000000..4ebf160e05e2
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/_brief_description_.txt
@@ -0,0 +1 @@
+basic concepts involving subgraphs and supergraphs
diff --git a/packages/proveit/graphs/inclusion/_sub_theories_.txt b/packages/proveit/graphs/inclusion/_sub_theories_.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/__init__.py b/packages/proveit/graphs/inclusion/_theory_nbs_/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/axioms.ipynb b/packages/proveit/graphs/inclusion/_theory_nbs_/axioms.ipynb
new file mode 100644
index 000000000000..eead97fb19fd
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/_theory_nbs_/axioms.ipynb
@@ -0,0 +1,92 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Axioms for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.inclusion</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the axioms of a theory:\n",
+    "%axioms_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "from proveit import E, V\n",
+    "from proveit.logic import And, Equals, Forall, Not\n",
+    "from proveit.logic.sets import KPowerSet, SubsetEq\n",
+    "from proveit.numbers import two\n",
+    "from proveit.graphs import Graph, E_prime, NotSubgraph, Subgraph, V_prime"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin axioms"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "subgraph_def = (\n",
+    "        Forall((V, V_prime),\n",
+    "                  Forall((E, E_prime),\n",
+    "                         Equals(\n",
+    "                         Subgraph(Graph(V_prime, E_prime), Graph(V, E)),\n",
+    "                         And(SubsetEq(V_prime, V), SubsetEq(E_prime, E))\n",
+    "                         ),\n",
+    "                        domains = [KPowerSet(V, two), KPowerSet(V_prime, two)])\n",
+    "        )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_subgraph_def = (\n",
+    "        Forall((V, V_prime),\n",
+    "                  Forall((E, E_prime),\n",
+    "                         Equals(\n",
+    "                         NotSubgraph(Graph(V_prime, E_prime), Graph(V, E)),\n",
+    "                         Not(Subgraph(Graph(V_prime, E_prime), Graph(V, E)))\n",
+    "                         ),\n",
+    "                        domains = [KPowerSet(V, two), KPowerSet(V_prime, two)])\n",
+    "        )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end axioms"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/common.ipynb b/packages/proveit/graphs/inclusion/_theory_nbs_/common.ipynb
new file mode 100644
index 000000000000..c043ae304de6
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/_theory_nbs_/common.ipynb
@@ -0,0 +1,59 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Common expressions for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.inclusion</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the common expressions of a theory:\n",
+    "%common_expressions_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "# from ... import ..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin common"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end common"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/demonstrations.ipynb b/packages/proveit/graphs/inclusion/_theory_nbs_/demonstrations.ipynb
new file mode 100644
index 000000000000..1b1fbe3837cc
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/_theory_nbs_/demonstrations.ipynb
@@ -0,0 +1,91 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Demonstrations for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.inclusion</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "%begin demonstrations\n",
+    "\n",
+    "from proveit import E, G, H, V, Variable\n",
+    "from proveit.graphs import E_prime, G_prime, Graph, NotSubgraph, Subgraph, V_prime"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Subgraphs & Supergraphs: $G' \\subseteq G$"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### MISC. TESTING"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Basic Constructions"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G, G_prime = (Graph(V, E), Graph(V_prime, E_prime))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Subgraph(G_prime, G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotSubgraph(G_prime, G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end demonstrations"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/fold_not_subgraph_def/allowed_presumptions.txt b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/fold_not_subgraph_def/allowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/fold_not_subgraph_def/disallowed_presumptions.txt b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/fold_not_subgraph_def/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/fold_not_subgraph_def/thm_proof.ipynb b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/fold_not_subgraph_def/thm_proof.ipynb
new file mode 100644
index 000000000000..795e3aac7b2a
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/fold_not_subgraph_def/thm_proof.ipynb
@@ -0,0 +1,86 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">inclusion</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#fold_not_subgraph_def\">fold_not_subgraph_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory\n",
+    "\n",
+    "from proveit import defaults\n",
+    "from proveit.graphs.inclusion import not_subgraph_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving fold_not_subgraph_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "defaults.assumptions = fold_not_subgraph_def.all_conditions()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_subgraph_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_subgraph_def_inst = not_subgraph_def.instantiate().instantiate()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%qed"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/fold_subgraph_def/allowed_presumptions.txt b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/fold_subgraph_def/allowed_presumptions.txt
new file mode 100644
index 000000000000..df2d242c76be
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/fold_subgraph_def/allowed_presumptions.txt
@@ -0,0 +1 @@
+proveit.logic.equality.lhs_via_equality
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/fold_subgraph_def/disallowed_presumptions.txt b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/fold_subgraph_def/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/fold_subgraph_def/thm_proof.ipynb b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/fold_subgraph_def/thm_proof.ipynb
new file mode 100644
index 000000000000..6cb9fc09ff6c
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/fold_subgraph_def/thm_proof.ipynb
@@ -0,0 +1,95 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">inclusion</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#fold_subgraph_def\">fold_subgraph_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory\n",
+    "\n",
+    "from proveit import defaults\n",
+    "from proveit.graphs.inclusion import subgraph_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving fold_subgraph_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "defaults.assumptions = fold_subgraph_def.all_conditions()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "subgraph_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "subgraph_def_inst = subgraph_def.instantiate().instantiate()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "subgraph_def_inst.derive_left_via_equality()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%qed"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_not_subgraph_def/allowed_presumptions.txt b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_not_subgraph_def/allowed_presumptions.txt
new file mode 100644
index 000000000000..cc2c81dedc51
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_not_subgraph_def/allowed_presumptions.txt
@@ -0,0 +1 @@
+proveit.logic.equality.rhs_via_equality
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_not_subgraph_def/disallowed_presumptions.txt b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_not_subgraph_def/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_not_subgraph_def/thm_proof.ipynb b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_not_subgraph_def/thm_proof.ipynb
new file mode 100644
index 000000000000..919cf209c715
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_not_subgraph_def/thm_proof.ipynb
@@ -0,0 +1,95 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">inclusion</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#unfold_not_subgraph_def\">unfold_not_subgraph_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory\n",
+    "\n",
+    "from proveit import defaults\n",
+    "from proveit.graphs.inclusion import not_subgraph_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving unfold_not_subgraph_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "defaults.assumptions = unfold_not_subgraph_def.all_conditions()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_subgraph_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_subgraph_def_inst = not_subgraph_def.instantiate().instantiate()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_subgraph_def_inst.derive_right_via_equality()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%qed"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_subgraph_def/allowed_presumptions.txt b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_subgraph_def/allowed_presumptions.txt
new file mode 100644
index 000000000000..cc2c81dedc51
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_subgraph_def/allowed_presumptions.txt
@@ -0,0 +1 @@
+proveit.logic.equality.rhs_via_equality
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_subgraph_def/disallowed_presumptions.txt b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_subgraph_def/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_subgraph_def/thm_proof.ipynb b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_subgraph_def/thm_proof.ipynb
new file mode 100644
index 000000000000..5bdc81bd98af
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/_theory_nbs_/proofs/unfold_subgraph_def/thm_proof.ipynb
@@ -0,0 +1,90 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">inclusion</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#unfold_subgraph_def\">unfold_subgraph_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory\n",
+    "\n",
+    "from proveit import E, V\n",
+    "from proveit.logic import InSet\n",
+    "from proveit.logic.sets import CartProd\n",
+    "from proveit.graphs import E_prime, Graph, V_prime\n",
+    "from proveit.graphs.inclusion import Subgraph, subgraph_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving unfold_subgraph_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "subgraph_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "subgraph_def_inst = subgraph_def.instantiate().instantiate(\n",
+    "    assumptions=[InSet(E, CartProd(V, V)), InSet(E_prime, CartProd(V_prime, V_prime))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "subgraph_def_inst.derive_right_via_equality(assumptions = [Subgraph(Graph(V_prime, E_prime), Graph(V, E))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%qed"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/theorems.ipynb b/packages/proveit/graphs/inclusion/_theory_nbs_/theorems.ipynb
new file mode 100644
index 000000000000..ceae4fbfb7b1
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/_theory_nbs_/theorems.ipynb
@@ -0,0 +1,120 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theorems (or conjectures) for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.inclusion</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the theorems of a theory:\n",
+    "%theorems_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "from proveit import E, V\n",
+    "from proveit.logic import And, Equals, Forall, Not\n",
+    "from proveit.logic.sets import KPowerSet, SubsetEq\n",
+    "from proveit.numbers import two\n",
+    "from proveit.graphs import Graph, E_prime, NotSubgraph, Subgraph, V_prime"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin theorems"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "unfold_subgraph_def = (\n",
+    "        Forall((V, V_prime),\n",
+    "                  Forall((E, E_prime),\n",
+    "                         And(SubsetEq(V_prime, V), SubsetEq(E_prime, E)),\n",
+    "                         conditions = [Subgraph(Graph(V_prime, E_prime), Graph(V, E))],\n",
+    "                         domains = [KPowerSet(V, two), KPowerSet(V_prime, two)])\n",
+    "        )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "fold_subgraph_def = (\n",
+    "        Forall((V, V_prime),\n",
+    "                  Forall((E, E_prime),\n",
+    "                         Subgraph(Graph(V_prime, E_prime), Graph(V, E)),\n",
+    "                         conditions = [And(SubsetEq(V_prime, V), SubsetEq(E_prime, E))],\n",
+    "                         domains = [KPowerSet(V, two), KPowerSet(V_prime, two)])\n",
+    "        )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "unfold_not_subgraph_def = (\n",
+    "        Forall((V, V_prime),\n",
+    "                  Forall((E, E_prime),\n",
+    "                         Not(Subgraph(Graph(V_prime, E_prime), Graph(V, E))),\n",
+    "                         conditions = [NotSubgraph(Graph(V_prime, E_prime), Graph(V, E))],\n",
+    "                         domains = [KPowerSet(V, two), KPowerSet(V_prime, two)])\n",
+    "        )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "fold_not_subgraph_def = (\n",
+    "        Forall((V, V_prime),\n",
+    "                  Forall((E, E_prime),\n",
+    "                         NotSubgraph(Graph(V_prime, E_prime), Graph(V, E)),\n",
+    "                         conditions = [Not(Subgraph(Graph(V_prime, E_prime), Graph(V, E)))],\n",
+    "                         domains = [KPowerSet(V, two), KPowerSet(V_prime, two)])\n",
+    "        )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end theorems"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/inclusion/_theory_nbs_/theory.ipynb b/packages/proveit/graphs/inclusion/_theory_nbs_/theory.ipynb
new file mode 100644
index 000000000000..14d7a773b953
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/_theory_nbs_/theory.ipynb
@@ -0,0 +1,33 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theory of <a class=\"ProveItLink\" href=\"../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../_theory_nbs_/theory.ipynb\">graphs</a>.inclusion\n",
+    "========\n",
+    "\n",
+    "Provide description here."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "%theory # toggles between interactive and static modes"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/inclusion/not_subgraph.py b/packages/proveit/graphs/inclusion/not_subgraph.py
new file mode 100644
index 000000000000..d6bbdd1191c5
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/not_subgraph.py
@@ -0,0 +1,26 @@
+from proveit import Literal
+from proveit.relation import Relation
+
+class NotSubgraph(Relation):
+    '''
+    Given graph G'(V',E') with vertex set V' and edge set E',
+    and given graph G(V,E) with vertex set V and edge set E,
+    NotSubgraph(G', G) represents the claim that graph G' is
+    NOT a subgraph of graph G. That would mean that V' is NOT
+    a subset of V and/or E' is NOT a subset of E.
+    '''
+
+    # The operator of the NotSubgraph operation.
+    # Following tradition in graph theory, we use the same operator
+    # for not subgraph as we use for not subset, relying on the context
+    # to distinguish the cases. We modify the string version for clarity.
+    _operator_ = Literal(string_format='not_subgraph', 
+                         latex_format=r'\nsubseteq',
+                         theory=__file__)
+
+    def __init__(self, A, B, *, styles=None):
+        '''
+        Create the expression for (A not_subgraph B)
+        '''
+        Relation.__init__(self, NotSubgraph._operator_, A, B,
+                           styles=styles)
\ No newline at end of file
diff --git a/packages/proveit/graphs/inclusion/subgraph.py b/packages/proveit/graphs/inclusion/subgraph.py
new file mode 100644
index 000000000000..cf88db4dfb36
--- /dev/null
+++ b/packages/proveit/graphs/inclusion/subgraph.py
@@ -0,0 +1,33 @@
+from proveit import Literal
+from proveit.logic.sets.inclusion.inclusion_relation import InclusionRelation
+
+class Subgraph(InclusionRelation):
+    '''
+    Given graph G'(V',E') with vertex set V' and edge set E',
+    and given graph G(V,E) with vertex set V and edge set E,
+    Subgraph(G', G) represents the claim that graph G' is a
+    subgraph of graph G. That would mean that V' is a subset of V,
+    and E' is a subset of E.
+    '''
+
+    # The operator of the Subgraph operation.
+    # Following tradition in graph theory, we use the same operator
+    # for subgraph as we use for subset, relying on the context to
+    # distinguish the cases. We modify the string version for clarity.
+    _operator_ = Literal(string_format='subgraph',
+                         latex_format=r'\subseteq',
+                         theory=__file__)
+
+    # map left-hand-sides to Subgraph Judgments
+    #   (populated in TransitivityRelation.derive_side_effects)
+    known_left_sides = dict()
+    # map right-hand-sides to Subgraph Judgments
+    #   (populated in TransitivityRelation.derive_side_effects)
+    known_right_sides = dict()
+
+    def __init__(self, A, B, *, styles=None):
+        '''
+        Create the expression for (G' subgraph G)
+        '''
+        InclusionRelation.__init__(self, Subgraph._operator_, A, B,
+                                   styles=styles)
\ No newline at end of file
diff --git a/packages/proveit/graphs/membership/__init__.py b/packages/proveit/graphs/membership/__init__.py
new file mode 100644
index 000000000000..2beead7e73da
--- /dev/null
+++ b/packages/proveit/graphs/membership/__init__.py
@@ -0,0 +1,10 @@
+from .in_graph import InGraph, GraphMembership
+from .not_in_graph import NotInGraph, GraphNonmembership
+
+# KEEP THE FOLLOWING IN __init__.py FOR THEORY PACKAGES.
+#  Make additions above, or add to sys.modules[__name__].__dict__ below.
+# This allows us to import common expression, axioms, and theorems of
+# the theory package directly from the package.
+import sys
+from proveit._core_.theory import TheoryPackage
+sys.modules[__name__] = TheoryPackage(__name__, __file__, locals())
diff --git a/packages/proveit/graphs/membership/_brief_description_.txt b/packages/proveit/graphs/membership/_brief_description_.txt
new file mode 100644
index 000000000000..23d32e8e7bb9
--- /dev/null
+++ b/packages/proveit/graphs/membership/_brief_description_.txt
@@ -0,0 +1 @@
+basic concepts involving membership in a graph
diff --git a/packages/proveit/graphs/membership/_sub_theories_.txt b/packages/proveit/graphs/membership/_sub_theories_.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/membership/_theory_nbs_/__init__.py b/packages/proveit/graphs/membership/_theory_nbs_/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/membership/_theory_nbs_/axioms.ipynb b/packages/proveit/graphs/membership/_theory_nbs_/axioms.ipynb
new file mode 100644
index 000000000000..54a89dddf1df
--- /dev/null
+++ b/packages/proveit/graphs/membership/_theory_nbs_/axioms.ipynb
@@ -0,0 +1,122 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Axioms for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.membership</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the axioms of a theory:\n",
+    "%axioms_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "from proveit import x, E, G, V\n",
+    "from proveit.logic import Equals, Forall, InSet, Not, Or\n",
+    "from proveit.graphs import InGraph, NotInGraph"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin axioms"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "An element of a graph is either a point or an edge (but not both)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# this should eventually use an exclusive or (XOR) instead of simple disjunction (OR)\n",
+    "# in_graph_def = (\n",
+    "#     Forall((E, V),\n",
+    "#     Forall((x, G,\n",
+    "#            Equals(InGraph(x, G), Or(InSet(x, V), InSet(x, E))))\n",
+    "#     ))\n",
+    "# )"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# this should eventually use an exclusive or (XOR) instead of simple disjunction (OR)\n",
+    "# (\n",
+    "#     Forall((E, V),\n",
+    "#     Forall(x,\n",
+    "#            Equals(InGraph(x, Graph(V,E)), Or(InSet(x, V), InSet(x, E))))\n",
+    "#     ))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "axiomatic definitions of Vertices() and Edges() functions\n",
+    "(and implicitly of Graph(V, E))\n",
+    "\n",
+    "Forall(V,\n",
+    "Forall(E, Vertices(Graph(V,E)) = V,\n",
+    "       conditions = [E subset of V X V]\n",
+    "))\n",
+    "\n",
+    "Forall(V,\n",
+    "Forall(E, Edges(Graph(V,E)) = E,\n",
+    "       conditions = [E subset of V X V]\n",
+    "))\n",
+    "\n",
+    "Forall(V,\n",
+    "Forall(E, Graph(V,E) is elem of class of Graphs,\n",
+    "       conditions = [E subset of V X V]\n",
+    "))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# not_in_graph_def = Forall((x, G), Equals(NotInGraph(x, G), Not(InGraph(x, G))))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end axioms"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/membership/_theory_nbs_/common.ipynb b/packages/proveit/graphs/membership/_theory_nbs_/common.ipynb
new file mode 100644
index 000000000000..95a47ce628bc
--- /dev/null
+++ b/packages/proveit/graphs/membership/_theory_nbs_/common.ipynb
@@ -0,0 +1,59 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Common expressions for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.membership</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the common expressions of a theory:\n",
+    "%common_expressions_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "# from ... import ..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin common"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end common"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/membership/_theory_nbs_/demonstrations.ipynb b/packages/proveit/graphs/membership/_theory_nbs_/demonstrations.ipynb
new file mode 100644
index 000000000000..9e57290e559b
--- /dev/null
+++ b/packages/proveit/graphs/membership/_theory_nbs_/demonstrations.ipynb
@@ -0,0 +1,143 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Demonstrations for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.membership</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "from proveit import x, G\n",
+    "from proveit.logic import in_bool, Not\n",
+    "from proveit.graphs import InGraph, NotInGraph\n",
+    "\n",
+    "%begin demonstrations"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Graph Membership & Non-Membership: $x \\in G$, $x \\notin G$"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Miscellaneous Testing\n",
+    "Testing various classes and methods related to graph membership. Some of this material could eventually be integrated into the `_demonstrations_` page and/or deleted as development continues."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Basic Constructions"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InGraph(x, G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInGraph(x, G)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Basic Testing of `InGraph()` methods"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InGraph(x, G).negated()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Basic Testing of `NotInGraph()` methods"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInGraph(x, G).deduce_in_bool(assumptions=[in_bool(InGraph(x, G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInGraph(x, G).unfold(assumptions=[NotInGraph(x, G)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInGraph(x, G).conclude_as_folded(assumptions=[Not(InGraph(x, G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInGraph(x, G).conclude_negation(assumptions=[InGraph(x, G)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end demonstrations"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/membership/_theory_nbs_/proofs/double_negated_membership/thm_proof.ipynb b/packages/proveit/graphs/membership/_theory_nbs_/proofs/double_negated_membership/thm_proof.ipynb
new file mode 100644
index 000000000000..e280b62c0b14
--- /dev/null
+++ b/packages/proveit/graphs/membership/_theory_nbs_/proofs/double_negated_membership/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">membership</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#double_negated_membership\">double_negated_membership</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving double_negated_membership"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/membership/_theory_nbs_/proofs/fold_not_in_graph/thm_proof.ipynb b/packages/proveit/graphs/membership/_theory_nbs_/proofs/fold_not_in_graph/thm_proof.ipynb
new file mode 100644
index 000000000000..c0e9ea2b1dc5
--- /dev/null
+++ b/packages/proveit/graphs/membership/_theory_nbs_/proofs/fold_not_in_graph/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">membership</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#fold_not_in_graph\">fold_not_in_graph</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving fold_not_in_graph"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/membership/_theory_nbs_/proofs/membership_denied_via_contradiction/thm_proof.ipynb b/packages/proveit/graphs/membership/_theory_nbs_/proofs/membership_denied_via_contradiction/thm_proof.ipynb
new file mode 100644
index 000000000000..d1b31da1735f
--- /dev/null
+++ b/packages/proveit/graphs/membership/_theory_nbs_/proofs/membership_denied_via_contradiction/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">membership</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#membership_denied_via_contradiction\">membership_denied_via_contradiction</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving membership_denied_via_contradiction"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/membership/_theory_nbs_/proofs/not_in_graph_is_bool/thm_proof.ipynb b/packages/proveit/graphs/membership/_theory_nbs_/proofs/not_in_graph_is_bool/thm_proof.ipynb
new file mode 100644
index 000000000000..203fa7780075
--- /dev/null
+++ b/packages/proveit/graphs/membership/_theory_nbs_/proofs/not_in_graph_is_bool/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">membership</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#not_in_graph_is_bool\">not_in_graph_is_bool</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving not_in_graph_is_bool"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/membership/_theory_nbs_/proofs/unfold_not_in_graph/thm_proof.ipynb b/packages/proveit/graphs/membership/_theory_nbs_/proofs/unfold_not_in_graph/thm_proof.ipynb
new file mode 100644
index 000000000000..db63b80c9b2e
--- /dev/null
+++ b/packages/proveit/graphs/membership/_theory_nbs_/proofs/unfold_not_in_graph/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">membership</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#unfold_not_in_graph\">unfold_not_in_graph</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving unfold_not_in_graph"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/membership/_theory_nbs_/theorems.ipynb b/packages/proveit/graphs/membership/_theory_nbs_/theorems.ipynb
new file mode 100644
index 000000000000..d6ab32b30716
--- /dev/null
+++ b/packages/proveit/graphs/membership/_theory_nbs_/theorems.ipynb
@@ -0,0 +1,99 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theorems (or conjectures) for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.membership</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the theorems of a theory:\n",
+    "%theorems_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "from proveit import x, G\n",
+    "from proveit.logic import FALSE, Forall, Implies, in_bool, Not\n",
+    "from proveit.graphs import InGraph, NotInGraph"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin theorems"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_graph_is_bool = Forall((x, G), in_bool(NotInGraph(x, G)), conditions=[in_bool(InGraph(x, G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "unfold_not_in_graph = Forall((x, G), Not(InGraph(x, G)), conditions=[NotInGraph(x, G)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "fold_not_in_graph = Forall((x, G), NotInGraph(x, G), conditions=[Not(InGraph(x, G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "double_negated_membership = Forall((x, G), Not(NotInGraph(x, G)), conditions=[InGraph(x, G)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "membership_denied_via_contradiction = Forall((x, G), Implies(Implies(InGraph(x, G), FALSE), NotInGraph(x, G)))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end theorems"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/membership/_theory_nbs_/theory.ipynb b/packages/proveit/graphs/membership/_theory_nbs_/theory.ipynb
new file mode 100644
index 000000000000..0b900892b1db
--- /dev/null
+++ b/packages/proveit/graphs/membership/_theory_nbs_/theory.ipynb
@@ -0,0 +1,33 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theory of <a class=\"ProveItLink\" href=\"../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../_theory_nbs_/theory.ipynb\">graphs</a>.membership\n",
+    "========\n",
+    "\n",
+    "Provide description here."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "%theory # toggles between interactive and static modes"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/membership/in_graph.py b/packages/proveit/graphs/membership/in_graph.py
new file mode 100644
index 000000000000..92cf8ccfd125
--- /dev/null
+++ b/packages/proveit/graphs/membership/in_graph.py
@@ -0,0 +1,180 @@
+from proveit import (Literal, defaults, USE_DEFAULTS, ProofFailure,
+                     UnusableProof, single_or_composite_expression,
+                     prover, equality_prover, relation_prover)
+from proveit.relation import Relation
+from proveit.logic.classes import InClass, ClassMembership
+
+# UNDER CONSTRUCTION, ADAPTING FROM InSet beginning 4/16/2025
+
+class InGraph(InClass):
+    '''
+    Graph membership (applicable to vertices and edges) is a
+    special case of class membership, so we'll derive from InClass
+    for possible code re-use. The operators are distinct (though
+    the formatting is the same).
+    '''
+
+    # operator of the InGraph operation
+    _operator_ = Literal(string_format='in',
+                         latex_format=r'\in',
+                         theory=__file__)
+
+    # map (element, domain) pairs to corresponding InGraph expressions
+    ingraph_expressions = dict()
+
+    def __init__(self, element, domain, *, styles=None):
+        element = single_or_composite_expression(element)
+        domain = single_or_composite_expression(domain)
+        InGraph.ingraph_expressions[(element, domain)] = self
+        InClass.__init__(self, element, domain, operator=InGraph._operator_,
+                         styles=styles)
+
+    def negated(self):
+        '''
+        Return the negated membership expression (not a judgment):
+        element not in (graph) domain.
+        '''
+        from .not_in_graph import NotInGraph
+        return NotInGraph(self.element, self.domain)
+
+    # A placeholder from the InSet class; delaying for now
+    # come back to this after subgraphs are defined
+    # def _readily_provable(self, check_directly_known_elem_equality=True):
+    #     '''
+    #     This membership is readily provable if the membership
+    #     object indicates that it is readily provable or there is a 
+    #     known as-strong membership (with known equal elements and the
+    #     domain a subset of the desired domain).
+    #     '''
+    #     if InClass._readily_provable(
+    #             self, check_directly_known_elem_equality=(
+    #                     check_directly_known_elem_equality)):
+    #         return True
+    #     if self.as_strong_known_membership() is not None:
+    #         return True
+    #     return False
+
+    # A placeholder from the InSet class; delaying for now
+    # @prover
+    # def conclude(self, **defaults_config):
+    #     '''
+    #     Attempt to conclude that the element is in the domain.  First
+    #     see if there is an equivalent known membership to use
+    #     (same domain).  If not, see if there is a membership object
+    #     that is readily provable and conclude via that object if so.
+    #     Then check for a membership that is at least as strong with
+    #     a possibly different domain to use.  Finally, defer to
+    #     InClass.conclude which defers to InRelation.conclude and
+    #     attempts simplifications.
+    #     '''
+    #     # See if the element, or something known to be equal to
+    #     # the element, is known to be a member of the domain or a subset
+    #     # of the domain.
+    #     as_strong_membership = self.as_strong_known_membership(
+    #             include_canonical_forms=False)
+    #     if as_strong_membership is not None:
+    #         if as_strong_membership.domain == self.domain:
+    #             try:
+    #                 # Use an equivalent known membership.
+    #                 return self.conclude_from_as_strong_membership(
+    #                         as_strong_membership)
+    #             except UnusableProof:
+    #                 pass
+        
+    #     if hasattr(self, 'membership_object') and (
+    #             self.membership_object._readily_provable()):
+    #         # Don't bother with a fancy, indirect approach if
+    #         # we can readily conclude membership via the membership
+    #         # object.
+    #         return self.membership_object.conclude()
+
+    #     as_strong_membership = self.as_strong_known_membership(
+    #             include_canonical_forms=True)
+    #     if as_strong_membership is not None:
+    #         # Use a known membership that is at least as strong.
+    #         return self.conclude_from_as_strong_membership(
+    #                 as_strong_membership)
+
+    #     return InClass.conclude(self)
+    
+    # A placeholder from the InSet class; delaying for now
+    # come back to this after subgraphs are defined
+    # @prover
+    # def conclude_from_as_strong_membership(self, as_strong_membership,
+    #                                        **defaults_config):
+    #     '''
+    #     Conclude from a membership with an equal element and a domain
+    #     that is a subset of the desired domain.
+    #     '''
+    #     from proveit.logic import Equals, SubsetEq
+    #     elem_sub = as_strong_membership.element
+    #     if as_strong_membership.domain == self.domain:
+    #         elem_sub_in_domain = as_strong_membership
+    #     else:
+    #         eq_rel = Equals(as_strong_membership.domain, self.domain)
+    #         if eq_rel.readily_provable():
+    #             # domains are equal -- just substitute the domain.
+    #             elem_sub_in_domain = eq_rel.sub_right_side_into(
+    #                     as_strong_membership.inner_expr().domain)
+    #         else:
+    #             # S is a superset of R, so now we can prove x in S.
+    #             sub_rel = SubsetEq(as_strong_membership.domain, self.domain)
+    #             try:
+    #                 sub_rel.prove()
+    #             except ProofFailure:
+    #                 # May have been blocked to avoid infinite
+    #                 # recursion.
+    #                 return InClass.conclude(self)
+    #             elem_sub_in_domain = sub_rel.derive_superset_membership(
+    #                     elem_sub)
+    #     if elem_sub == self.element:
+    #         return elem_sub_in_domain # done
+    #     # Just need to sub in the element for _elem_sub.
+    #     return elem_sub_in_domain.inner_expr().element.substitute(
+    #             self.element)        
+
+    # A placeholder from the InSet class; delaying for now
+    # come back to this after subgraphs are defined
+    # def as_strong_known_membership(self, include_canonical_forms=True):
+    #     '''
+    #     If there is a known membership that is as strong as this one,
+    #     where the element is known to be equal this one's element
+    #     and the domain is a subset of this one's domain, return this
+    #     as-strong known membership.  Otherwise, return None.
+    #     '''
+    #     from proveit.logic import Equals, SubsetEq
+    #     known_memberships = list(
+    #             InClass.yield_known_memberships(
+    #                     self.element, 
+    #                     include_canonical_forms=include_canonical_forms))
+    #     # First see of there is a known membership with the same domain.
+    #     for known_membership in known_memberships:
+    #         if known_membership.domain == self.domain:
+    #             # this is the best to use; we are done
+    #             return known_membership
+    #     # Next see of there is a known membership with a domain
+    #     # readily provable to be equal to this domain.
+    #     for known_membership in known_memberships:
+    #         eq_rel = Equals(known_membership.domain, self.domain)
+    #         if eq_rel.readily_provable():
+    #             return known_membership
+    #     # Finaly see of there is a known membership with a domain
+    #     # readily provable to be a subset of to this domain.
+    #     for known_membership in known_memberships:
+    #         sub_rel = SubsetEq(known_membership.domain, self.domain)
+    #         if sub_rel.readily_provable():
+    #             return known_membership
+    #     return None # No match found.
+
+class GraphMembership(ClassMembership):
+    def __init__(self, element, domain):
+        '''
+        Base class for any 'membership object' returned by a domain's
+        'membership_object' method.
+        '''
+        # The expression represented by this Membership.
+        if (element, domain) in InGraph.ingraph_expressions:
+            expr = InGraph.ingraph_expressions[(element, domain)]
+        else:
+            expr = InGraph(element, domain)
+        ClassMembership.__init__(self, element, domain, expr=expr)
diff --git a/packages/proveit/graphs/membership/not_in_graph.py b/packages/proveit/graphs/membership/not_in_graph.py
new file mode 100644
index 000000000000..b3e72419150e
--- /dev/null
+++ b/packages/proveit/graphs/membership/not_in_graph.py
@@ -0,0 +1,200 @@
+from proveit import (Literal, defaults, USE_DEFAULTS, equality_prover, 
+                     ProofFailure, prover, relation_prover)
+from proveit import x, G
+from proveit.relation import Relation
+from proveit.logic.classes import NotInClass, ClassNonmembership
+
+# UNDER CONSTRUCTION, ADAPTING FROM InSet beginning 4/16/2025
+
+
+class NotInGraph(NotInClass):
+    '''
+    Graph non-membership is a special case of class nonmembership,
+    so we derive from NotInClass for code re-use.  The operators are
+    distinct (though the formatting is the same).
+    '''
+    # operator of the NotInSet operation
+    _operator_ = Literal(string_format='not-in', latex_format=r'\notin',
+                         theory=__file__)
+
+    # map (element, domain) pairs to corresponding NotInGraph exprs
+    notingraph_expressions = dict()
+
+    def __init__(self, element, domain, *, styles=None):
+        NotInGraph.notingraph_expressions[(element, domain)] = self
+        NotInClass.__init__(
+                self, element, domain, operator=NotInGraph._operator_,
+                styles=styles)
+
+    @relation_prover
+    def deduce_in_bool(self, **defaults_config):
+        '''
+        Deduce and return that this 'not in' statement is in the set
+        of BOOLEANS. For example, NotInGraph(x, G(V,E)).deduce_in_bool()
+        returns:
+                   |- NotInGraph(x, G(V,E)) in Bool.
+
+        '''
+        from . import not_in_graph_is_bool
+        return not_in_graph_is_bool.instantiate(
+                {x: self.element, G: self.domain})
+
+    @prover
+    def unfold(self, **defaults_config):
+        r'''
+        From NotInGraph(x, G), derive and return Not(InGraph(x, G)).
+        For example,
+
+            NotInGraph(a, G(V, E)).unfold_not_in(
+                assumptions=[NotInGraph(a, G(V, E))])
+        returns
+
+            NotInGraph(a, G(V, E)) |- Not (a in G(V, E)).
+
+        We include the auto_simplify=False to keep the membership
+        result inside the Not() from being reduced to False.
+        '''
+        from . import unfold_not_in_graph
+        return unfold_not_in_graph.instantiate(
+            {x: self.element, G: self.domain}, auto_simplify=False)
+
+    # A placeholder from the NotInSet class; delaying for now
+    # come back to this after subgraphs are defined
+    # def _readily_provable(self):
+    #     '''
+    #     This membership is readily provable if the membership
+    #     object indicates that it is readily provable or there is a 
+    #     known as-strong membership (with known equal elements and the
+    #     domain a subset of the desired domain).
+    #     '''
+    #     if NotInClass._readily_provable(self):
+    #         return True
+    #     if self.as_strong_known_nonmembership() is not None:
+    #         return True
+    #     return False
+
+    # A placeholder from the NotInSet class; delaying for now
+    # @prover
+    # def conclude(self, **defaults_config):
+    #     '''
+    #     Attempt to conclude that the element is not in the domain. 
+    #     First see if the corresponding membership has been disproven. 
+    #     Then see if there is a as-strong known nonmembership to use.  
+    #     If not, use the NotInClass conclude strategies (which uses the
+    #     Relation conclude strategies that simplify both sides and then 
+    #     uses the domain-specific conclude method of the membership
+    #     object as a last resort).
+    #     '''
+    #     # Has the membership been disproven?
+    #     if self.negated().disproven(): # don't use readily_disprovable
+    #         return self.conclude_as_folded()
+        
+    #     # See if the element, or something known to be equal to
+    #     # the element, is known to be a nonmember of the domain or a 
+    #     # superset of the domain.
+    #     as_strong_nonmembership = self.as_strong_known_nonmembership()
+    #     if as_strong_nonmembership is not None:
+    #         return self.conclude_from_as_strong_nonmembership(
+    #                 as_strong_nonmembership)
+    #     return NotInClass.conclude(self)
+
+    @prover
+    def conclude_as_folded(self, **defaults_config):
+        '''
+        Attempt to conclude x not in G via Not(x in G).
+        '''
+        from . import fold_not_in_graph
+        return fold_not_in_graph.instantiate(
+            {x: self.element, G: self.domain})
+
+    @prover
+    def conclude_negation(self, **defaults_config):
+        '''
+        Attempt to conclude the negation of nonmembership via
+        proving membership.
+        '''
+        from . import double_negated_membership
+        return double_negated_membership.instantiate(
+                {x:self.element, G:self.domain})
+
+    # A placeholder from the NotInSet class; delaying for now
+    # come back to this after subgraphs are defined
+    # @prover
+    # def conclude_from_as_strong_nonmembership(
+    #         self, as_strong_nonmembership, **defaults_config):
+    #     '''
+    #     Conclude from a nonmembership with an equal element and a 
+    #     domain that is a superset of the desired domain.
+    #     '''
+    #     from proveit.logic import Equals, SubsetEq
+    #     elem_sub = as_strong_nonmembership.element
+    #     if as_strong_nonmembership.domain == self.domain:
+    #         elem_sub_notin_domain = as_strong_nonmembership
+    #     else:
+    #         eq_rel = Equals(as_strong_nonmembership.domain, self.domain)
+    #         if eq_rel.readily_provable():
+    #             # domains are equal -- just substitute the domain.
+    #             elem_sub_notin_domain = eq_rel.sub_right_side_into(
+    #                     as_strong_nonmembership.inner_expr().domain)
+    #         else:
+    #             # S is a subset of R, so now we can prove 
+    #             # x not in S.
+    #             sub_rel = SubsetEq(self.domain, as_strong_nonmembership.domain)
+    #             try:
+    #                 sub_rel.prove()
+    #             except ProofFailure:
+    #                 # May have been blocked to avoid infinite
+    #                 # recursion.
+    #                 return NotInClass.conclude(self)
+    #             elem_sub_notin_domain = sub_rel.derive_subset_nonmembership(
+    #                     elem_sub)
+    #     if elem_sub == self.element:
+    #         return elem_sub_notin_domain # done
+    #     # Just need to sub in the element for _elem_sub.
+    #     Equals(elem_sub, self.element).conclude_via_transitivity()
+    #     return elem_sub_notin_domain.inner_expr().element.substitute(
+    #             self.element)
+
+    # A placeholder from the NotInSet class; delaying for now
+    # come back to this after subgraphs are defined
+    # def as_strong_known_nonmembership(self):
+    #     '''
+    #     If there is a known nonmembership that is as strong as this 
+    #     one, where the element is equal to this one's element and the 
+    #     domain is a subset of this one's domain, return this
+    #     as-strong known membership.  Otherwise, return None.
+    #     '''
+    #     from proveit.logic import Equals, SubsetEq
+    #     known_nonmemberships = list(
+    #             NotInClass.yield_known_nonmemberships(self.element))
+    #     # First see of there is a known nonmembership with the same domain.
+    #     for known_nonmembership in known_nonmemberships:
+    #         if known_nonmembership.domain == self.domain:
+    #             # this is the best to use; we are done
+    #             return known_nonmembership
+    #     # Next see of there is a known nonmembership with a domain
+    #     # readily provable to be equal to this domain.
+    #     for known_nonmembership in known_nonmemberships:
+    #         eq_rel = Equals(known_nonmembership.domain, self.domain)
+    #         if eq_rel.readily_provable():
+    #             return known_nonmembership
+    #     # Finaly see of there is a known nonmembership with a domain
+    #     # readily provable to be a superset of to this domain.
+    #     for known_nonmembership in known_nonmemberships:
+    #         sub_rel = SubsetEq(self.domain, known_nonmembership.domain)
+    #         if sub_rel.readily_provable():
+    #             return known_nonmembership
+    #     return None # No match found.
+
+class GraphNonmembership(ClassNonmembership):
+    def __init__(self, element, domain):
+        '''
+        Base class for any 'non-membership object' returned by a
+        domain's 'non-membership_object' method.
+        '''
+        # The expression represented by this Non-Membership.
+        if (element, domain) in NotInGraph.notingraph_expressions:
+            expr = NotInGraph.notingraph_expressions[(element, domain)]
+        else:
+            expr = NotInGraph(element, domain)
+        ClassNonmembership.__init__(self, element, domain, expr=expr)
diff --git a/packages/proveit/graphs/paths/__init__.py b/packages/proveit/graphs/paths/__init__.py
new file mode 100644
index 000000000000..355de8e870a3
--- /dev/null
+++ b/packages/proveit/graphs/paths/__init__.py
@@ -0,0 +1,9 @@
+from .paths import Path, Paths
+
+# KEEP THE FOLLOWING IN __init__.py FOR THEORY PACKAGES.
+#  Make additions above, or add to sys.modules[__name__].__dict__ below.
+# This allows us to import common expression, axioms, and theorems of
+# the theory package directly from the package.
+import sys
+from proveit._core_.theory import TheoryPackage
+sys.modules[__name__] = TheoryPackage(__name__, __file__, locals())
diff --git a/packages/proveit/graphs/paths/_brief_description_.txt b/packages/proveit/graphs/paths/_brief_description_.txt
new file mode 100644
index 000000000000..dae23eda4815
--- /dev/null
+++ b/packages/proveit/graphs/paths/_brief_description_.txt
@@ -0,0 +1 @@
+basic concepts involving paths in a graph and paths as graphs
diff --git a/packages/proveit/graphs/paths/_sub_theories_.txt b/packages/proveit/graphs/paths/_sub_theories_.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/paths/_theory_nbs_/__init__.py b/packages/proveit/graphs/paths/_theory_nbs_/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/paths/_theory_nbs_/axioms.ipynb b/packages/proveit/graphs/paths/_theory_nbs_/axioms.ipynb
new file mode 100644
index 000000000000..3f684b183dc5
--- /dev/null
+++ b/packages/proveit/graphs/paths/_theory_nbs_/axioms.ipynb
@@ -0,0 +1,59 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Axioms for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.paths</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the axioms of a theory:\n",
+    "%axioms_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "# from ... import ..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin axioms"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end axioms"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/paths/_theory_nbs_/common.ipynb b/packages/proveit/graphs/paths/_theory_nbs_/common.ipynb
new file mode 100644
index 000000000000..d51d4e3dc63d
--- /dev/null
+++ b/packages/proveit/graphs/paths/_theory_nbs_/common.ipynb
@@ -0,0 +1,59 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Common expressions for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.paths</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the common expressions of a theory:\n",
+    "%common_expressions_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "# from ... import ..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin common"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end common"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/paths/_theory_nbs_/demonstrations.ipynb b/packages/proveit/graphs/paths/_theory_nbs_/demonstrations.ipynb
new file mode 100644
index 000000000000..9a3eaf7c9fca
--- /dev/null
+++ b/packages/proveit/graphs/paths/_theory_nbs_/demonstrations.ipynb
@@ -0,0 +1,130 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Demonstrations for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.paths</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "%begin demonstrations\n",
+    "\n",
+    "from proveit import E, G, P, V\n",
+    "from proveit.logic import InClass, NotInClass\n",
+    "from proveit.logic.sets import NotInSet, InSet\n",
+    "from proveit.graphs import Path, Paths"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### The Class of Paths, and Elements of That Class: $\\mathrm{Paths}$, $\\mathrm{Path}(V, E)$"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### MISC. TESTING"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Basic Constructions"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "paths_class = Paths()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# does this not automatically convert to InClass notation?\n",
+    "InSet(P, paths_class)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(P, paths_class)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InClass(P, paths_class)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInClass(P, paths_class)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "example_path = Path(V, E)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(example_path, Paths())"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end demonstrations"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/paths/_theory_nbs_/theorems.ipynb b/packages/proveit/graphs/paths/_theory_nbs_/theorems.ipynb
new file mode 100644
index 000000000000..9f33fde2ae7e
--- /dev/null
+++ b/packages/proveit/graphs/paths/_theory_nbs_/theorems.ipynb
@@ -0,0 +1,59 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theorems (or conjectures) for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.paths</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the theorems of a theory:\n",
+    "%theorems_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "# from ... import ..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin theorems"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end theorems"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/paths/_theory_nbs_/theory.ipynb b/packages/proveit/graphs/paths/_theory_nbs_/theory.ipynb
new file mode 100644
index 000000000000..186e36894cb5
--- /dev/null
+++ b/packages/proveit/graphs/paths/_theory_nbs_/theory.ipynb
@@ -0,0 +1,33 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theory of <a class=\"ProveItLink\" href=\"../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../_theory_nbs_/theory.ipynb\">graphs</a>.paths\n",
+    "========\n",
+    "\n",
+    "Provide description here."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "%theory # toggles between interactive and static modes"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/paths/paths.py b/packages/proveit/graphs/paths/paths.py
new file mode 100644
index 000000000000..a7237f751abc
--- /dev/null
+++ b/packages/proveit/graphs/paths/paths.py
@@ -0,0 +1,76 @@
+from proveit import Function, Literal
+from proveit.logic import ClassMembership
+from proveit.graphs import Graph
+
+class Paths(Literal):
+    '''
+    Paths() represents the set of all graphs that are (undirected)
+    paths. A undirected path is a non-empty graph P = (V,E)
+    with non-empty vertex set V and (possibly empty) edge set E such
+    that:
+            V = {x0, x1, ..., xk},
+            E = {{x0,x1}, {x1,x2}, ..., {x_{k-1},x_{k}},
+
+    where the x_{i} are all distinct. The vertices x0 and xk are said
+    to be linked by the path P and are called its endvertices, or
+    endpoints, or simply its ends. The vertices x1, ..., x_{k-1} are
+    the inner vertices of P. The number of edges in the path P,
+    denoted ||P||, is its length (in this example, ||P|| = k).
+    '''
+
+    # the literal string for representing the class of Paths
+    def __init__(self, *, styles=None):
+        Literal.__init__(self, string_format='Paths', 
+                         latex_format=r'\textrm{Paths}',
+                         styles=styles)
+
+    @property
+    def is_proper_class(self):
+        '''
+        Paths consitute a proper class (i.e. instead of a set).
+        This indicates that InClass() should be used instead of
+        InSet() when this is a domain.
+        '''
+        return True
+
+    def membership_object(self, element):
+        from .paths_membership import PathsMembership
+        return PathsMembership(element, self)
+
+    def nonmembership_object(self, element):
+        from .paths_membership import PathsNonmembership
+        return PathsNonmembership(element, self)
+
+
+class Path(Graph):
+    '''
+    Path(V,E) represents the special type of graph called a path,
+    with vertex set V and edge set E. A path is a non-empty graph
+    P = (V,E) with vertex set V and (possibly empty) edge set E such
+    that:
+
+        V = {x0, x1, ..., xk}, E = {x0x1, x1x2, ..., x_{k-1}x_{k}},
+
+    where the x_{i} are all distinct. The vertices x0 and xk are said
+    to be linked by the path P and are called its endvertices, or
+    endpoints, or simply its ends. The vertices x1, ..., x_{k-1} are
+    the inner vertices of P. The number of edges in the path P,
+    denoted ||P||, is its length (in this example, ||P|| = k).
+    '''
+
+    # the literal operator of the Path operator
+    _operator_ = Literal(string_format='Path',
+                         latex_format=r'\text{Path}',
+                         theory=__file__)
+
+    def __init__(self, V, E, *, styles=None):
+        '''
+        Create or represent a path, Path(V,E), with vertex set V
+        and edge set E. If explicit sets of vertices and edges are
+        provided, they are NOT currently verified to represent a
+        valid path.
+        '''
+        self.vertex_set = V
+        self.edge_set   = E
+        Function.__init__(self, Path._operator_, (V, E), styles=styles)
+
diff --git a/packages/proveit/graphs/paths/paths_membership.py b/packages/proveit/graphs/paths/paths_membership.py
new file mode 100644
index 000000000000..781f00e1864f
--- /dev/null
+++ b/packages/proveit/graphs/paths/paths_membership.py
@@ -0,0 +1,23 @@
+# from proveit.logic import SetMembership, SetNonmembership
+from proveit.logic import ClassMembership, ClassNonmembership
+
+
+
+class PathsMembership(ClassMembership):
+    '''
+    Defines methods that apply to membership in the set Paths(G)
+    of all paths in a graph G.
+    '''
+
+    def __init__(self, element, domain):
+        ClassMembership.__init__(self, element, domain)
+
+
+class PathsNonmembership(ClassNonmembership):
+    '''
+    Defines methods that apply to non-membership in the set Paths(G)
+    of all paths in a graph G.
+    '''
+
+    def __init__(self, element, domain):
+        ClassNonmembership.__init__(self, element, domain)
\ No newline at end of file
diff --git a/packages/proveit/graphs/paths_of/__init__.py b/packages/proveit/graphs/paths_of/__init__.py
new file mode 100644
index 000000000000..0e31c3e0c200
--- /dev/null
+++ b/packages/proveit/graphs/paths_of/__init__.py
@@ -0,0 +1,9 @@
+from .paths_of import PathsOf
+
+# KEEP THE FOLLOWING IN __init__.py FOR THEORY PACKAGES.
+#  Make additions above, or add to sys.modules[__name__].__dict__ below.
+# This allows us to import common expression, axioms, and theorems of
+# the theory package directly from the package.
+import sys
+from proveit._core_.theory import TheoryPackage
+sys.modules[__name__] = TheoryPackage(__name__, __file__, locals())
diff --git a/packages/proveit/graphs/paths_of/_brief_description_.txt b/packages/proveit/graphs/paths_of/_brief_description_.txt
new file mode 100644
index 000000000000..8a9fbaa01a54
--- /dev/null
+++ b/packages/proveit/graphs/paths_of/_brief_description_.txt
@@ -0,0 +1 @@
+basic concepts involving paths within a specified graph
diff --git a/packages/proveit/graphs/paths_of/_sub_theories_.txt b/packages/proveit/graphs/paths_of/_sub_theories_.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/paths_of/_theory_nbs_/__init__.py b/packages/proveit/graphs/paths_of/_theory_nbs_/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/paths_of/_theory_nbs_/axioms.ipynb b/packages/proveit/graphs/paths_of/_theory_nbs_/axioms.ipynb
new file mode 100644
index 000000000000..79ec4ea43cdd
--- /dev/null
+++ b/packages/proveit/graphs/paths_of/_theory_nbs_/axioms.ipynb
@@ -0,0 +1,59 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Axioms for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.paths_of</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the axioms of a theory:\n",
+    "%axioms_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "# from ... import ..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin axioms"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end axioms"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/paths_of/_theory_nbs_/common.ipynb b/packages/proveit/graphs/paths_of/_theory_nbs_/common.ipynb
new file mode 100644
index 000000000000..8b25f72fbf20
--- /dev/null
+++ b/packages/proveit/graphs/paths_of/_theory_nbs_/common.ipynb
@@ -0,0 +1,59 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Common expressions for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.paths_of</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the common expressions of a theory:\n",
+    "%common_expressions_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "# from ... import ..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin common"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end common"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/paths_of/_theory_nbs_/demonstrations.ipynb b/packages/proveit/graphs/paths_of/_theory_nbs_/demonstrations.ipynb
new file mode 100644
index 000000000000..baf4cca32605
--- /dev/null
+++ b/packages/proveit/graphs/paths_of/_theory_nbs_/demonstrations.ipynb
@@ -0,0 +1,101 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Demonstrations for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.paths_of</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "%begin demonstrations\n",
+    "\n",
+    "from proveit import E, G, P, V\n",
+    "from proveit.logic.sets import InSet, NotInSet\n",
+    "from proveit.graphs import Graph, PathsOf"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Paths in Graph $G$: $\\mathrm{PathsOf}(\\mathrm{G}(V, E))$"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### MISC. TESTING"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Basic Constructions"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G = Graph(V, E)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "paths_of_g = PathsOf(G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(P, paths_of_g)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(P, paths_of_g)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end demonstrations"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/paths_of/_theory_nbs_/theorems.ipynb b/packages/proveit/graphs/paths_of/_theory_nbs_/theorems.ipynb
new file mode 100644
index 000000000000..1e67f0ce4c5f
--- /dev/null
+++ b/packages/proveit/graphs/paths_of/_theory_nbs_/theorems.ipynb
@@ -0,0 +1,59 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theorems (or conjectures) for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.paths_of</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the theorems of a theory:\n",
+    "%theorems_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "# from ... import ..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin theorems"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end theorems"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/paths_of/_theory_nbs_/theory.ipynb b/packages/proveit/graphs/paths_of/_theory_nbs_/theory.ipynb
new file mode 100644
index 000000000000..98c28c67e646
--- /dev/null
+++ b/packages/proveit/graphs/paths_of/_theory_nbs_/theory.ipynb
@@ -0,0 +1,33 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theory of <a class=\"ProveItLink\" href=\"../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../_theory_nbs_/theory.ipynb\">graphs</a>.paths_of\n",
+    "========\n",
+    "\n",
+    "Provide description here."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "%theory # toggles between interactive and static modes"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/paths_of/paths_of.py b/packages/proveit/graphs/paths_of/paths_of.py
new file mode 100644
index 000000000000..654b90deb36b
--- /dev/null
+++ b/packages/proveit/graphs/paths_of/paths_of.py
@@ -0,0 +1,39 @@
+from proveit import Function, Literal
+
+class PathsOf(Function):
+    '''
+    Given a graph G = G(V, E) with vertex set V and edge set E,
+    PathsOf(G) represents the set of all paths in the graph G.
+    A Path (in an undirected graph G) is a non-empty graph P = (V,E)
+    with non-empty vertex set V and (possibly empty) edge set E such
+    that:
+            V = {x0, x1, ..., xk},
+            E = {{x0,x1}, {x1,x2}, ..., {x_{k-1},x_{k}},
+
+    where the x_{i} are all distinct. The vertices x0 and xk are said
+    to be linked by the path P and are called its endvertices, or
+    endpoints, or simply its ends. The vertices x1, ..., x_{k-1} are
+    the inner vertices of P. The number of edges in the path P,
+    denoted ||P||, is its length (in this example, ||P|| = k).
+    '''
+
+    # the literal operator of the PathsOf operation
+    _operator_ = Literal(string_format='PathsOf',
+                         latex_format=r'\mathrm{PathsOf}',
+                         theory=__file__)
+
+    def __init__(self, G, *, styles=None):
+        '''
+        Represent the set of paths in the graph G = G(V,E) with vertex
+        set V and edge set E.
+        '''
+        Function.__init__(
+                self, PathsOf._operator_, G, styles=styles)
+
+    def membership_object(self, element):
+        from .paths_of_membership import PathsOfMembership
+        return PathsOfMembership(element, self)
+
+    def nonmembership_object(self, element):
+        from .paths_of_membership import PathsOfNonmembership
+        return PathsOfNonmembership(element, self)
diff --git a/packages/proveit/graphs/paths_of/paths_of_membership.py b/packages/proveit/graphs/paths_of/paths_of_membership.py
new file mode 100644
index 000000000000..02e0e1539701
--- /dev/null
+++ b/packages/proveit/graphs/paths_of/paths_of_membership.py
@@ -0,0 +1,21 @@
+from proveit.logic import SetMembership, SetNonmembership
+
+
+class PathsOfMembership(SetMembership):
+    '''
+    Defines methods that apply to membership in the set PathsOf(G)
+    of all paths in graph G.
+    '''
+
+    def __init__(self, element, domain):
+        SetMembership.__init__(self, element, domain)
+
+
+class PathsOfNonmembership(SetNonmembership):
+    '''
+    Defines methods that apply to non-membership in the set PathsOf(G)
+    of all paths in graph G.
+    '''
+
+    def __init__(self, element, domain):
+        SetNonmembership.__init__(self, element, domain)
\ No newline at end of file
diff --git a/packages/proveit/graphs/union/__init__.py b/packages/proveit/graphs/union/__init__.py
new file mode 100644
index 000000000000..611fe3f30a42
--- /dev/null
+++ b/packages/proveit/graphs/union/__init__.py
@@ -0,0 +1,10 @@
+from .graph_union import GraphUnion
+from .graph_union_membership import GraphUnionMembership
+
+# KEEP THE FOLLOWING IN __init__.py FOR THEORY PACKAGES.
+#  Make additions above, or add to sys.modules[__name__].__dict__ below.
+# This allows us to import common expression, axioms, and theorems of
+# the theory package directly from the package.
+import sys
+from proveit._core_.theory import TheoryPackage
+sys.modules[__name__] = TheoryPackage(__name__, __file__, locals())
diff --git a/packages/proveit/graphs/union/_brief_description_.txt b/packages/proveit/graphs/union/_brief_description_.txt
new file mode 100644
index 000000000000..1939b57f4269
--- /dev/null
+++ b/packages/proveit/graphs/union/_brief_description_.txt
@@ -0,0 +1 @@
+basic concepts involving the union of graphs
diff --git a/packages/proveit/graphs/union/_sub_theories_.txt b/packages/proveit/graphs/union/_sub_theories_.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/union/_theory_nbs_/__init__.py b/packages/proveit/graphs/union/_theory_nbs_/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/union/_theory_nbs_/axioms.ipynb b/packages/proveit/graphs/union/_theory_nbs_/axioms.ipynb
new file mode 100644
index 000000000000..6cfe753efda8
--- /dev/null
+++ b/packages/proveit/graphs/union/_theory_nbs_/axioms.ipynb
@@ -0,0 +1,99 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Axioms for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.union</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the axioms of a theory:\n",
+    "%axioms_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "from proveit import n, x, E, G, V\n",
+    "from proveit.logic import Equals, Forall, Or\n",
+    "from proveit.logic.sets import InSet\n",
+    "from proveit.numbers import NaturalPos\n",
+    "from proveit.graphs import G_1_to_n, Graph, GraphUnion, InGraph, x_in_any_G_1_to_n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin axioms"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Graph Union Membership\n",
+    "\n",
+    "An element (i.e., a vertex or edge) of the union of a collection of graphs is an element of at least one of those graphs."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "graph_union_membership_def = Forall(n,\n",
+    "       Forall((x, G_1_to_n),\n",
+    "              Equals(InGraph(x, GraphUnion(G_1_to_n)),\n",
+    "                     x_in_any_G_1_to_n)),\n",
+    "       domain=NaturalPos\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "But that membership axiom doesn't really get at the heart of the desired idea that the union of a set of graphs is itself a graph. To better achieve that and to avoid the idiosyncratic construct $x \\in G$ (meaning $x \\in \\texttt{Vertices}(G) \\lor x \\in \\texttt{Edges}(G)$), we could instead try:\n",
+    "\n",
+    "$\\forall_{G_{1}, G_{2}, \\ldots, G_{n} \\in \\texttt{Graphs}}\n",
+    " \\big[ G_{1} \\cup G_{2} \\cup \\ldots \\cup G_{n} \\in \\texttt{Graphs} \\big]$\n",
+    "\n",
+    "$\\forall_{G_{1}, G_{2}, \\ldots, G_{n} \\in \\texttt{Graphs}}\n",
+    " \\big[\\texttt{Vertices}(G_{1} \\cup G_{2} \\cup \\ldots \\cup G_{n})\n",
+    " = \\texttt{Vertices}(G_{1}) \\cup \\texttt{Vertices}(G_{2}) \\cup \\ldots \\texttt{Vertices}(G_{n})\\big]$\n",
+    "\n",
+    " $\\forall_{G_{1}, G_{2}, \\ldots, G_{n} \\in \\texttt{Graphs}}\n",
+    " \\big[\\texttt{Edges}(G_{1} \\cup G_{2} \\cup \\ldots \\cup G_{n})\n",
+    " = \\texttt{Edges}(G_{1}) \\cup \\texttt{Edges}(G_{2}) \\cup \\ldots \\texttt{Edges}(G_{n})\\big]$\n",
+    "\n",
+    "where we us all three as axiomatic. Alternatively, we could possibly use the Vertices() and Edges() statements as axioms, then prove the more general union statement as a theorem. The proof might be accomplished by showing that the new Edges() set is a subset of the set of 2-element subsets of the new Vertices() set, and then we have the axiom that $\\forall_{V}\\forall_{E \\subseteq [V]^{2}}(\\texttt{Graph}(V, E) \\in \\texttt{Graphs})$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end axioms"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/union/_theory_nbs_/common.ipynb b/packages/proveit/graphs/union/_theory_nbs_/common.ipynb
new file mode 100644
index 000000000000..78cdd0accb17
--- /dev/null
+++ b/packages/proveit/graphs/union/_theory_nbs_/common.ipynb
@@ -0,0 +1,59 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Common expressions for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.union</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the common expressions of a theory:\n",
+    "%common_expressions_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "# from ... import ..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin common"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end common"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/union/_theory_nbs_/demonstrations.ipynb b/packages/proveit/graphs/union/_theory_nbs_/demonstrations.ipynb
new file mode 100644
index 000000000000..b66d300170be
--- /dev/null
+++ b/packages/proveit/graphs/union/_theory_nbs_/demonstrations.ipynb
@@ -0,0 +1,296 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Demonstrations for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.union</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "from proveit import g, i, n, x, G, ExprRange, IndexedVar\n",
+    "from proveit.logic import And, InSet, Or\n",
+    "from proveit.numbers import one, two, NaturalPos\n",
+    "from proveit.graphs import GraphUnion, InGraph, NotInGraph\n",
+    "%begin demonstrations"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Graph Union, Graph Union Membership, & Related Concepts and Methods: $G_{1} \\cup G_{2} \\cup \\cdots \\cup G_{n}$"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Miscellaneous Testing\n",
+    "Testing various classes and methods related to unions of graphs and membership (or non-membership) in such unions. Some of this material could eventually be integrated into the `_demonstrations_` page and/or deleted as development continues."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Basic Constructions: Graph Unions and Union Membership\n",
+    "\n",
+    "A graph $G = (V, E)$ consists of an ordered pair of sets $V$ and $E$, where $V$ is the set of vertices and $E$ is the set of edges. The union $G = G_{1} \\cup G_{2} \\cup \\cdots \\cup G_{n}$ of graphs $G_{1}, G_{2}, \\ldots G_{n}$ is a graph $G = (V, E)$ with vertex $V = V_{1} \\cup \\cdots \\cup V_{n}$ and edge set $E = E_{1} \\cup \\cdots \\cup E_{n}$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G1, G2 = (IndexedVar(G, one), IndexedVar(G, two))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "GraphUnion(G1, G2)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_1_to_n = ExprRange(i, IndexedVar(G, i), one, n)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "GraphUnion(G_1_to_n)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InGraph(G, GraphUnion(G1, G2))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInGraph(G, GraphUnion(G_1_to_n))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Basic Testing of `GraphUnion()` methods\n",
+    "\n",
+    "TBA"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Basic Testing of `GraphUnionMembership()` methods"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_in_union_G1_G2 = InGraph(G, GraphUnion(G1, G2))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_in_union_G1_G2.definition()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_in_union_G1_G2.as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_in_union_G1_G2.unfold(assumptions = [G_in_union_G1_G2])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_in_union_G1_G2.conclude(assumptions = [Or(InGraph(G, G1), InGraph(G, G2))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_in_union_G1_G2.deduce_in_bool()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Basic Testing of `GraphUnionNonmembership()` methods"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_not_in_union_G1_to_Gn = NotInGraph(G, GraphUnion(G_1_to_n))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_not_in_union_G1_G2 = NotInGraph(G, GraphUnion(G1, G2))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_not_in_union_G1_to_Gn.definition(assumptions=[InSet(n, NaturalPos)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_not_in_union_G1_G2.definition()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_not_in_union_G1_to_Gn.as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_not_in_union_G1_G2.as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "try:\n",
+    "    G_not_in_union_G1_G2.conclude(assumptions = [And(NotInGraph(G, G1), NotInGraph(G, G2))])\n",
+    "except Exception as the_exception:\n",
+    "    print(f\"Exception: {the_exception}\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "try:\n",
+    "    G_not_in_union_G1_to_Gn.conclude(\n",
+    "        assumptions = [InSet(n, NaturalPos), And(ExprRange(i, NotInGraph(G, IndexedVar(G, i)), one, n))])\n",
+    "except Exception as the_exception:\n",
+    "    print(f\"Exception: {the_exception}\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_not_in_union_G1_G2.deduce_in_bool()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G_not_in_union_G1_to_Gn.deduce_in_bool(assumptions = [InSet(n, NaturalPos)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end demonstrations"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/union/_theory_nbs_/proofs/membership_folding/thm_proof.ipynb b/packages/proveit/graphs/union/_theory_nbs_/proofs/membership_folding/thm_proof.ipynb
new file mode 100644
index 000000000000..fb55221aacf8
--- /dev/null
+++ b/packages/proveit/graphs/union/_theory_nbs_/proofs/membership_folding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">union</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#membership_folding\">membership_folding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving membership_folding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/union/_theory_nbs_/proofs/membership_unfolding/thm_proof.ipynb b/packages/proveit/graphs/union/_theory_nbs_/proofs/membership_unfolding/thm_proof.ipynb
new file mode 100644
index 000000000000..4fc7a5fecfa6
--- /dev/null
+++ b/packages/proveit/graphs/union/_theory_nbs_/proofs/membership_unfolding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">union</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#membership_unfolding\">membership_unfolding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving membership_unfolding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/union/_theory_nbs_/proofs/nonmembership_equiv/thm_proof.ipynb b/packages/proveit/graphs/union/_theory_nbs_/proofs/nonmembership_equiv/thm_proof.ipynb
new file mode 100644
index 000000000000..2ecd4e40f65a
--- /dev/null
+++ b/packages/proveit/graphs/union/_theory_nbs_/proofs/nonmembership_equiv/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">union</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#nonmembership_equiv\">nonmembership_equiv</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving nonmembership_equiv"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/union/_theory_nbs_/proofs/nonmembership_folding/thm_proof.ipynb b/packages/proveit/graphs/union/_theory_nbs_/proofs/nonmembership_folding/thm_proof.ipynb
new file mode 100644
index 000000000000..cbb9f70e6cae
--- /dev/null
+++ b/packages/proveit/graphs/union/_theory_nbs_/proofs/nonmembership_folding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">union</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#nonmembership_folding\">nonmembership_folding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving nonmembership_folding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/union/_theory_nbs_/proofs/union_membership_is_bool/thm_proof.ipynb b/packages/proveit/graphs/union/_theory_nbs_/proofs/union_membership_is_bool/thm_proof.ipynb
new file mode 100644
index 000000000000..e89f49c703b6
--- /dev/null
+++ b/packages/proveit/graphs/union/_theory_nbs_/proofs/union_membership_is_bool/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">union</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#union_membership_is_bool\">union_membership_is_bool</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving union_membership_is_bool"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/union/_theory_nbs_/proofs/union_nonmembership_is_bool/thm_proof.ipynb b/packages/proveit/graphs/union/_theory_nbs_/proofs/union_nonmembership_is_bool/thm_proof.ipynb
new file mode 100644
index 000000000000..7a6b6fd807ff
--- /dev/null
+++ b/packages/proveit/graphs/union/_theory_nbs_/proofs/union_nonmembership_is_bool/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">union</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#union_nonmembership_is_bool\">union_nonmembership_is_bool</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving union_nonmembership_is_bool"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/union/_theory_nbs_/theorems.ipynb b/packages/proveit/graphs/union/_theory_nbs_/theorems.ipynb
new file mode 100644
index 000000000000..1d0ab199ad71
--- /dev/null
+++ b/packages/proveit/graphs/union/_theory_nbs_/theorems.ipynb
@@ -0,0 +1,134 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theorems (or conjectures) for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.union</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the theorems of a theory:\n",
+    "%theorems_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "from proveit import n, x, E, G, V\n",
+    "from proveit.logic import Equals, Forall, Or\n",
+    "# from proveit.logic.sets import InSet\n",
+    "from proveit.numbers import NaturalPos\n",
+    "from proveit.graphs import (G_1_to_n, Graph, GraphUnion, InGraph, NotInGraph,\n",
+    "                            x_in_any_G_1_to_n, x_not_in_any_G_1_to_n)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin theorems"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "membership_unfolding = (\n",
+    "    Forall(n, Forall((x, G_1_to_n), x_in_any_G_1_to_n, conditions = [InGraph(x, GraphUnion(G_1_to_n))]),\n",
+    "    domain=NaturalPos\n",
+    "))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "membership_folding = (\n",
+    "    Forall(n, Forall((x, G_1_to_n), InGraph(x, GraphUnion(G_1_to_n)), conditions = [x_in_any_G_1_to_n]),\n",
+    "    domain=NaturalPos\n",
+    "))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit.logic import in_bool\n",
+    "union_membership_is_bool = (\n",
+    "    Forall(n, Forall((x, G_1_to_n), in_bool(InGraph(x, GraphUnion(G_1_to_n)))),\n",
+    "    domain=NaturalPos\n",
+    "))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "nonmembership_equiv = (\n",
+    "    Forall(n, Forall((x, G_1_to_n), \n",
+    "    Equals(NotInGraph(x, GraphUnion(G_1_to_n)), x_not_in_any_G_1_to_n)),\n",
+    "    domain=NaturalPos)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "nonmembership_folding = (\n",
+    "    Forall(n, Forall((x, G_1_to_n), \n",
+    "              NotInGraph(x, GraphUnion(G_1_to_n)),\n",
+    "              conditions=[x_not_in_any_G_1_to_n]),\n",
+    "    domain=NaturalPos)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit.logic import in_bool\n",
+    "union_nonmembership_is_bool = (\n",
+    "    Forall(n, Forall((x, G_1_to_n), in_bool(NotInGraph(x, GraphUnion(G_1_to_n)))),\n",
+    "    domain=NaturalPos\n",
+    "))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end theorems"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/union/_theory_nbs_/theory.ipynb b/packages/proveit/graphs/union/_theory_nbs_/theory.ipynb
new file mode 100644
index 000000000000..906066dfefff
--- /dev/null
+++ b/packages/proveit/graphs/union/_theory_nbs_/theory.ipynb
@@ -0,0 +1,33 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theory of <a class=\"ProveItLink\" href=\"../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../_theory_nbs_/theory.ipynb\">graphs</a>.union\n",
+    "========\n",
+    "\n",
+    "Provide description here."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "%theory # toggles between interactive and static modes"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/union/graph_union.py b/packages/proveit/graphs/union/graph_union.py
new file mode 100644
index 000000000000..0c7db04b02a6
--- /dev/null
+++ b/packages/proveit/graphs/union/graph_union.py
@@ -0,0 +1,53 @@
+from proveit import Literal, Operation, USE_DEFAULTS, relation_prover
+from proveit import m, n, A, S, x
+
+
+class GraphUnion(Operation):
+    '''
+    GraphUnion(G1, G2, ..., Gn) represents the union of graphs
+    G1, G2, ..., Gn. The union of graphs G_{i} = (V_{i}, E_{i})
+    with vertex sets V_{i} and edge sets E_{i} gives a graph
+    G = (V, E) = (U V_{i}, U E_{i}) with vertex set V consisting
+    of the union of the vertex sets V_{i} and an edge set E
+    consisting of the union of the edge sets E_{i}.
+    For example, the GraphUnion of two graphs G1 = (V1, E1) and
+    G2 = (V2, E2) is given by:
+
+        G1 U G2 = (V1 U V2, E1 U E2).
+
+    '''
+
+    # Operator of the GraphUnion operation. We use the same notation
+    # as we do for union of sets more generally, relying on context
+    # to make clear what type of union operation is occurring.
+    _operator_ = Literal(
+            string_format='U', latex_format=r'\cup',theory=__file__)
+
+    def __init__(self, *operands, styles=None):
+        '''
+        The union of any number of graphs: G1 U G2 U ... U Gn
+        '''
+        Operation.__init__(self, GraphUnion._operator_, operands,
+                           styles=styles)
+
+    def membership_object(self, element):
+        from .graph_union_membership import GraphUnionMembership
+        return GraphUnionMembership(element, self)
+
+    def nonmembership_object(self, element):
+        from .graph_union_membership import GraphUnionNonmembership
+        return GraphUnionNonmembership(element, self)
+
+    # keeping this here for now; the graph version will need the
+    # concept of subgraph (instead of subset).
+    # @relation_prover
+    # def deduce_superset_eq_relation(self, superset, **defaults_config):
+    #     # Check for special case of a union subset
+    #     # A_1 union ... union ... A_m \subseteq S
+    #     from . import union_inclusion
+    #     _A = self.operands
+    #     _m = _A.num_elements()
+    #     _S = superset
+    #     return union_inclusion.instantiate(
+    #                 {A:_A, m:_m, S:_S})
+            
\ No newline at end of file
diff --git a/packages/proveit/graphs/union/graph_union_membership.py b/packages/proveit/graphs/union/graph_union_membership.py
new file mode 100644
index 000000000000..957126c7d111
--- /dev/null
+++ b/packages/proveit/graphs/union/graph_union_membership.py
@@ -0,0 +1,170 @@
+from proveit import USE_DEFAULTS, equality_prover, prover
+from proveit.logic import SetMembership, SetNonmembership
+from proveit.numbers import num
+from proveit import m, n, x, G
+from proveit.graphs import GraphMembership, GraphNonmembership
+
+
+class GraphUnionMembership(GraphMembership):
+    '''
+    GraphUnionMembership represents membership in a union of
+    multiple graphs and provides methods that apply to membership
+    in a union of graphs. See GraphUnion class for related concepts.
+    Note that the membership relation is InGraph instead of InSet.
+    '''
+
+    def __init__(self, element, domain):
+        GraphMembership.__init__(self, element, domain)
+
+    # for future consideration, borrowed from sets UnionMembership
+    # def side_effects(self, judgment):
+    #     '''
+    #     Unfold the enumerated set membership as a side-effect.
+    #     '''
+    #     yield self.unfold
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        Given self = elem in (G1 U G2 U ... U Gn), deduce and return 
+            [elem in (G1 U G2 U ... U Gn)] = 
+            [(elem in G1) V (elem in G2) V ... V (elem in Gn)].
+        '''
+        from . import graph_union_membership_def
+        element = self.element
+        operands = self.domain.operands
+        _G_sub = operands
+        _n_sub = _G_sub.num_elements()
+        return graph_union_membership_def.instantiate(
+                {n: _n_sub, x: element, G: _G_sub}, auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self=[elem in (G1 U G2 U ... U Gn)], return
+        [(elem in G1) V (elem in G2) V ... V (elem in Gn)].
+        '''
+        from proveit.logic import Or
+        from proveit.graphs import InGraph
+        element = self.element
+        return Or(*self.domain.operands.map_elements(
+                lambda subset : InGraph(element, subset)))
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From self = [elem in (G1 U G2 U ... U Gn)], derive and return
+        [(elem in G1) V (elem in G2) V ... V (elem in Gn)].
+        '''
+        from . import membership_unfolding 
+        element = self.element
+        operands = self.domain.operands
+        _G_sub = operands
+        _n_sub = _G_sub.num_elements()
+        return membership_unfolding.instantiate(
+            {n:_n_sub, x:element, G:_G_sub}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on self = [elem in (G1 U G2 U ... U Gn)], and knowing or
+        assuming [[elem in G1] V [elem in G2] V ... V [elem in Gn]],
+        derive and return self.
+        '''
+        from . import membership_folding
+        element = self.element
+        operands = self.domain.operands
+        _G_sub = operands
+        _n_sub = _G_sub.num_elements()
+        return membership_folding.instantiate(
+                {n:_n_sub, x: element, G:_G_sub})
+
+    @prover
+    def deduce_in_bool(self, **defaults_config):
+        '''
+        Deduce that self = (elem in (G1 U G2 U ... U Gn)) is in Bool.
+        Used as helper function elsewhere and not usually explicitly
+        called by the user.
+        '''
+        from . import union_membership_is_bool
+        element = self.element
+        operands = self.domain.operands
+        _G_sub = operands
+        _n_sub = _G_sub.num_elements()
+        return union_membership_is_bool.instantiate(
+                {n:_n_sub, x:element, G:_G_sub})
+
+
+class GraphUnionNonmembership(GraphNonmembership):
+    '''
+    Defines methods that apply to non-membership in a union of graphs.
+    '''
+
+    def __init__(self, element, domain):
+        GraphNonmembership.__init__(self, element, domain)
+
+    def side_effects(self, judgment):
+        '''
+        Currently no side-effects for union nonmembership.
+        '''
+        return
+        yield
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self=[elem not in (G1 U G2 U ... U Gn)], deduce and return:
+        |- [elem not in (G1 U G2 U ... U Gn)]
+         = [(elem not in G1) AND (elem not in G2) AND ...
+                AND elem not in Gn].
+        '''
+
+        from . import nonmembership_equiv
+        element = self.element
+        operands = self.domain.operands
+        _G_sub = operands
+        _n_sub = _G_sub.num_elements()
+        return nonmembership_equiv.instantiate(
+            {n: _n_sub, x: element, G: _G_sub}, auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self=[elem not in (G1 U G2 U ... U Gn)], return
+        [(elem not in G1) AND (elem not in G2) AND ... 
+         AND (elem not in Gn)].
+        '''
+        from proveit.logic import And, NotInSet
+        from proveit.graphs import NotInGraph
+        element = self.element
+        return And(*self.domain.operands.map_elements(
+                lambda subset : NotInGraph(element, subset)))
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        From self = [elem not in (G1 U G2 U ... U Gn)], and knowing
+        or assuming [elem not in G1] AND [elem not in G2] AND ...
+        AND [elem not in Gn], derive and return self.
+        '''
+        from . import nonmembership_folding
+        element = self.element
+        operands = self.domain.operands
+        _G_sub = operands
+        _n_sub = _G_sub.num_elements()
+        return nonmembership_folding.instantiate(
+            {n:_n_sub, x:element, G:_G_sub})
+
+    @prover
+    def deduce_in_bool(self, **defaults_config):
+        '''
+        Deduce that self = (elem NotIn (G1 U G2 U ... U Gn)) is in Bool.
+        Used as helper function elsewhere and not usually explicitly
+        called by the user.
+        '''
+        from . import union_nonmembership_is_bool
+        element = self.element
+        operands = self.domain.operands
+        _G_sub = operands
+        _n_sub = _G_sub.num_elements()
+        return union_nonmembership_is_bool.instantiate(
+                {n:_n_sub, x:element, G:_G_sub})
+
diff --git a/packages/proveit/graphs/vertices.py b/packages/proveit/graphs/vertices.py
new file mode 100644
index 000000000000..f184809a1fc1
--- /dev/null
+++ b/packages/proveit/graphs/vertices.py
@@ -0,0 +1,90 @@
+from proveit import u, v, G, equality_prover, Function, Literal
+
+class Vertices(Function):
+    '''
+    Given a graph G(V, E) with vertex set V and edge set E,
+    Vertices(G(V, E)) represents the set V of vertices ---
+    that is, Vertices(G(V,E)) = V.
+    '''
+
+    # the literal operator of the Vertices operation
+    _operator_ = Literal(string_format='Vertices',
+                         latex_format=r'\mathrm{Vertices}',
+                         theory=__file__)
+
+    def __init__(self, G, *, styles=None):
+        '''
+        Given a graph G(V,E) = (V,E), represent the vertex set of G.
+        '''
+        self.graph = G
+        Function.__init__(
+                self, Vertices._operator_, G, styles=styles)
+
+    def membership_object(self, element):
+        from .vertices_membership import VerticesMembership
+        return VerticesMembership(element, self)
+
+    def nonmembership_object(self, element):
+        from .vertices_membership import VerticesNonmembership
+        return VerticesNonmembership(element, self)
+
+
+class Degree(Function):
+    '''
+    Degree(v, G), denoted deg(v), represents the degree or valency of
+    the vertex v occuring in the graph G. For an undirected graph with
+    no loops, deg(v) will be equal to the number of edges incident
+    with vertex v. For vertex v in a directed graph, deg(v) will equal
+    the sum of the in-degree and out-degree of vertex v.
+    '''
+
+    # the literal operator of the Degree operation
+    _operator_ = Literal(string_format='deg',
+                         latex_format=r'\mathrm{deg}',
+                         theory=__file__)
+
+    def __init__(self, v, G, *, styles=None):
+        '''
+        Given a vertex v of a graph G(V,E) = (V,E), represent the
+        degree of the vertex v.
+        '''
+        Function.__init__(
+                self, Degree._operator_, (v, G), styles=styles)
+
+
+class AdjacentVertices(Function):
+    '''
+    For vertices u, v in graph G, AdjacentVertices(u, v, G) denotes
+    the claim that vertices u and v are adjacent in graph G (i.e., 
+    AdjacentVertices(u, v, G) means that {u, v} is an edge in graph G).
+    We use the full 'AdjacentVertices()' for naming and calling the
+    class to distinguish the situation from adjacent edges.
+    '''
+    # the literal operator of the Adjacent operation
+    _operator_ = Literal(string_format='Adjacent',
+                         latex_format=r'\mathrm{Adjacent}',
+                         theory=__file__)
+
+    def __init__(self, u, v, G, *, styles=None):
+        '''
+        Given vertices u, v in graph G, represent the claim that
+        vertices u and v are adjacent in G.
+        '''
+        self.graph = G
+        self.vertices = (u, v)
+        Function.__init__(
+                self, AdjacentVertices._operator_, (u, v, G), styles=styles)
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = AdjacentVertices(u, v, G), deduce and return the
+        equality: AdjacentVertices(u, v, G) = {u, v} in Edges(G).
+        '''
+        from . import adjacent_vertices_def
+        _u_sub = self.operands[0]
+        _v_sub = self.operands[1]
+        _G_sub = self.operands[2]
+        return adjacent_vertices_def.instantiate(
+            {u:_u_sub, v:_v_sub, G:_G_sub}, auto_simplify=False)
+
diff --git a/packages/proveit/graphs/vertices_membership.py b/packages/proveit/graphs/vertices_membership.py
new file mode 100644
index 000000000000..172555a414cd
--- /dev/null
+++ b/packages/proveit/graphs/vertices_membership.py
@@ -0,0 +1,156 @@
+from proveit import v, E, V, equality_prover, prover
+from proveit.logic import SetMembership, SetNonmembership
+from proveit.graphs import Graph
+
+
+class VerticesMembership(SetMembership):
+    '''
+    Defines methods that apply to membership in the set
+    Vertices(G(V,E)) of the vertices V of graph G.
+    '''
+
+    def __init__(self, element, domain):
+        SetMembership.__init__(self, element, domain)
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = [elem in Vertices(Graph(V,E))], deduce and return:
+            [elem in Vertices(Graph(V,E))] = [elem in V]
+        '''
+
+        from . import vertices_membership_def
+        element = self.element
+        _V_sub  = self.domain.graph.vertex_set
+        _E_sub  = self.domain.graph.edge_set
+        return vertices_membership_def.instantiate(
+                {v:element, V:_V_sub, E:_E_sub },auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self = [elem in Vertices(Graph(V,E))], return: [elem in V]
+        (i.e. an expression, not a Judgment). Only works if the
+        Vertices domain has as an operand an actual Graph object
+        Graph(V,E) with a specified vertex set V.
+        '''
+        if isinstance(self.domain.operand, Graph):
+            from proveit.logic import InSet
+            element = self.element
+            _V =  self.domain.graph.vertex_set
+            return InSet(element, _V)
+        else:
+            raise NotImplementedError(
+                "VerticesMembership.as_defined() was called on "
+                f"self = {self.expr} with domain = {self.expr.domain}, "
+                "but the method is implemented only for domains of the "
+                "form Vertices(G) where G is an explicit Graph object "
+                "of the form G = Graph(V,E) with a named vertex set V.")
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From self = [elem in Vertices(Graph(V,E))],
+        derive and return [elem in V], knowing or assuming self.
+        '''
+        from . import vertices_membership_unfolding
+        element = self.element
+        _V_sub  = self.domain.graph.vertex_set
+        _E_sub  = self.domain.graph.edge_set
+        return vertices_membership_unfolding.instantiate(
+            {v:element, V:_V_sub, E:_E_sub}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on self = [elem in Vertices(Graph(V,E))], and
+        knowing or assuming [elem in V] (and that E is a subset
+        of [V]^2, a subset of the set of 2-element subsets of V)
+        derive and return self.
+        '''
+        from . import vertices_membership_folding
+        element = self.element
+        _V_sub  = self.domain.graph.vertex_set
+        _E_sub  = self.domain.graph.edge_set
+        return vertices_membership_folding.instantiate(
+            {v:element, V:_V_sub, E:_E_sub}, auto_simplify=False)
+
+
+
+class VerticesNonmembership(SetNonmembership):
+    '''
+    Defines methods that apply to non-membership in the set
+    Vertices(G(V,E)) of the vertices V of graph G.
+    '''
+
+    def __init__(self, element, domain):
+        SetNonmembership.__init__(self, element, domain)
+
+    def side_effects(self, judgment):
+        '''
+        Currently no side-effects for VerticesNonmembership.
+        '''
+        return
+        yield
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = [elem not in Vertices(Graph(V,E))], deduce and
+        return: [elem not in Vertices(Graph(V,E))] = [elem not in V].
+        '''
+
+        from . import vertices_nonmembership_def
+        element = self.element
+        _V_sub  = self.domain.graph.vertex_set
+        _E_sub  = self.domain.graph.edge_set
+        return vertices_nonmembership_def.instantiate(
+                {v:element, V:_V_sub, E:_E_sub },auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self = [elem not in Vertices(Graph(V,E))],
+        return: [elem not in V] (i.e. an expression, not a Judgment)
+        '''
+        if isinstance(self.domain.operand, Graph):
+            from proveit.logic import NotInSet
+            element = self.element
+            _V =  self.domain.graph.vertex_set
+            return NotInSet(element, _V)
+        else:
+            raise NotImplementedError(
+                "VerticesNonmembership.as_defined() called on "
+                f"self = {self.expr} with domain = {self.expr.domain}, "
+                "but the method is implemented only for domains of the "
+                "form Vertices(G) where G is an explicit Graph object "
+                "of the form G = Graph(V,E) with a named vertex set V.")
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From self = [elem not in Vertices(Graph(V,E))],
+        derive and return [elem not in V], knowing or assuming self,
+        (and that E is a subset of [V]^2, i.e., a subset of the set
+        of 2-element subsets of V).
+        '''
+        from . import vertices_nonmembership_unfolding
+        element = self.element
+        _V_sub  = self.domain.graph.vertex_set
+        _E_sub  = self.domain.graph.edge_set
+        return vertices_nonmembership_unfolding.instantiate(
+            {v:element, V:_V_sub, E:_E_sub}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on self = [elem not in Vertices(Graph(V,E))], and
+        knowing or assuming [elem not in V] (and that E is a subset
+        of [V]^2, a subset of the set of 2-element subsets of V)
+        derive and return self.
+        '''
+        from . import vertices_nonmembership_folding
+        element = self.element
+        _V_sub  = self.domain.graph.vertex_set
+        _E_sub  = self.domain.graph.edge_set
+        return vertices_nonmembership_folding.instantiate(
+            {v:element, V:_V_sub, E:_E_sub}, auto_simplify=False)
+
diff --git a/packages/proveit/graphs/walks/__init__.py b/packages/proveit/graphs/walks/__init__.py
new file mode 100644
index 000000000000..08045500d479
--- /dev/null
+++ b/packages/proveit/graphs/walks/__init__.py
@@ -0,0 +1,13 @@
+from .walks import (BeginningVertex, Circuits, ClosedTrails,
+        ClosedWalk, ClosedWalks, Cycles,
+        EdgeSequence, EdgeSet, EndingVertex, EndVertices,
+        EulerianCircuits, EulerianTrails, Paths, Trails,
+        WalkLength, Walks)
+
+# KEEP THE FOLLOWING IN __init__.py FOR THEORY PACKAGES.
+#  Make additions above, or add to sys.modules[__name__].__dict__ below.
+# This allows us to import common expression, axioms, and theorems of
+# the theory package directly from the package.
+import sys
+from proveit._core_.theory import TheoryPackage
+sys.modules[__name__] = TheoryPackage(__name__, __file__, locals())
diff --git a/packages/proveit/graphs/walks/_brief_description_.txt b/packages/proveit/graphs/walks/_brief_description_.txt
new file mode 100644
index 000000000000..189a5f032a8a
--- /dev/null
+++ b/packages/proveit/graphs/walks/_brief_description_.txt
@@ -0,0 +1 @@
+walks, trails, paths, circuits, and cycles, as sequences of vertices within a graph
diff --git a/packages/proveit/graphs/walks/_sub_theories_.txt b/packages/proveit/graphs/walks/_sub_theories_.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/__init__.py b/packages/proveit/graphs/walks/_theory_nbs_/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/axioms.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/axioms.ipynb
new file mode 100644
index 000000000000..4c87cf4cb082
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/axioms.ipynb
@@ -0,0 +1,330 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Axioms for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.walks</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the axioms of a theory:\n",
+    "%axioms_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "from proveit import i, k, C, G, S, T, W, X, Function\n",
+    "from proveit.logic import Card, Equals, Forall, Functions, Injections, InSet, Set, SetOfAll\n",
+    "from proveit.logic.sets import Restriction\n",
+    "from proveit.numbers import zero, one, three, Add, greater_eq, Interval, Natural, subtract\n",
+    "from proveit.graphs import (\n",
+    "        AdjacentVertices, BeginningVertex, Circuits, ClosedTrails,\n",
+    "        ClosedWalk, ClosedWalks, Cycles, Edges, EdgeSequence, EndingVertex,\n",
+    "        EndVertices, EulerianCircuits, Graphs, Paths, Size, Trails, Vertices, Walks)\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin axioms"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Walks & Closed Walks\n",
+    "\n",
+    "A walk $W$ of length $k$ in a simple graph $G$, denoted using $W \\in \\texttt{Walks}(k, G)$, is essentially a sequence of vertices of $G$ such that whenever two vertices $u$ and $v$ are consecutive in the sequence, $u$ and $v$ are also adjacent in $G$. A _closed_ walk $W$ of length $k$ in a simple graph $G$, denoted using $W \\in \\texttt{ClosedWalks}(k, G)$, is a walk that begins and ends at the same vertex. Notice that trails, paths, circuits, and cycles are all special types of walks."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "walks_k_G_def = (\n",
+    "    Forall(G,\n",
+    "    Forall(k,\n",
+    "    Equals(\n",
+    "        Walks(k, G),\n",
+    "        SetOfAll(S,\n",
+    "             S,\n",
+    "             conditions = [Forall(i, AdjacentVertices(Function(S, i), Function(S, Add(i, one)), G),\n",
+    "                                  domain = Interval(zero, subtract(k, one)))],\n",
+    "             domain = Functions(Interval(zero, k), Vertices(G)))\n",
+    "    ),\n",
+    "    domain = Natural),\n",
+    "    domain = Graphs\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Since a length-$k$ walk $W$ in graph $G$ is a function $W:(0, 1, \\ldots, k)\\rightarrow \\texttt{Vertices}(G)$, the begining vertex is $W(0)$ and the ending vertex is $W(k)$. Sometimes it is useful to consider the _set_ of both endvertices, denoted with $\\texttt{EndVertices}(W)$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "beginning_vertex_def = (\n",
+    "    Forall(G,\n",
+    "    Forall(k,\n",
+    "    Forall(W,\n",
+    "        Equals(BeginningVertex(W), Function(W, zero)),\n",
+    "    domain =Walks(k, G)),\n",
+    "    domain = Natural),\n",
+    "    domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "ending_vertex_def = (\n",
+    "    Forall(G,\n",
+    "    Forall(k,\n",
+    "    Forall(W,\n",
+    "        Equals(EndingVertex(W), Function(W, k)),\n",
+    "    domain=Walks(k, G)),\n",
+    "    domain = Natural),\n",
+    "    domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "endvertices_def = (\n",
+    "    Forall(G,\n",
+    "    Forall(k,\n",
+    "    Forall(W,\n",
+    "        Equals(EndVertices(W), Set(Function(W, zero), Function(W, k))),\n",
+    "    domain=Walks(k, G)),\n",
+    "    domain = Natural),\n",
+    "    domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "A __closed__ walk $W$ is a walk whose beginning and ending vertices are the same. We could define such closure using the observation that $|\\texttt{EndVertices}(W)| = 1$, but it's more straightforward to use our `BeginningVertex` and `EndingVertex` classes:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "closed_walks_k_G_def = (\n",
+    "    Forall(G, Forall(k,\n",
+    "        Equals(\n",
+    "            ClosedWalks(k, G),\n",
+    "            SetOfAll(W,\n",
+    "                 W,\n",
+    "                 conditions = [Equals(BeginningVertex(W), EndingVertex(W))],\n",
+    "                 domain = Walks(k, G) )\n",
+    "        ),\n",
+    "    domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Trails & Closed Trails\n",
+    "\n",
+    "A trail $T$ of length $k$ in a simple graph $G$, denoted using $T \\in \\texttt{Trails}(k, G)$, is a walk in which no edge is repeated. This means that the _edge_ sequence is a one-to-one (injective) function $S: [0, 1, \\ldots, k] \\xrightarrow[]{1:1} \\texttt{Edges}(G)$. A _closed_ trail is a trail in which the beginning and ending vertices are the same."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "trails_k_G_def = (\n",
+    "    Forall(G, Forall(k,\n",
+    "        Equals(\n",
+    "            Trails(k, G),\n",
+    "            SetOfAll(W,\n",
+    "                 W,\n",
+    "                 conditions = [InSet(EdgeSequence(W), Injections(Interval(zero, subtract(k, one)), Edges(G)))],\n",
+    "                 domain = Walks(k, G) )\n",
+    "        ),\n",
+    "    domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "closed_trails_k_G_def = (\n",
+    "    Forall(G, Forall(k,\n",
+    "        Equals(\n",
+    "            ClosedTrails(k, G),\n",
+    "            SetOfAll(W,\n",
+    "                 W,\n",
+    "                 conditions = [InSet(EdgeSequence(W), Injections(Interval(zero, subtract(k, one)), Edges(G)))],\n",
+    "                 domain = ClosedWalks(k, G) )\n",
+    "        ),\n",
+    "    domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Paths\n",
+    "\n",
+    "A path $P$ of length $k$ in a simple graph $G$, denoted using $P \\in \\texttt{Paths}(k, G)$, is a walk in which no vertex (and thus no edge) is repeated. This means that the vertex sequence is a one-to-one (injective) function $S: [0, 1, \\ldots, k] \\rightarrow \\texttt{Vertices}(G)$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "paths_k_G_def = (\n",
+    "    Forall(G, Forall(k,\n",
+    "        Equals(\n",
+    "            Paths(k, G),\n",
+    "            SetOfAll(W,\n",
+    "                 W,\n",
+    "                 conditions = [InSet(W, Injections(Interval(zero, subtract(k, one)), Vertices(G)))],\n",
+    "                 domain = Walks(k, G), )\n",
+    "        ),\n",
+    "    domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Circuits\n",
+    "\n",
+    "A circuit $C$ of length $k$ in a simple graph $G$, denoted using $C \\in \\texttt{Circuits}(k, G)$, is a closed trail of length $3$ or more. This means that for $k \\in {3, 4, 5, \\ldots}$, a closed trail _is_ a circuit."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "circuits_k_G_def = (\n",
+    "    Forall(G, Forall(k,\n",
+    "        Equals(\n",
+    "            Circuits(k, G),\n",
+    "            ClosedTrails(k, G) ),\n",
+    "    conditions = [greater_eq(k, three)], domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Eulerian Circuits"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "An Eulerian circuit in graph $G$, denoted using $C \\in \\texttt{EulerianCircuits}(G)$, is a circuit in $G$ that uses every edge in $G$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "eulerian_circuits_G_def = (\n",
+    "    Forall(G,\n",
+    "        Equals(\n",
+    "            EulerianCircuits(G),\n",
+    "            Circuits(Size(G), G) ),\n",
+    "    domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Cycles\n",
+    "\n",
+    "A cycle $C$ of length $k$ in a simple graph $G$, denoted using $C \\in \\texttt{Cycles}(k, G)$, is a circuit in which no vertex is repeated (except the beginning and ending vertices). Another way of saying this is to realize that a cycle is a circuit (_i.e._, a closed trail) $C \\in \\texttt{Circuits}(k, G)$ such that the restriction $C\\vert_{[0, 1, \\ldots, k-1]}$ is a length-($k$-$1$) path in $G$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "cycles_k_G_def = (\n",
+    "    Forall(G, Forall(k,\n",
+    "        Equals(Cycles(k, G),\n",
+    "               SetOfAll(C, C,\n",
+    "               conditions = [InSet(Restriction(C, Interval(zero, subtract(k, one))),\n",
+    "                                   Paths(subtract(k, one), G))],\n",
+    "               domain = Circuits(k, G))),\n",
+    "    conditions = [greater_eq(k, three)], domain = Natural),\n",
+    "    domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end axioms"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/common.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/common.ipynb
new file mode 100644
index 000000000000..3e822a6f5d42
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/common.ipynb
@@ -0,0 +1,99 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Common expressions for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.walks</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the common expressions of a theory:\n",
+    "%common_expressions_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "from proveit import e, i, k, n, C, G, W, ExprTuple, ExprRange, IndexedVar\n",
+    "from proveit.numbers import one\n",
+    "from proveit.graphs import Cycles, EdgeSet"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin common"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "C_1_to_n = ExprTuple(ExprRange(i, IndexedVar(C, i), one, n))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "W_1_to_n = ExprTuple(ExprRange(i, IndexedVar(W, i), one, n))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "EdgeSet_W_1_to_n = ExprTuple(ExprRange(i, EdgeSet(IndexedVar(W, i)), one, n))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "e_1_to_n = ExprRange(i, IndexedVar(e, i), one, n)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Cycles_e_1_to_n = ExprTuple(ExprRange(i, Cycles(IndexedVar(e, i), G), one, n))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end common"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/demonstrations.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/demonstrations.ipynb
new file mode 100644
index 000000000000..adff0f2a4ac7
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/demonstrations.ipynb
@@ -0,0 +1,1127 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Demonstrations for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.walks</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "from proveit import a, b, i, k, C, G, P, S, T, W, Function\n",
+    "from proveit.logic import And, Equals, Forall, InClass, InSet, Set, SetOfAll\n",
+    "from proveit.logic.sets import Functions, Injections\n",
+    "from proveit.numbers import zero, one, three, Add, greater_eq, Interval, Natural, subtract\n",
+    "from proveit.graphs import (\n",
+    "        AdjacentVertices, BeginningVertex, Circuits, ClosedTrails,\n",
+    "        ClosedWalks, Cycles, Edges, EdgeSequence, EdgeSet, EndingVertex,\n",
+    "        EndVertices, EulerianCircuits, EulerianTrails, Graph, Graphs, Paths,\n",
+    "        Size, Trails, Vertices, WalkLength, Walks)\n",
+    "%begin demonstrations\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Walks of Various Kinds\n",
+    "### Including: Walks, Trails, Paths, Circuits, & Cycles"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`Walks(k, G)` represents the set of all walks of length $k$ in the simple undirected graph $G$. \n",
+    "\n",
+    "A __walk__ $W$ in a simple undirected graph $G$ is a sequence $(u = v_0, v_1, ..., v = v_k)$ of vertices of $G$ such that consecutive vertices in the sequence are adjacent in $G$ --- in other words, every pair $\\{v_{i}, v_{i+1}\\}$ of vertices (for $i \\in \\{0, 1, ..., k-1\\}$) in the sequence corresponds to an edge in $G$. We have chosen to embrace a formal definition of a sequence, so a length-$k$ walk in graph $G$ is actually a function, $W:[0, 1, 2, \\ldots, k]\\rightarrow \\texttt{Vertices}(G)$.\n",
+    "\n",
+    "Trails, paths, circuits, and cycles are all special cases of walks.\n",
+    "\n",
+    "In the walk with sequence $(u = v_0, v_1, ..., v = v_k)$, $u$ and $v$ are called the endvertices or endpoints of the walk, and a walk from $u$ to $v$ is often referred to as a $u$-$v$ walk. The vertices $v_1, v_2, ..., v_{k-1}$ are called _internal vertices_. The number of edges in the walk $W$, denoted by $||W||$, is the _length_ of the walk (in this example, $||W|| = k$). The number of vertices in walk $W$, including multiplicities of repeated vertices, is denoted $|W|$, analogous to the notation for the order of a graph, $|G|$. In the example used above, $|W| = k+1$."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Miscellaneous Testing\n",
+    "Testing various classes and methods related to walks in an undirected simple graph. Some of this material could eventually be integrated into the `_demonstrations_` page and/or deleted as development continues."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Walks(k, G)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We can claim that $W$ is a length-$k$ walk in $G$ by claiming $W$ is a member of the set of all such length-$k$ walks:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, Walks(k, G))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "And we could make more specific assumptions about a walk $W$, for example claiming or assuming that $W$ is a length-$k$ _u_,_v_-walk in $G$ “connecting” vertices $u$ and $v$, using the following:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "temp_assumptions = [InSet(W, Walks(k, G)), Equals(EndVertices(W), Set(a, b))]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The length (_i.e._, number of edges) in a walk $W$, denoted by $\\|W\\|$:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "WalkLength(W)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Assert that walk $W$ is a closed walk:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, ClosedWalks(k, G))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The sequence of edges traveled during a walk $W$ is denoted by $\\texttt{EdgeSeq}(W)$:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "EdgeSequence(W)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The set (not the sequence) of edges traveled during a walk $W$ is denoted by $\\texttt{EdgeSet}(W)$:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "EdgeSet(W)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`Trails(k, G)` represents the set of all trails of length $k$ in the simple graph $G$. \n",
+    "\n",
+    "A __trail__ $T$ in a simple graph $G$ is a walk in which no edge is repeated. A _closed_ trail is a trail in which the beginning vertex and ending vertex are the same."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Trails(k, G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, Trails(k, G))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "ClosedTrails(k, G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, ClosedTrails(k, G))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`Paths(k, G)` represents the set of all paths of length $k$ in the simple graph $G$. \n",
+    "\n",
+    "A __path__ $P$ in a simple graph $G$ is a walk in which no vertex (and thus no edge) is repeated."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Paths(k, G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(P, Paths(k, G))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "A __circuit__ is a closed trail (_i.e._, a closed walk with no repeated edges). A circuit of length $k$ in graph $G$ is denoted by its membership in the class of all such circuits:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Circuits(k, G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Circuits(k, G))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "An __Eulerian circuit__ in graph $G$ is a circuit in $G$ that uses every edge of $G$. An Eulerian circuit in graph $G$ then always has length equal to the number of edges in $G$ (or the size of $G$), denoted by $||G||$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "EulerianCircuits(G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, EulerianCircuits(G))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "A __cycle__ is a circuit in which no vertex is repeated, in other words: a closed walk in which no vertex (and thus no edge) is repeated. With some abuse of terminology, one might call a cycle a “closed path.” A cycle of length $k$ (generally referred to as a $k$-cycle) in graph $G$ is denoted by its membership in the class of all such cycles:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Cycles(k, G)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# cycle membership under construction"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The _set_ of endvertices (or endpoints) of a walk $W$ is denoted $\\texttt{EndVertices}(W)$:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "EndVertices(W)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "EndVertices(W).definition(assumptions = [InClass(G, Graphs), InSet(WalkLength(W), Natural), InSet(W, Walks(WalkLength(W), G))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Since a length-$k$ walk $W$ in graph $G$ is a function $W:(0, 1, \\ldots, k)\\rightarrow \\texttt{Vertices}(G)$, there is a natural “begining vertex” and “ending vertex,” denoted `BeginningVertex(W)` and `EndingVertex(W)`, respectively."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "BeginningVertex(W)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "EndingVertex(W)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The set of all Eulerian trails in graph $G$:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "EulerianTrails(G)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Walks Membership and Related Methods"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, Walks(k, G))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, Walks(k, G)).deduce_in_bool(assumptions = [InClass(G, Graphs), InSet(k, Natural)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, Walks(k, G)).definition(assumptions = [InClass(G, Graphs), InSet(k, Natural)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, Walks(k, G)).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, Walks(k, G)).element_length_derivation(assumptions = [InClass(G, Graphs), InSet(k, Natural), InSet(W, Walks(k, G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, Walks(k, G)).unfold(assumptions = [InSet(W, Walks(k, G)), InClass(G, Graphs), InSet(k, Natural)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, Walks(k, G)).conclude(\n",
+    "    assumptions = [InClass(G, Graphs), InSet(k, Natural),\n",
+    "                   InSet(W, SetOfAll(S, S,\n",
+    "                            conditions = [\n",
+    "                                Forall(i, AdjacentVertices(Function(S, i), Function(S, Add(i, one)), G),\n",
+    "                                       domain = Interval(zero, subtract(k, one)))],\n",
+    "                            domain = Functions(Interval(zero, k), Vertices(G))))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, Walks(k, G)).conclude(\n",
+    "    assumptions = [InClass(G, Graphs), InSet(k, Natural),\n",
+    "                   InSet(W, Functions(Interval(zero, k), Vertices(G))),\n",
+    "                   Forall(i, AdjacentVertices(Function(W, i), Function(W, Add(i, one)), G),\n",
+    "                                       domain = Interval(zero, subtract(k, one)))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### ClosedWalks Membership and Related Methods"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, ClosedWalks(k, G))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, ClosedWalks(k, G)).deduce_in_bool(assumptions = [InClass(G, Graphs), InSet(k, Natural)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, ClosedWalks(k, G)).definition(assumptions = [InClass(G, Graphs), InSet(k, Natural)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, ClosedWalks(k, G)).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, ClosedWalks(k, G)).unfold(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(k, Natural), InSet(W, ClosedWalks(k, G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, ClosedWalks(k, G)).conclude(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(k, Natural),\n",
+    "                       InSet(W, SetOfAll(S, S, conditions = [Equals(BeginningVertex(S), EndingVertex(S))],\n",
+    "                                domain = Walks(k, G)))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(W, ClosedWalks(k, G)).derive_element_in_walks(\n",
+    "    assumptions = [InClass(G, Graphs), InSet(k, Natural), InSet(W, ClosedWalks(k, G))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Trails Membership and Related Methods"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, Trails(k, G))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, Trails(k, G)).deduce_in_bool(assumptions = [InClass(G, Graphs), InSet(k, Natural)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, Trails(k, G)).definition(assumptions = [InClass(G, Graphs), InSet(k, Natural)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, Trails(k, G)).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, Trails(k, G)).unfold(assumptions = [InClass(G, Graphs), InSet(k, Natural), InSet(T, Trails(k, G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, Trails(k, G)).conclude(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(k, Natural),\n",
+    "                       InSet(T, SetOfAll(W, W, conditions = [InSet(EdgeSequence(W), Injections(Interval(zero, subtract(k, one)), Edges(G)))],\n",
+    "                                        domain = Walks(k, G)))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, Trails(k, G)).conclude(\n",
+    "        assumptions = [\n",
+    "            InClass(G, Graphs), InSet(k, Natural),\n",
+    "            And(InSet(T, Walks(k, G)),\n",
+    "            InSet(EdgeSequence(T), Injections(Interval(zero, subtract(k, one)), Edges(G))))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, Trails(k, G)).derive_element_in_walks(\n",
+    "    assumptions = [InClass(G, Graphs), InSet(k, Natural), InSet(T, Trails(k, G))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### ClosedTrails Membership and Related Methods"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, ClosedTrails(k, G))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, ClosedTrails(k, G)).deduce_in_bool(assumptions = [InClass(G, Graphs), InSet(k, Natural)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, ClosedTrails(k, G)).definition(assumptions = [InClass(G, Graphs), InSet(k, Natural)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, ClosedTrails(k, G)).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, ClosedTrails(k, G)).unfold(assumptions = [InClass(G, Graphs), InSet(k, Natural), InSet(T, ClosedTrails(k, G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, ClosedTrails(k, G)).conclude(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(k, Natural),\n",
+    "                       InSet(T, SetOfAll(W, W, conditions = [InSet(EdgeSequence(W), Injections(Interval(zero, subtract(k, one)), Edges(G)))],\n",
+    "                                        domain = ClosedWalks(k, G)))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, ClosedTrails(k, G)).conclude(\n",
+    "        assumptions = [\n",
+    "            InClass(G, Graphs), InSet(k, Natural),\n",
+    "            And(InSet(T, ClosedWalks(k, G)),\n",
+    "            InSet(EdgeSequence(T), Injections(Interval(zero, subtract(k, one)), Edges(G))))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, ClosedTrails(k, G)).derive_element_in_closed_walks(\n",
+    "    assumptions = [InClass(G, Graphs), InSet(k, Natural), InSet(T, ClosedTrails(k, G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(T, Walks(k, G)).prove(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(k, Natural), InSet(T, ClosedTrails(k, G))]\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Paths Membership and Related Methods"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(P, Paths(k, G))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(P, Paths(k, G)).deduce_in_bool(assumptions = [InClass(G, Graphs), InSet(k, Natural)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(P, Paths(k, G)).definition(assumptions = [InClass(G, Graphs), InSet(k, Natural)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(P, Paths(k, G)).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(P, Paths(k, G)).unfold(assumptions = [InClass(G, Graphs), InSet(k, Natural), InSet(P, Paths(k, G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(P, Injections(Interval(zero, k), Vertices(G)))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(P, Paths(k, G)).conclude(\n",
+    "    assumptions = [InClass(G, Graphs), InSet(k, Natural),\n",
+    "                   InSet(P, Walks(k, G)), InSet(P, Injections(Interval(zero, k), Vertices(G)))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(P, Paths(k, G)).conclude(\n",
+    "    assumptions = [InClass(G, Graphs), InSet(k, Natural),\n",
+    "                   InSet(P, SetOfAll(W, W, conditions =[InSet(W, Injections(Interval(zero, k), Vertices(G)))],\n",
+    "                                     domain = Walks(k, G)))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(P, Paths(k, G)).derive_element_in_trails(\n",
+    "    assumptions = [InClass(G, Graphs), InSet(k, Natural), InSet(P, Paths(k, G))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Because of cascading of side-effects in walk-related memberships, we can automatically prove that $P$ is a walk if we know or assume that $P$ is a path:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(P, Walks(k, G)).prove(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(k, Natural), InSet(P, Paths(k, G))]\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Circuits Membership and Related Methods"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Circuits(k, G))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Circuits(k, G)).deduce_in_bool(assumptions = [InClass(G, Graphs), InSet(k, Natural)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Circuits(k, G)).definition(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(k, Natural), greater_eq(k, three)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Circuits(k, G)).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Circuits(k, G)).unfold(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(k, Natural),\n",
+    "                       greater_eq(k, three), InSet(C, Circuits(k, G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Circuits(k, G)).conclude(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(k, Natural),\n",
+    "                       greater_eq(k, three), InSet(C, ClosedTrails(k, G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Circuits(k, G)).derive_element_in_closed_trails(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(k, Natural),\n",
+    "                       greater_eq(k, three), InSet(C, Circuits(k, G))]\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Walks(k, G)).prove(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(k, Natural),\n",
+    "                       greater_eq(k, three), InSet(C, Circuits(k, G))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Eulerian Circuits Membership and Related Methods"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, EulerianCircuits(G))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, EulerianCircuits(G)).deduce_in_bool(assumptions = [InClass(G, Graphs)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, EulerianCircuits(G)).definition(assumptions = [InClass(G, Graphs)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, EulerianCircuits(G)).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, EulerianCircuits(G)).unfold(assumptions = [InClass(G, Graphs), InSet(C, EulerianCircuits(G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, EulerianCircuits(G)).conclude(assumptions = [InClass(G, Graphs), InSet(C, Circuits(Size(G), G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, EulerianCircuits(G)).derive_element_in_closed_walks(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(C, EulerianCircuits(G))]\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Cycles Membership and Related Methods"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Cycles(k, G))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Cycles(k, G)).deduce_in_bool(assumptions = [InClass(G, Graphs), InSet(k, Natural)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Cycles(k, G)).definition(assumptions = [InClass(G, Graphs), InSet(k, Natural), greater_eq(k, three)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Cycles(k, G)).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Cycles(k, G)).unfold(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(k, Natural), greater_eq(k, three), InSet(C, Cycles(k, G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit.logic.sets import Restriction\n",
+    "InSet(C, Cycles(k, G)).conclude(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(k, Natural), greater_eq(k, three),\n",
+    "                       InSet(C, SetOfAll(W, W,\n",
+    "                       conditions=[InSet(Restriction(W, Interval(zero, subtract(k, one))), Paths(subtract(k, one), G))],\n",
+    "                       domain=Circuits(k, G)))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Cycles(k, G)).derive_element_in_circuits(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(k, Natural),\n",
+    "                       greater_eq(k, three), InSet(C, Cycles(k, G))]\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Walks(k, G)).prove(assumptions = [InClass(G, Graphs), InSet(k, Natural),\n",
+    "                       greater_eq(k, three), InSet(C, Cycles(k, G))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Size(G).derive_size_in_natural(assumptions = [InClass(G, Graphs)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(C, Walks(Size(G), G)).prove(\n",
+    "        assumptions = [InClass(G, Graphs), InSet(Size(G), Natural), InSet(C, EulerianCircuits(G))]\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end demonstrations"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuit_partitions_into_cycles/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuit_partitions_into_cycles/thm_proof.ipynb
new file mode 100644
index 000000000000..60f184ce019c
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuit_partitions_into_cycles/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#circuit_partitions_into_cycles\">circuit_partitions_into_cycles</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving circuit_partitions_into_cycles"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuits_membership_def/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuits_membership_def/thm_proof.ipynb
new file mode 100644
index 000000000000..2725b76f44e6
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuits_membership_def/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#circuits_membership_def\">circuits_membership_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving circuits_membership_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuits_membership_folding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuits_membership_folding/thm_proof.ipynb
new file mode 100644
index 000000000000..02d9304aff0a
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuits_membership_folding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#circuits_membership_folding\">circuits_membership_folding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving circuits_membership_folding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuits_membership_is_bool/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuits_membership_is_bool/thm_proof.ipynb
new file mode 100644
index 000000000000..dd7bec343a7c
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuits_membership_is_bool/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#circuits_membership_is_bool\">circuits_membership_is_bool</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving circuits_membership_is_bool"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuits_membership_unfolding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuits_membership_unfolding/thm_proof.ipynb
new file mode 100644
index 000000000000..912c98347f24
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuits_membership_unfolding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#circuits_membership_unfolding\">circuits_membership_unfolding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving circuits_membership_unfolding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuits_within_closed_trails/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuits_within_closed_trails/thm_proof.ipynb
new file mode 100644
index 000000000000..bac75244bb8e
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/circuits_within_closed_trails/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#circuits_within_closed_trails\">circuits_within_closed_trails</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving circuits_within_closed_trails"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_def/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_def/thm_proof.ipynb
new file mode 100644
index 000000000000..4dff0356f014
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_def/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#closed_trails_membership_def\">closed_trails_membership_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving closed_trails_membership_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_folding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_folding/thm_proof.ipynb
new file mode 100644
index 000000000000..e1059f472c5e
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_folding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#closed_trails_membership_folding\">closed_trails_membership_folding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving closed_trails_membership_folding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_folding_components/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_folding_components/thm_proof.ipynb
new file mode 100644
index 000000000000..0052e96f6070
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_folding_components/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#closed_trails_membership_folding_components\">closed_trails_membership_folding_components</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving closed_trails_membership_folding_components"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_is_bool/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_is_bool/thm_proof.ipynb
new file mode 100644
index 000000000000..b3ae61176b49
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_is_bool/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#closed_trails_membership_is_bool\">closed_trails_membership_is_bool</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving closed_trails_membership_is_bool"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_unfolding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_unfolding/thm_proof.ipynb
new file mode 100644
index 000000000000..9668ceb55728
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_unfolding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#closed_trails_membership_unfolding\">closed_trails_membership_unfolding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving closed_trails_membership_unfolding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_unfolding_components/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_unfolding_components/thm_proof.ipynb
new file mode 100644
index 000000000000..6c7780c73d25
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_membership_unfolding_components/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#closed_trails_membership_unfolding_components\">closed_trails_membership_unfolding_components</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving closed_trails_membership_unfolding_components"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_within_closed_walks/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_within_closed_walks/thm_proof.ipynb
new file mode 100644
index 000000000000..8208c02eb954
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_trails_within_closed_walks/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#closed_trails_within_closed_walks\">closed_trails_within_closed_walks</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving closed_trails_within_closed_walks"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_walks_membership_def/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_walks_membership_def/thm_proof.ipynb
new file mode 100644
index 000000000000..989e6bbe2065
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_walks_membership_def/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#closed_walks_membership_def\">closed_walks_membership_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving closed_walks_membership_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_walks_membership_folding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_walks_membership_folding/thm_proof.ipynb
new file mode 100644
index 000000000000..9297b86f23f1
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_walks_membership_folding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#closed_walks_membership_folding\">closed_walks_membership_folding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving closed_walks_membership_folding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_walks_membership_is_bool/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_walks_membership_is_bool/thm_proof.ipynb
new file mode 100644
index 000000000000..c3e9f2bce0d1
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_walks_membership_is_bool/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#closed_walks_membership_is_bool\">closed_walks_membership_is_bool</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving closed_walks_membership_is_bool"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_walks_membership_unfolding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_walks_membership_unfolding/thm_proof.ipynb
new file mode 100644
index 000000000000..a3a6fd7c37f1
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_walks_membership_unfolding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#closed_walks_membership_unfolding\">closed_walks_membership_unfolding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving closed_walks_membership_unfolding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_walks_within_walks/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_walks_within_walks/thm_proof.ipynb
new file mode 100644
index 000000000000..a373021e68cd
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/closed_walks_within_walks/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#closed_walks_within_walks\">closed_walks_within_walks</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving closed_walks_within_walks"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/cycles_membership_def/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/cycles_membership_def/thm_proof.ipynb
new file mode 100644
index 000000000000..6e43be9a74f7
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/cycles_membership_def/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#cycles_membership_def\">cycles_membership_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving cycles_membership_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/cycles_membership_folding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/cycles_membership_folding/thm_proof.ipynb
new file mode 100644
index 000000000000..483faf8c1a98
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/cycles_membership_folding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#cycles_membership_folding\">cycles_membership_folding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving cycles_membership_folding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/cycles_membership_is_bool/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/cycles_membership_is_bool/thm_proof.ipynb
new file mode 100644
index 000000000000..1456e0eb57d2
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/cycles_membership_is_bool/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#cycles_membership_is_bool\">cycles_membership_is_bool</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving cycles_membership_is_bool"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/cycles_membership_unfolding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/cycles_membership_unfolding/thm_proof.ipynb
new file mode 100644
index 000000000000..6de35181d787
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/cycles_membership_unfolding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#cycles_membership_unfolding\">cycles_membership_unfolding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving cycles_membership_unfolding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/cycles_within_circuits/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/cycles_within_circuits/thm_proof.ipynb
new file mode 100644
index 000000000000..65f5da0e2b6b
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/cycles_within_circuits/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#cycles_within_circuits\">cycles_within_circuits</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving cycles_within_circuits"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/eulerian_circuits_membership_def/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/eulerian_circuits_membership_def/thm_proof.ipynb
new file mode 100644
index 000000000000..435ae089897c
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/eulerian_circuits_membership_def/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#eulerian_circuits_membership_def\">eulerian_circuits_membership_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving eulerian_circuits_membership_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/eulerian_circuits_membership_folding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/eulerian_circuits_membership_folding/thm_proof.ipynb
new file mode 100644
index 000000000000..e86518110458
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/eulerian_circuits_membership_folding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#eulerian_circuits_membership_folding\">eulerian_circuits_membership_folding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving eulerian_circuits_membership_folding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/eulerian_circuits_membership_is_bool/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/eulerian_circuits_membership_is_bool/thm_proof.ipynb
new file mode 100644
index 000000000000..80213492a031
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/eulerian_circuits_membership_is_bool/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#eulerian_circuits_membership_is_bool\">eulerian_circuits_membership_is_bool</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving eulerian_circuits_membership_is_bool"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/eulerian_circuits_membership_unfolding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/eulerian_circuits_membership_unfolding/thm_proof.ipynb
new file mode 100644
index 000000000000..43c14cf544c1
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/eulerian_circuits_membership_unfolding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#eulerian_circuits_membership_unfolding\">eulerian_circuits_membership_unfolding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving eulerian_circuits_membership_unfolding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/eulerian_circuits_within_closed_walks/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/eulerian_circuits_within_closed_walks/thm_proof.ipynb
new file mode 100644
index 000000000000..4f268fbb603d
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/eulerian_circuits_within_closed_walks/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#eulerian_circuits_within_closed_walks\">eulerian_circuits_within_closed_walks</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving eulerian_circuits_within_closed_walks"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_def/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_def/thm_proof.ipynb
new file mode 100644
index 000000000000..4f89f9bb9b07
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_def/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#paths_membership_def\">paths_membership_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving paths_membership_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_folding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_folding/thm_proof.ipynb
new file mode 100644
index 000000000000..aa97f27f9ae2
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_folding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#paths_membership_folding\">paths_membership_folding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving paths_membership_folding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_folding_components/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_folding_components/thm_proof.ipynb
new file mode 100644
index 000000000000..485fc4f37b43
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_folding_components/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#paths_membership_folding_components\">paths_membership_folding_components</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving paths_membership_folding_components"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_is_bool/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_is_bool/thm_proof.ipynb
new file mode 100644
index 000000000000..81dd8f5c7340
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_is_bool/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#paths_membership_is_bool\">paths_membership_is_bool</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving paths_membership_is_bool"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_unfolding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_unfolding/thm_proof.ipynb
new file mode 100644
index 000000000000..571ac57c2409
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_unfolding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#paths_membership_unfolding\">paths_membership_unfolding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving paths_membership_unfolding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_unfolding_components/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_unfolding_components/thm_proof.ipynb
new file mode 100644
index 000000000000..ba73c5d6dbb3
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_membership_unfolding_components/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#paths_membership_unfolding_components\">paths_membership_unfolding_components</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving paths_membership_unfolding_components"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_within_trails/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_within_trails/thm_proof.ipynb
new file mode 100644
index 000000000000..42c5b466d5aa
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/paths_within_trails/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#paths_within_trails\">paths_within_trails</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving paths_within_trails"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_def/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_def/thm_proof.ipynb
new file mode 100644
index 000000000000..7da8c3d9327c
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_def/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#trails_membership_def\">trails_membership_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving trails_membership_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_folding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_folding/thm_proof.ipynb
new file mode 100644
index 000000000000..26bc0eedcc20
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_folding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#trails_membership_folding\">trails_membership_folding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving trails_membership_folding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_folding_components/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_folding_components/thm_proof.ipynb
new file mode 100644
index 000000000000..ff8250a587d6
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_folding_components/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#trails_membership_folding_components\">trails_membership_folding_components</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving trails_membership_folding_components"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_is_bool/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_is_bool/thm_proof.ipynb
new file mode 100644
index 000000000000..6d32c3646aca
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_is_bool/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#trails_membership_is_bool\">trails_membership_is_bool</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving trails_membership_is_bool"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_unfolding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_unfolding/thm_proof.ipynb
new file mode 100644
index 000000000000..0425ae0ee59c
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_unfolding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#trails_membership_unfolding\">trails_membership_unfolding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving trails_membership_unfolding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_unfolding_components/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_unfolding_components/thm_proof.ipynb
new file mode 100644
index 000000000000..9e80a53288b4
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_membership_unfolding_components/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#trails_membership_unfolding_components\">trails_membership_unfolding_components</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving trails_membership_unfolding_components"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_within_walks/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_within_walks/thm_proof.ipynb
new file mode 100644
index 000000000000..f7b79b90359a
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/trails_within_walks/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#trails_within_walks\">trails_within_walks</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving trails_within_walks"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/walk_implies_path/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walk_implies_path/thm_proof.ipynb
new file mode 100644
index 000000000000..a6a181e40212
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walk_implies_path/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#walk_implies_path\">walk_implies_path</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving walk_implies_path"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/walk_length_from_membership/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walk_length_from_membership/thm_proof.ipynb
new file mode 100644
index 000000000000..f6ab938c5638
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walk_length_from_membership/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#walk_length_from_membership\">walk_length_from_membership</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving walk_length_from_membership"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_def/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_def/thm_proof.ipynb
new file mode 100644
index 000000000000..c2948433cf01
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_def/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#walks_membership_def\">walks_membership_def</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving walks_membership_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_folding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_folding/thm_proof.ipynb
new file mode 100644
index 000000000000..5046e5b6c9ff
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_folding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#walks_membership_folding\">walks_membership_folding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving walks_membership_folding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_folding_components/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_folding_components/thm_proof.ipynb
new file mode 100644
index 000000000000..6b1c36224bf5
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_folding_components/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#walks_membership_folding_components\">walks_membership_folding_components</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving walks_membership_folding_components"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_is_bool/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_is_bool/thm_proof.ipynb
new file mode 100644
index 000000000000..04ebad452e92
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_is_bool/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#walks_membership_is_bool\">walks_membership_is_bool</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving walks_membership_is_bool"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_unfolding/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_unfolding/thm_proof.ipynb
new file mode 100644
index 000000000000..5745cc1ef142
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_unfolding/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#walks_membership_unfolding\">walks_membership_unfolding</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving walks_membership_unfolding"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_unfolding_components/thm_proof.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_unfolding_components/thm_proof.ipynb
new file mode 100644
index 000000000000..c7cdf25a6807
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/proofs/walks_membership_unfolding_components/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">graphs</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">walks</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#walks_membership_unfolding_components\">walks_membership_unfolding_components</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving walks_membership_unfolding_components"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/theorems.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/theorems.ipynb
new file mode 100644
index 000000000000..e49d5ea2c5cc
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/theorems.ipynb
@@ -0,0 +1,968 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theorems (or conjectures) for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.graphs.walks</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the theorems of a theory:\n",
+    "%theorems_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "from proveit import i, j, k, n, u, v, C, G, P, S, T, W, Function\n",
+    "from proveit.logic import And, Equals, Exists, Forall, in_bool\n",
+    "from proveit.logic.sets import (Disjoint, Functions, Injections, InSet,\n",
+    "        Set, SetOfAll, SubsetEq, Union)\n",
+    "from proveit.numbers import (zero, one, three, Add, greater_eq,\n",
+    "        Interval, LessEq, Natural, NaturalPos, subtract)\n",
+    "from proveit.graphs import (AdjacentVertices, BeginningVertex, Circuits,\n",
+    "        ClosedTrails, ClosedWalks, Edges, EdgeSet, EdgeSequence, EndingVertex,\n",
+    "        EndVertices, Graphs, Paths, Trails, Vertices, WalkLength, Walks)\n",
+    "# some convenient common expressions:\n",
+    "from proveit.graphs.walks import W_1_to_n, e_1_to_n, EdgeSet_W_1_to_n, Cycles_e_1_to_n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin theorems"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### WalksMembership($k$, $G$)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "walks_membership_def = (\n",
+    "    Forall(G, Forall(k, Forall(W,\n",
+    "    Equals(\n",
+    "        InSet(W, Walks(k, G)),\n",
+    "        InSet(W,\n",
+    "        SetOfAll(S,\n",
+    "             S,\n",
+    "             conditions = [Forall(i, AdjacentVertices(Function(S, i), Function(S, Add(i, one)), G),\n",
+    "                                  domain = Interval(zero, subtract(k, one)))],\n",
+    "             domain = Functions(Interval(zero, k), Vertices(G))))\n",
+    "    )), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "walks_membership_unfolding = (\n",
+    "    Forall(G, Forall(k, Forall(W,\n",
+    "    InSet(W,\n",
+    "          SetOfAll(S, S,\n",
+    "              conditions = [Forall(i, AdjacentVertices(Function(S, i), Function(S, Add(i, one)), G),\n",
+    "                                  domain = Interval(zero, subtract(k, one)))],\n",
+    "          domain = Functions(Interval(zero, k), Vertices(G)))),\n",
+    "    conditions = [InSet(W, Walks(k, G))]), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "walks_membership_folding = (\n",
+    "    Forall(G, Forall(k, Forall(W,\n",
+    "    InSet(W, Walks(k, G)),\n",
+    "    conditions = [InSet(W,\n",
+    "          SetOfAll(S, S,\n",
+    "              conditions = [Forall(i, AdjacentVertices(Function(S, i), Function(S, Add(i, one)), G),\n",
+    "                                  domain = Interval(zero, subtract(k, one)))],\n",
+    "          domain = Functions(Interval(zero, k), Vertices(G))))]), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "walks_membership_unfolding_components = (\n",
+    "    Forall(G, Forall(k, Forall(W,\n",
+    "    And(InSet(W, Functions(Interval(zero, k), Vertices(G))),\n",
+    "        Forall(i, AdjacentVertices(Function(W, i), Function(W, Add(i, one)), G), domain = Interval(zero, subtract(k, one)))),\n",
+    "    conditions = [InSet(W, Walks(k, G))]), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "walks_membership_folding_components = (\n",
+    "    Forall(G, Forall(k, Forall(W,\n",
+    "    InSet(W, Walks(k, G)),\n",
+    "    conditions = [Forall(i, AdjacentVertices(Function(W, i), Function(W, Add(i, one)), G),\n",
+    "        domain = Interval(zero, subtract(k, one)))],\n",
+    "    domain = Functions(Interval(zero, k), Vertices(G))),\n",
+    "    domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "walk_length_from_membership = (\n",
+    "    Forall(G,\n",
+    "    Forall(k,\n",
+    "    Forall(W,\n",
+    "        Equals(WalkLength(W), k),\n",
+    "    domain = Walks(k, G)),\n",
+    "    domain = Natural),\n",
+    "    domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### ClosedWalksMembership($k$, $G$)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "closed_walks_membership_def = (\n",
+    "Forall(G, Forall(k, Forall(W,\n",
+    "Equals(\n",
+    "    InSet(W, ClosedWalks(k, G)),\n",
+    "    InSet(W,\n",
+    "    SetOfAll(S,\n",
+    "         S,\n",
+    "         conditions = [Equals(BeginningVertex(S), EndingVertex(S))],\n",
+    "         domain = Walks(k, G)))\n",
+    ")), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "closed_walks_membership_unfolding = (\n",
+    "Forall(G, Forall(k, Forall(W,\n",
+    "InSet(W,\n",
+    "      SetOfAll(S,\n",
+    "               S,\n",
+    "               conditions = [Equals(BeginningVertex(S), EndingVertex(S))],\n",
+    "               domain = Walks(k, G))),\n",
+    "domain = ClosedWalks(k, G)), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "closed_walks_membership_folding = (\n",
+    "Forall(G, Forall(k, Forall(W,\n",
+    "    InSet(W,ClosedWalks(k, G)),\n",
+    "    domain = SetOfAll(S, S,\n",
+    "                  conditions = [Equals(BeginningVertex(S), EndingVertex(S))],\n",
+    "                  domain = Walks(k, G))),\n",
+    "domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### TrailsMembership($k$, $G$)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "trails_membership_def = (\n",
+    "    Forall(G, Forall(k, Forall(T,\n",
+    "    Equals(\n",
+    "        InSet(T, Trails(k, G)),\n",
+    "        InSet(T,\n",
+    "        SetOfAll(W,\n",
+    "                 W,\n",
+    "                 conditions = [InSet(EdgeSequence(W), Injections(Interval(zero, subtract(k, one)), Edges(G)))],\n",
+    "                 domain = Walks(k, G) ))\n",
+    "    )), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "trails_membership_unfolding = (\n",
+    "    Forall(G, Forall(k, Forall(T,\n",
+    "    InSet(T,\n",
+    "        SetOfAll(W,\n",
+    "        W,\n",
+    "        conditions = [InSet(EdgeSequence(W), Injections(Interval(zero, subtract(k, one)), Edges(G)))],\n",
+    "        domain = Walks(k, G) )),\n",
+    "    domain = Trails(k, G)), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "trails_membership_folding = (\n",
+    "    Forall(G, Forall(k, Forall(T,\n",
+    "    InSet(T, Trails(k, G)),\n",
+    "    conditions = [],\n",
+    "    domain = SetOfAll(W,\n",
+    "        W,\n",
+    "        conditions = [InSet(EdgeSequence(W), Injections(Interval(zero, subtract(k, one)), Edges(G)))],\n",
+    "        domain = Walks(k, G) )), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "trails_membership_unfolding_components = (\n",
+    "    Forall(G, Forall(k, Forall(T,\n",
+    "    And(InSet(T, Walks(k, G)),\n",
+    "        InSet(EdgeSequence(T), Injections(Interval(zero, subtract(k, one)), Edges(G)))),\n",
+    "    domain = Trails(k, G)), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "trails_membership_folding_components = (\n",
+    "    Forall(G, Forall(k, Forall(W,\n",
+    "    InSet(W, Trails(k, G)),\n",
+    "    conditions = [InSet(EdgeSequence(W), Injections(Interval(zero, subtract(k, one)), Edges(G)))],\n",
+    "    domain = Walks(k, G)), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### ClosedTrailsMembership($k$, $G$)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "closed_trails_membership_def = (\n",
+    "    Forall(G, Forall(k, Forall(T,\n",
+    "    Equals(\n",
+    "        InSet(T, ClosedTrails(k, G)),\n",
+    "        InSet(T,\n",
+    "        SetOfAll(W,\n",
+    "                 W,\n",
+    "                 conditions = [InSet(EdgeSequence(W), Injections(Interval(zero, subtract(k, one)), Edges(G)))],\n",
+    "                 domain = ClosedWalks(k, G) ))\n",
+    "    )), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "closed_trails_membership_unfolding = (\n",
+    "    Forall(G, Forall(k, Forall(T,\n",
+    "    InSet(T,\n",
+    "        SetOfAll(W,\n",
+    "        W,\n",
+    "        conditions = [InSet(EdgeSequence(W), Injections(Interval(zero, subtract(k, one)), Edges(G)))],\n",
+    "        domain = ClosedWalks(k, G) )),\n",
+    "    domain = ClosedTrails(k, G)), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "closed_trails_membership_folding = (\n",
+    "    Forall(G, Forall(k, Forall(T,\n",
+    "    InSet(T, ClosedTrails(k, G)),\n",
+    "    conditions = [],\n",
+    "    domain = SetOfAll(W,\n",
+    "        W,\n",
+    "        conditions = [InSet(EdgeSequence(W), Injections(Interval(zero, subtract(k, one)), Edges(G)))],\n",
+    "        domain = ClosedWalks(k, G) )), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "closed_trails_membership_unfolding_components = (\n",
+    "    Forall(G, Forall(k, Forall(T,\n",
+    "    And(InSet(T, ClosedWalks(k, G)),\n",
+    "        InSet(EdgeSequence(T), Injections(Interval(zero, subtract(k, one)), Edges(G)))),\n",
+    "    domain = ClosedTrails(k, G)), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "closed_trails_membership_folding_components = (\n",
+    "    Forall(G, Forall(k, Forall(W,\n",
+    "    InSet(W, ClosedTrails(k, G)),\n",
+    "    conditions = [InSet(EdgeSequence(W), Injections(Interval(zero, subtract(k, one)), Edges(G)))],\n",
+    "    domain = ClosedWalks(k, G)), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### PathsMembership($k$, $G$)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "paths_membership_def = (\n",
+    "    Forall(G, Forall(k, Forall(P,\n",
+    "    Equals(\n",
+    "        InSet(P, Paths(k, G)),\n",
+    "        InSet(P,\n",
+    "        SetOfAll(W,\n",
+    "             W,\n",
+    "             conditions = [InSet(W, Injections(Interval(zero, k), Vertices(G)))],\n",
+    "             domain = Walks(k, G)))\n",
+    "    )), domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "paths_membership_unfolding = (\n",
+    "    Forall(G, Forall(k, Forall(P,\n",
+    "        InSet(P,\n",
+    "        SetOfAll(W,\n",
+    "             W,\n",
+    "             conditions = [InSet(W, Injections(Interval(zero, k), Vertices(G)))],\n",
+    "             domain = Walks(k, G))),\n",
+    "    conditions = [InSet(P, Paths(k, G))]),\n",
+    "    domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "paths_membership_folding = (\n",
+    "    Forall(G, Forall(k, Forall(P,\n",
+    "        InSet(P, Paths(k, G)),\n",
+    "        domain = SetOfAll(W, W,\n",
+    "             conditions = [InSet(W, Injections(Interval(zero, k), Vertices(G)))],\n",
+    "             domain = Walks(k, G))),\n",
+    "    domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "paths_membership_unfolding_components = (\n",
+    "    Forall(G, Forall(k, Forall(P,\n",
+    "        And(InSet(P, Walks(k, G)),\n",
+    "        InSet(P, Injections(Interval(zero, k), Vertices(G)))),\n",
+    "    conditions = [InSet(P, Paths(k, G))]),\n",
+    "    domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "paths_membership_folding_components = (\n",
+    "    Forall(G, Forall(k, Forall(W,\n",
+    "        InSet(W, Paths(k, G)),\n",
+    "    conditions = [InSet(W, Injections(Interval(zero, k), Vertices(G)))], domain = Walks(k, G)),\n",
+    "    domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### CircuitsMembership($k$, $G$)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "circuits_membership_def = (\n",
+    "    Forall(G, Forall(k, Forall(C,\n",
+    "    Equals(\n",
+    "        InSet(C, Circuits(k, G)),\n",
+    "        InSet(C, ClosedTrails(k, G))\n",
+    "    )), conditions = [greater_eq(k, three)], domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "circuits_membership_unfolding = (\n",
+    "    Forall(G, Forall(k, Forall(C,\n",
+    "        InSet(C, ClosedTrails(k, G)),\n",
+    "        domain = Circuits(k, G)), conditions = [greater_eq(k, three)], domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "circuits_membership_folding = (\n",
+    "    Forall(G, Forall(k, Forall(C,\n",
+    "        InSet(C, Circuits(k, G)),\n",
+    "        domain = ClosedTrails(k, G)),\n",
+    "    conditions = [greater_eq(k, three)], domain = Natural),\n",
+    "    domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### EulerianCircuitsMembership($G$)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit.graphs import EulerianCircuits, Size"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "eulerian_circuits_membership_def = (\n",
+    "    Forall(G, Forall(C,\n",
+    "    Equals(\n",
+    "        InSet(C, EulerianCircuits(G)),\n",
+    "        InSet(C, Circuits(Size(G), G))\n",
+    "    )), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "eulerian_circuits_membership_unfolding = (\n",
+    "    Forall(G, Forall(C,\n",
+    "    InSet(C, Circuits(Size(G), G)\n",
+    "    ), domain = EulerianCircuits(G)), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "eulerian_circuits_membership_folding = (\n",
+    "    Forall(G, Forall(C,\n",
+    "    InSet(C, EulerianCircuits(G)\n",
+    "    ), domain = Circuits(Size(G), G)), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### CycesMembership($k$, $G$)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit.logic.sets import Restriction\n",
+    "from proveit.graphs import Cycles"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "cycles_membership_def = (\n",
+    "    Forall(G, Forall(k, Forall(C,\n",
+    "    Equals(\n",
+    "        InSet(C, Cycles(k, G)),\n",
+    "        InSet(C, SetOfAll(W, W,\n",
+    "               conditions = [InSet(Restriction(W, Interval(zero, subtract(k, one))),\n",
+    "                                   Paths(subtract(k, one), G))],\n",
+    "               domain = Circuits(k, G)))\n",
+    "    )), conditions = [greater_eq(k, three)], domain = Natural), domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "cycles_membership_unfolding = (\n",
+    "    Forall(G, Forall(k, Forall(C,\n",
+    "    InSet(C, SetOfAll(W, W,\n",
+    "             conditions = [InSet(Restriction(W, Interval(zero, subtract(k, one))),\n",
+    "                                   Paths(subtract(k, one), G))],\n",
+    "             domain = Circuits(k, G))),\n",
+    "    domain = Cycles(k, G)),\n",
+    "    conditions = [greater_eq(k, three)],\n",
+    "    domain = Natural),\n",
+    "    domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "cycles_membership_folding = (\n",
+    "    Forall(G, Forall(k, Forall(C,\n",
+    "    InSet(C, Cycles(k, G)),\n",
+    "    domain = SetOfAll(W, W,\n",
+    "             conditions = [InSet(Restriction(W, Interval(zero, subtract(k, one))),\n",
+    "                                   Paths(subtract(k, one), G))],\n",
+    "             domain = Circuits(k, G))),\n",
+    "    conditions = [greater_eq(k, three)],\n",
+    "    domain = Natural),\n",
+    "    domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### General Theorems Involving Walks, Trails, Paths, Circuits, & Cycles"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "##### The existence of a $u$-$v$ walk implies the existence of a $u$-$v$ path:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "walk_implies_path = Forall(G,\n",
+    "Forall((u, v),\n",
+    "Forall((j, W),\n",
+    "Exists((k, P), Equals(EndVertices(P), Set(u, v)),\n",
+    "       conditions = [LessEq(k, j)], domains = [Natural, Paths(k, G)]),\n",
+    "conditions = [Equals(EndVertices(W), Set(u, v))],\n",
+    "domains = [Natural, Walks(j, G)]),\n",
+    "domain = Vertices(G)),\n",
+    "domain = Graphs)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "##### Every circuit can be partitioned into a collection of edge-disjoint cycles:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "circuit_partitions_into_cycles = (\n",
+    "Forall(G,\n",
+    "Forall(k,\n",
+    "Forall(C,\n",
+    "    Exists(n,\n",
+    "    Exists(e_1_to_n,\n",
+    "    Exists(W_1_to_n,\n",
+    "    And(Equals(EdgeSet(C), Union(*EdgeSet_W_1_to_n)), Disjoint(*EdgeSet_W_1_to_n)).with_wrapping_at(1),\n",
+    "    domains = [*Cycles_e_1_to_n]),\n",
+    "    domain  = NaturalPos),\n",
+    "    domain = NaturalPos),\n",
+    "domain = Circuits(k, G)),\n",
+    "domain = Natural),\n",
+    "domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Subset/Superset Relationships Among Various Walks"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "closed_walks_within_walks = (\n",
+    "    Forall(G,\n",
+    "    Forall(k,\n",
+    "        SubsetEq(ClosedWalks(k, G), Walks(k, G)),\n",
+    "    domain = Natural),\n",
+    "    domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "trails_within_walks = (\n",
+    "    Forall(G,\n",
+    "    Forall(k,\n",
+    "        SubsetEq(Trails(k, G), Walks(k, G)),\n",
+    "    domain = Natural),\n",
+    "    domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "closed_trails_within_closed_walks = (\n",
+    "    Forall(G,\n",
+    "    Forall(k,\n",
+    "        SubsetEq(ClosedTrails(k, G), ClosedWalks(k, G)),\n",
+    "    domain = Natural),\n",
+    "    domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "paths_within_trails = (\n",
+    "    Forall(G,\n",
+    "    Forall(k,\n",
+    "        SubsetEq(Paths(k, G), Trails(k, G)),\n",
+    "    domain = Natural),\n",
+    "    domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "circuits_within_closed_trails = (\n",
+    "Forall(G,\n",
+    "Forall(k,\n",
+    "    SubsetEq(Circuits(k, G), ClosedTrails(k, G)),\n",
+    "    conditions = [greater_eq(k, three)],\n",
+    "domain = Natural),\n",
+    "domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "eulerian_circuits_within_closed_walks = (\n",
+    "Forall(G,\n",
+    "    SubsetEq(EulerianCircuits(G), ClosedWalks(Size(G), G)),\n",
+    "domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "cycles_within_circuits = (\n",
+    "    Forall(G, Forall(k,\n",
+    "    SubsetEq(Cycles(k, G), Circuits(k, G)),\n",
+    "    conditions = [greater_eq(k, three)], domain = Natural),\n",
+    "    domain = Graphs)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Some standard membership boolean claims"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "walks_membership_is_bool = Forall(G,\n",
+    "Forall(k,\n",
+    "Forall(W, in_bool(InSet(W, Walks(k, G)))),\n",
+    "domain = Natural),\n",
+    "domain = Graphs\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "closed_walks_membership_is_bool = Forall(G,\n",
+    "Forall(k,\n",
+    "Forall(W, in_bool(InSet(W, ClosedWalks(k, G)))),\n",
+    "domain = Natural),\n",
+    "domain = Graphs\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "trails_membership_is_bool = Forall(G,\n",
+    "Forall(k,\n",
+    "Forall(T, in_bool(InSet(T, Trails(k, G)))),\n",
+    "domain = Natural),\n",
+    "domain = Graphs\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "closed_trails_membership_is_bool = Forall(G,\n",
+    "Forall(k,\n",
+    "Forall(T, in_bool(InSet(T, ClosedTrails(k, G)))),\n",
+    "domain = Natural),\n",
+    "domain = Graphs\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "paths_membership_is_bool = Forall(G,\n",
+    "Forall(k,\n",
+    "Forall(P, in_bool(InSet(P, Paths(k, G)))),\n",
+    "domain = Natural),\n",
+    "domain = Graphs\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "circuits_membership_is_bool = Forall(G,\n",
+    "Forall(k,\n",
+    "Forall(C, in_bool(InSet(C, Circuits(k, G)))),\n",
+    "domain = Natural),\n",
+    "domain = Graphs\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "eulerian_circuits_membership_is_bool = Forall(G,\n",
+    "Forall(C, in_bool(InSet(C, EulerianCircuits(G)))),\n",
+    "domain = Graphs\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "cycles_membership_is_bool = Forall(G, Forall(k, \n",
+    "Forall(C, in_bool(InSet(C, Cycles(k, G)))),\n",
+    "domain = Natural),\n",
+    "domain = Graphs\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end theorems"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/_theory_nbs_/theory.ipynb b/packages/proveit/graphs/walks/_theory_nbs_/theory.ipynb
new file mode 100644
index 000000000000..b37d9d15045d
--- /dev/null
+++ b/packages/proveit/graphs/walks/_theory_nbs_/theory.ipynb
@@ -0,0 +1,33 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theory of <a class=\"ProveItLink\" href=\"../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../_theory_nbs_/theory.ipynb\">graphs</a>.walks\n",
+    "========\n",
+    "\n",
+    "Walks within graphs, conceptualized as sequences of vertices (in a simple graph), including special walks called trails, paths, circuits, and cycles."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "%theory # toggles between interactive and static modes"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/graphs/walks/walks.py b/packages/proveit/graphs/walks/walks.py
new file mode 100644
index 000000000000..848293b6c9d8
--- /dev/null
+++ b/packages/proveit/graphs/walks/walks.py
@@ -0,0 +1,508 @@
+from proveit import equality_prover, Function, Literal
+from proveit.logic import ClassMembership
+from proveit.graphs import Graph,  Size
+
+class Walks(Function):
+    '''
+    Walks(k, G) represents the set of walks of length k in the simple
+    undirected graph G. A walk W in a simple undirected graph G is a
+    sequence (u = v0, v1, ..., v = vk) of vertices of G such that
+    consecutive vertices in the sequence are adjacent in G ---
+    in other words, every pair {v_{i}, v_{i+1}} of vertices
+    (for i in {0, 1, ..., k-1}) corresponds to an edge in G.
+    u and v are called the endvertices or endpoints of the walk, and
+    a walk from u to v is often referred to as a u-v walk.
+    The vertices v1, v2, ..., v_{k-1} are called internal vertices.
+    The number of edges in the walk W, denoted by ||W||, is the
+    length of the walk (in this example, ||W|| = k).
+    The number of vertices in the walk W, including multiplicities
+    of repeated vertices, is denoted |W|, analogous to the notation
+    for the order of a graph, |G|.
+    Ultimately, the vertex sequence characterizing a walk W is
+    conceptualized as a function, S: {0, 1, ..., k} -> Vertices(G),
+    and thus a walk is itself conceptualized as a special kind of
+    function.
+    Trails, paths, circuits, and cycles are all special cases of walks.
+    '''
+
+    # the literal operator of the Walks operation
+    _operator_ = Literal(string_format='Walks',
+                         latex_format=r'\textrm{Walks}',
+                         theory=__file__)
+
+    def __init__(self, k, G, *, styles=None):
+        '''
+        Represent Walks(k, G), the set of all walks of
+        length k in graph G.
+        '''
+        self.graph = G
+        self.length = k
+        Function.__init__(
+                self, Walks._operator_, (k, G), styles=styles)
+
+    def membership_object(self, element):
+        from .walks_membership import WalksMembership
+        return WalksMembership(element, self)
+
+#     def nonmembership_object(self, element):
+#         from .walks_membership import WalksNonmembership
+#         return WalksNonmembership(element, self)
+
+
+class ClosedWalks(Function):
+    '''
+    ClosedWalks(k, G) represents the set of closed walks of length k
+    in the simple undirected graph G. See the description in the Walks
+    class for details about walks more generally. A closed walk is a
+    walk in which the first and last vertex are the same. Closed walks
+    are important in part because ClosedWalks(k, G) is a superset for
+    Circuits(k, G) and Cycles(k, G).
+    '''
+
+    # the literal operator of the ClosedWalks operation
+    _operator_ = Literal(string_format='ClosedWalks',
+                         latex_format=r'\textrm{ClosedWalks}',
+                         theory=__file__)
+
+    def __init__(self, k, G, *, styles=None):
+        '''
+        Represent ClosedWalks(k, G), the set of all closed walks of
+        length k in graph G.
+        '''
+        self.graph = G
+        self.length = k
+        Function.__init__(
+                self, ClosedWalks._operator_, (k, G), styles=styles)
+
+    def membership_object(self, element):
+        from .walks_membership import ClosedWalksMembership
+        return ClosedWalksMembership(element, self)
+
+#     def nonmembership_object(self, element):
+#         from .walks_membership import WalksNonmembership
+#         return WalksNonmembership(element, self)
+
+
+class Trails(Function):
+    '''
+    Trails(k, G) represents the set of trails of length k in the
+    simple graph G. A trail is a walk in which no edge is repeated.
+    See the Walks() class above for a definition of walk.
+    A trail with endvertices u and v is often called a u-v trail.
+    '''
+
+    # the literal operator of the Trails operation
+    _operator_ = Literal(string_format='Trails',
+                         latex_format=r'\textrm{Trails}',
+                         theory=__file__)
+
+    def __init__(self, k, G, *, styles=None):
+        '''
+        Represent Trails(k, G), the set of all trails of
+        length k in graph G.
+        '''
+        self.graph = G
+        self.length = k
+        Function.__init__(
+                self, Trails._operator_, (k, G), styles=styles)
+
+    def membership_object(self, element):
+        from .walks_membership import TrailsMembership
+        return TrailsMembership(element, self)
+
+    # def nonmembership_object(self, element):
+    #     from .walks_membership import TrailsNonmembership
+    #     return TrailsNonmembership(element, self)
+
+
+class ClosedTrails(Function):
+    '''
+    ClosedTrails(k, G) represents the set of closed trails of length
+    k in the simple graph G. A trail is a walk in which no edge is
+    repeated; a _closed_ trail is a trail that begins and ends at the
+    same vertex, and can be conceptualized as a special case of a
+    closed walk.
+    '''
+
+    # the literal operator of the ClosedTrails operation
+    _operator_ = Literal(string_format='ClosedTrails',
+                         latex_format=r'\textrm{ClosedTrails}',
+                         theory=__file__)
+
+    def __init__(self, k, G, *, styles=None):
+        '''
+        Represent ClosedTrails(k, G), the set of all closed trails of
+        length k in graph G.
+        '''
+        self.graph = G
+        self.length = k
+        Function.__init__(
+                self, ClosedTrails._operator_, (k, G), styles=styles)
+
+    def membership_object(self, element):
+        from .walks_membership import ClosedTrailsMembership
+        return ClosedTrailsMembership(element, self)
+
+    # def nonmembership_object(self, element):
+    #     from .walks_membership import TrailsNonmembership
+    #     return TrailsNonmembership(element, self)
+
+
+class Paths(Function):
+    '''
+    Paths(k, G) represents the set of paths of length k in the simple
+    graph G. A path is a walk in which no vertex (and thus no edge)
+    is repeated. See the Walks() class above for a definition of walk.
+    A path with endvertices u and v is often called a u-v path.
+    '''
+
+    # the literal operator of the Paths operation
+    _operator_ = Literal(string_format='Paths',
+                         latex_format=r'\textrm{Paths}',
+                         theory=__file__)
+
+    def __init__(self, k, G, *, styles=None):
+        '''
+        Represent Paths(k, G), the set of all paths of
+        length k in graph G.
+        '''
+        self.graph = G
+        self.length = k
+        Function.__init__(
+                self, Paths._operator_, (k, G), styles=styles)
+
+    def membership_object(self, element):
+        from .walks_membership import PathsMembership
+        return PathsMembership(element, self)
+
+#     def nonmembership_object(self, element):
+#         from .walks_membership import PathsNonmembership
+#         return PathsNonmembership(element, self)
+
+
+class Circuits(Function):
+    '''
+    Circuits(k, G) represents the set of circuits of length k >= 3 in
+    the simple undirected graph G. A circuit in a graph G is a closed
+    trail of length 3 or more in G. Because a circuit is a closed
+    trail, a circuit begins and ends at the same vertex without
+    repeating any edges, and a circuit can be described by choosing
+    any of its vertices as the beginning (and ending) vertex. See the
+    description above in the Walks class for more details about walks.
+    '''
+
+    # the literal operator of the Circuits operation
+    _operator_ = Literal(string_format='Circuits',
+                         latex_format=r'\textrm{Circuits}',
+                         theory=__file__)
+
+    def __init__(self, k, G, *, styles=None):
+        '''
+        Represent Circuits(k, G), the set of all circuits of
+        length k in graph G.
+        '''
+        self.graph = G
+        self.length = k
+        Function.__init__(
+                self, Circuits._operator_, (k, G), styles=styles)
+
+    def membership_object(self, element):
+        from .walks_membership import CircuitsMembership
+        return CircuitsMembership(element, self)
+
+#     def nonmembership_object(self, element):
+#         from .walks_membership import CircuitsNonmembership
+#         return CircuitsNonmembership(element, self)
+
+
+class EulerianCircuits(Function):
+    '''
+    EulerianCircuits(G) represents the set of Eulerian circuits
+    of the simple undirected graph G. An Eulerian circuit in G is
+    a circuit in G that uses every edge of G. Since it uses every
+    edge, there is no length parameter k that appears in other
+    types of walks. Because it is a circuit, we expect an Eulerian
+    circuit to be non-trivial: it should consist of at least 3 edges.
+    The set of EulerianCircuits(G) could be an empty set.
+    '''
+
+    # the literal operator of the EulerianCircuits operation
+    _operator_ = Literal(string_format='EulerianCircuits',
+                         latex_format=r'\textrm{EulerianCircuits}',
+                         theory=__file__)
+
+    def __init__(self, G, *, styles=None):
+        '''
+        Represent EulerianCircuits(G), the set of all Eulerian
+        circuits in graph G.
+        '''
+        self.graph = G
+        self.length = Size(G)
+        Function.__init__(
+                self, EulerianCircuits._operator_, G, styles=styles)
+
+    def membership_object(self, element):
+        from .walks_membership import EulerianCircuitsMembership
+        return EulerianCircuitsMembership(element, self)
+
+#     def nonmembership_object(self, element):
+#         from .walks_membership import CircuitsNonmembership
+#         return CircuitsNonmembership(element, self)
+
+
+class Cycles(Function):
+    '''
+    Cycles(k, G) represents the set of cycles of length k in
+    the simple undirected graph G. A cycle in a graph G can be
+    thought of as a "closed path": a cycle is a circuit without 
+    repeating any vertices (except for the beginning and ending
+    vertices). A cycle of length k is often referred to as a k-cycle.
+    A 3-cycle is often simply referred to as a triangle. A cycle of
+    odd length is called an odd cycle; a cycle of even length is
+    called an even cycle. See the description above in the Walks
+    class for more details about walks.
+    '''
+
+    # the literal operator of the Cycles operation
+    _operator_ = Literal(string_format='Cycles',
+                         latex_format=r'\textrm{Cycles}',
+                         theory=__file__)
+
+    def __init__(self, k, G, *, styles=None):
+        '''
+        Represent Cycles(k, G), the set of all cycles of
+        length k (also called k-cycles) in graph G.
+        '''
+        self.graph = G
+        self.length = k
+        Function.__init__(
+                self, Cycles._operator_, (k, G), styles=styles)
+
+    def membership_object(self, element):
+        from .walks_membership import CyclesMembership
+        return CyclesMembership(element, self)
+
+#     def nonmembership_object(self, element):
+#         from .walks_membership import CyclesNonmembership
+#         return CyclesNonmembership(element, self)
+
+
+class WalkLength(Function):
+    '''
+    WalkLength(W), denoted ||W||, represents the length of walk W
+    (where W could be a walk or one of the special cases of a
+    trail, path, circuit, etc., all of which area also walks),
+    equivalent to the number of edges crossed during the walk
+    (including multiplicities). For example, given a walk
+    W in Walks(5, G) in graph G with vertex sequence
+        S = (a, b, c, b, d, e),
+    we have WalkLength(W) = ||W|| = 5. Notice that ||W|| is always
+    equal to len(S) - 1, where len(S) is the length of sequence S.
+    '''
+
+    # literal operator of the WalkLength operation.
+    _operator_ = Literal(string_format='Len', theory=__file__)
+
+    def __init__(self, W, *, styles=None):
+        '''
+        Represent WalkLength(W), the length of walk W, equivalent
+        to the number of edges traveled during walk W.
+        '''
+        self.walk = W
+        Function.__init__(
+                self, WalkLength._operator_, W, styles=styles)
+
+    def string(self, **kwargs):
+        return '||' + self.operand.string() + '||'
+
+    def latex(self, **kwargs):
+        return r'\left\|' + self.operand.latex() + r'\right\|'
+
+
+class ClosedWalk(Function):
+    '''
+    ClosedWalk(W) is a propositional function (or predicate)
+    claiming that walk W is closed (i.e., that the endvertices of
+    walk W are equal). Recall that trails, paths, circuits, and
+    cycles are all special cases of walks.
+    '''
+
+    # the literal operator of the ClosedWalk() operation
+    _operator_ = Literal(string_format='Closed',
+                         latex_format=r'\textrm{Closed}',
+                         theory=__file__)
+
+    def __init__(self, W, *, styles=None):
+        '''
+        Represent the propositional function ClosedWalk(G),
+        claiming walk W is closed.
+        '''
+        self.walk = W
+        Function.__init__(
+                self, ClosedWalk._operator_, W, styles=styles)
+
+class EdgeSequence(Function):
+    '''
+    Given a walk W in Walks(k, G) consisting of vertex sequence S in
+    graph G, EdgeSeq(W) represents the sequence of edges traveled
+    along the walk, which for a simple graph is completely
+    determined by the vertex sequence S. The number of edges in
+    EdgeSequence(W) is the length of the walk W, represented with
+    WalkLength(W).
+    '''
+
+    # the literal operator of the EdgeSequence operation
+    _operator_ = Literal(string_format='EdgeSeq',
+                         latex_format=r'\mathrm{EdgeSeq}',
+                         theory=__file__)
+
+    def __init__(self, W, *, styles=None):
+        '''
+        Given a walk W = Walk(S, G) in graph G, represent the
+        sequence of edges traveled during the walk.
+        '''
+        self.walk = W
+        Function.__init__(
+                self, EdgeSequence._operator_, W, styles=styles)
+
+
+class EdgeSet(Function):
+    '''
+    Given a walk W in Walks(k, G) consisting of vertex sequence S in
+    graph G, EdgeSet(W) represents the set (not the sequence) of edges
+    traveled along the walk, which for a simple graph is completely
+    determined by the vertex sequence S. The sequence of edges traveled
+    in the walk W, represented by EdgeSequence(W), will have the same
+    edges as EdgeSet(W), but EdgeSequence(W) might have repeated
+    edges.
+    '''
+
+    # the literal operator of the EdgeSet operation
+    _operator_ = Literal(string_format='EdgeSet',
+                         latex_format=r'\mathrm{EdgeSet}',
+                         theory=__file__)
+
+    def __init__(self, W, *, styles=None):
+        '''
+        Given a walk W = Walk(S, G) in graph G, represent the
+        set of edges traveled during the walk.
+        '''
+        self.walk = W
+        Function.__init__(
+                self, EdgeSet._operator_, W, styles=styles)
+
+
+class EndVertices(Function):
+    '''
+    EndVertices(W) represents the set of endvertices or endpoints of
+    the walk W (and recall that trails, paths, circuits, and cycles
+    are special cases of walks). For example, given a walk
+    W = Walk(S, G) in graph G with vertex sequence
+        S = (a, b, c, b, d, e),
+    we have EndVertices(W) = {a, e}. Notice that EndVertices(W) will
+    have a single element when W is a circuit or a cycle.
+    '''
+
+    # the literal operator of the EndVertices(W) operation
+    _operator_ = Literal(string_format='EndVertices',
+                         latex_format=r'\textrm{EndVertices}',
+                         theory=__file__)
+
+    def __init__(self, W, *, styles=None):
+        '''
+        Represent EndVertices(W), the set of endvertices of walk W.
+        '''
+        self.walk = W
+        Function.__init__(
+                self, EndVertices._operator_, W, styles=styles)
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = [EndVertices(W)], deduce and return
+        the equality:
+            [EndVertices(W)]
+             = {BeginningVertex(W) = EndingVertex(W)}
+        '''
+        from . import endvertices_def
+        from proveit import k, G, W
+        from proveit.graphs import WalkLength
+        _W_sub  = self.operand
+        _k_sub  = WalkLength(self.operand)
+        # _G_sub  = _W_sub.graph
+
+        return endvertices_def.instantiate(
+                {k: _k_sub, W: _W_sub, G:G },auto_simplify=False)
+
+
+class BeginningVertex(Function):
+    '''
+    BeginningVertex(W) represents the beginning vertex of walk W
+    (and recall that trails, paths, circuits, and cycles are all cases
+    of walks). Since a length-k walk W is actually a function:
+        W:[0,1,...,k] -> Vertices(G),
+    BeginningVertex(W) represents the value W(0).
+    '''
+
+    # the literal operator of the BeginningVertex(W) operation
+    _operator_ = Literal(string_format='BeginningVertex',
+                         latex_format=r'\textrm{BeginningVertex}',
+                         theory=__file__)
+
+    def __init__(self, W, *, styles=None):
+        '''
+        Represent BeginningVertex(W), the beginning vertex W(0) of
+        walk W in a graph G.
+        '''
+        self.walk = W
+        Function.__init__(
+                self, BeginningVertex._operator_, W, styles=styles)
+
+
+class EndingVertex(Function):
+    '''
+    EndingVertex(W) represents the ending vertex of walk W (and recall
+    that trails, paths, circuits, and cycles are all cases of walks).
+    Since a length-k walk W is actually a function:
+        W:[0,1,...,k] -> Vertices(G),
+    EndingVertex(W) represents the value W(k).
+    '''
+
+    # the literal operator of the EndingVertex(W) operation
+    _operator_ = Literal(string_format='EndingVertex',
+                         latex_format=r'\textrm{EndingVertex}',
+                         theory=__file__)
+
+    def __init__(self, W, *, styles=None):
+        '''
+        Represent EndingVertex(W), the ending vertex W(k) of length-k
+        walk W in graph G.
+        '''
+        self.walk = W
+        Function.__init__(
+                self, EndingVertex._operator_, W, styles=styles)
+
+
+class EulerianTrails(Function):
+    '''
+    EulerianTrails(G) represents the set of Eulerian trails in the
+    simple undirected graph G. An Eulerian trail in graph G is a u-v
+    walk that uses each and every edge of G exactly once, and such
+    an Eulerian trail will exist in G if and only if G is connected
+    with exactly two vertices of odd degree. The odd degree vertices
+    will then also be the u and v of the u-v walk.
+    '''
+
+    # the literal operator of the EulerianTrails operation
+    _operator_ = Literal(string_format='EulerianTrails',
+                         latex_format=r'\textrm{EulerianTrails}',
+                         theory=__file__)
+
+    def __init__(self, G, *, styles=None):
+        '''
+        Represent EulerianTrails(G), the set of all Eulerian trails
+        in graph G.
+        '''
+        self.graph = G
+        Function.__init__(
+                self, EulerianTrails._operator_, G, styles=styles)
diff --git a/packages/proveit/graphs/walks/walks_membership.py b/packages/proveit/graphs/walks/walks_membership.py
new file mode 100644
index 000000000000..af64412cd148
--- /dev/null
+++ b/packages/proveit/graphs/walks/walks_membership.py
@@ -0,0 +1,849 @@
+from proveit import (i, k, C, G, P, S, T, W, equality_prover, Function,
+        prover, relation_prover)
+from proveit.logic import (Equals, Forall, InSet, SetMembership,
+            SetNonmembership)
+from proveit.logic.sets import Functions, Injections, Restriction, SetOfAll
+from proveit.numbers import zero, one, Add, Interval, subtract
+from proveit.graphs import (AdjacentVertices, BeginningVertex,
+            Circuits, ClosedWalks, Edges, EdgeSequence, EndingVertex,
+            Graph, Paths, Size, Vertices, Walks)
+
+
+class WalksMembership(SetMembership):
+    '''
+    Defines methods that apply to membership in the set
+    of length-k walks in the simple graph G, denoted Walks(k, G).
+    See related membership classes further below for Trails and Paths.
+    '''
+
+    def __init__(self, element, domain):
+        SetMembership.__init__(self, element, domain)
+
+    def side_effects(self, judgment):
+        '''
+        Yield side-effects when proving 'W in Walks(k, G)' for
+        k in Natural and G in Graphs.
+        '''
+        yield self.element_length_derivation
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = [elem in Walks(k, G)], deduce and return:
+        [elem in Walks(G)]
+        = elem in {S | Adjacent(S(i), S(i+1))}
+        for {S in Functions([0,...,k], Vertices(G))}
+        '''
+        from . import walks_membership_def
+        element = self.element
+        _G_sub  = self.domain.graph
+        _k_sub  = self.domain.length
+        return walks_membership_def.instantiate(
+                {G: _G_sub, k: _k_sub, W: element },auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self = [elem in Walks(k, G)],
+        return the expression (NOT a judgment):
+        elem in {S | Adjacent(S(i), S(i+1))}
+        for {S in Functions([0,...,k], Vertices(G))}
+        '''
+        element = self.element
+        _G      = self.domain.graph
+        _k      = self.domain.length
+        return InSet(element,
+               SetOfAll(S, S,
+               conditions = [
+                   Forall(i,
+                   AdjacentVertices(
+                           Function(S, i), Function(S, Add(i, one)), _G),
+                   domain = Interval(zero, subtract(_k, one)))],
+               domain = Functions(Interval(zero, _k), Vertices(_G))))
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From self = [elem in Walks(k, G)], and knowing self,
+        derive and return:
+        elem in {S | Adjacent(S(i), S(i+1))}
+        for {S in Functions([0,...,k], Vertices(G))}.
+        '''
+        from . import walks_membership_unfolding
+        element = self.element
+        _G_sub  = self.domain.graph
+        _k_sub  = self.domain.length
+        return walks_membership_unfolding.instantiate(
+            {G: _G_sub, k: _k_sub, W: element}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on self = [W in Walks(k, G)], derive and return
+        self, knowing or assuming at least one of the following:
+        (1) W in Functions([0,...,k], Vertices(G)) AND
+            forall i in [0,..., k-1][AdjacentVertices(W(i), W(i+1), G)]
+        (2) elem in {S | Adjacent(S(i), S(i+1))}
+            for {S in Functions([0,...,k], Vertices(G))}
+        '''
+        _W = self.element
+        _G  = self.domain.graph
+        _k  = self.domain.length
+        functions_set = Functions(Interval(zero, _k), Vertices(_G))
+        adj_verts = Forall(
+            i, AdjacentVertices(Function(_W, i),
+                                Function(_W, Add(i, one)), _G),
+            domain = Interval(zero, subtract(_k, one)))
+        if (InSet(_W, functions_set).proven()
+            and adj_verts.proven()):
+            from . import walks_membership_folding_components
+            return walks_membership_folding_components.instantiate(
+                    {G: _G, k: _k, W: _W},
+                    auto_simplify=False)
+        
+        from . import walks_membership_folding
+        return walks_membership_folding.instantiate(
+            {G: _G, k: _k, W: _W}, auto_simplify=False)
+
+    @relation_prover
+    def deduce_in_bool(self, **defaults_config):
+        from . import walks_membership_is_bool
+        _G_sub = self.domain.graph
+        _k_sub = self.domain.length
+        _W_sub = self.element
+        return walks_membership_is_bool.instantiate(
+            {G:_G_sub, k:_k_sub, W:_W_sub}, auto_simplify=False)
+
+    @prover
+    def element_length_derivation(self, **defaults_config):
+        '''
+        Can't seem to make this work as an @equality_prover?
+        From self = (element in Walks(k, G)) derive and return that
+        WalkLength(element) = k.
+        '''
+        from . import walk_length_from_membership
+        _G_sub = self.domain.graph
+        _k_sub = self.domain.length
+        _W_sub = self.element
+        return walk_length_from_membership.instantiate(
+                {G: _G_sub, k: _k_sub, W: _W_sub}, auto_simplify=False)
+
+
+class ClosedWalksMembership(SetMembership):
+    '''
+    Defines methods that apply to membership in the set
+    of length-k closed walks in the simple graph G, denoted
+    ClosedWalks(k, G).
+    '''
+
+    def __init__(self, element, domain):
+        SetMembership.__init__(self, element, domain)
+
+    def side_effects(self, judgment):
+        '''
+        Yield side-effects when proving 'W in ClosedWalks(k, G)' for
+        k in Natural and G in Graphs.
+        '''
+        yield self.derive_element_in_walks
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = [elem in ClosedWalks(k, G)], deduce and return
+        the equality:
+            [elem in ClosedWalks(k, G)]
+             = elem in {S | BeginningVertex(S) = EndingVertex(S)}
+             for {S in Walks(k, G)}
+        '''
+        from . import closed_walks_membership_def
+        element = self.element
+        _G_sub  = self.domain.graph
+        _k_sub  = self.domain.length
+        return closed_walks_membership_def.instantiate(
+                {G: _G_sub, k: _k_sub, W: element },auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self = [elem in ClosedWalks(k, G)],
+        return the expression (NOT a judgment):
+            elem in {S | BeginningVertex(S) = EndingVertex(S)}
+            for {S in Walks(k, G)}
+        '''
+        element = self.element
+        _G      = self.domain.graph
+        _k      = self.domain.length
+        return InSet(element,
+               SetOfAll(S, S,
+               conditions = [Equals(BeginningVertex(S), EndingVertex(S))],
+               domain = Walks(_k, _G)))
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From self = [elem in ClosedWalks(k, G)], and knowing (or
+        (assuming) self, derive and return:
+             elem in {S | BeginningVertex(S) = EndingVertex(S)}
+             for {S in Walks(k, G)}.
+        '''
+        from . import closed_walks_membership_unfolding
+        element = self.element
+        _G_sub  = self.domain.graph
+        _k_sub  = self.domain.length
+        return closed_walks_membership_unfolding.instantiate(
+            {G: _G_sub, k: _k_sub, W: element}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on self = [W in ClosedWalks(k, G)], derive and return
+        self, knowing or assuming that:
+            W in {S | BeginningVertex(S) = EndingVertex(S)}
+            for {S in Walks(k, G)}
+        '''
+        _W = self.element
+        _G  = self.domain.graph
+        _k  = self.domain.length
+        from . import closed_walks_membership_folding
+        return closed_walks_membership_folding.instantiate(
+            {G: _G, k: _k, W: _W}, auto_simplify=False)
+
+    @relation_prover
+    def deduce_in_bool(self, **defaults_config):
+        from . import closed_walks_membership_is_bool
+        _G_sub = self.domain.graph
+        _k_sub = self.domain.length
+        _W_sub = self.element
+        return closed_walks_membership_is_bool.instantiate(
+            {G:_G_sub, k:_k_sub, W:_W_sub}, auto_simplify=False)
+
+    @prover
+    def derive_element_in_walks(self, **defaults_config):
+        from . import closed_walks_within_walks
+        _G      = self.domain.graph
+        _k      = self.domain.length
+        return (closed_walks_within_walks.instantiate(
+            {G:_G, k:_k}, auto_simplify=False)
+            .derive_superset_membership(self.element, auto_simplify=False))
+
+
+class TrailsMembership(SetMembership):
+    '''
+    Defines methods that apply to membership in the set
+    of length-k trails in the simple graph G, denoted Trails(k, G).
+    See related membership classes above and below for Walks and Paths.
+    '''
+
+    def __init__(self, element, domain):
+        SetMembership.__init__(self, element, domain)
+
+    def side_effects(self, judgment):
+        '''
+        Yield side-effects when proving 'W in Trails(k, G)' for
+        k in Natural and G in Graphs.
+        '''
+        yield self.derive_element_in_walks
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = [elem in Trails(k, G)], deduce and return:
+        [elem in Trails(k, G)]
+        = elem in
+        {W | EdgeSequence(W) in Injections([0, ..., k-1], Edges(G))}
+        for {W in Walks(k, G)}.
+        W being in Walks(k, G) takes care of the requirement that 
+        consecutive elements of the vertex sequence are adjacent in
+        the graph G.
+        '''
+        from . import trails_membership_def
+        element = self.element
+        _G_sub  = self.domain.graph
+        _k_sub  = self.domain.length
+        return trails_membership_def.instantiate(
+                {G: _G_sub, k: _k_sub, T: element },auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self = [elem in Trails(k, G)],
+        return the expression (NOT a judgment):
+        elem in {W | EdgeSequence(W) in
+                 Injections([0, ..., k-1], Edges(G))}
+        for {W in Walks(k, G)}
+        '''
+        element = self.element
+        _G      = self.domain.graph
+        _k      = self.domain.length
+        return InSet(element,
+               SetOfAll(W, W,
+               conditions = [
+                   InSet(EdgeSequence(W),
+                   Injections(Interval(zero, subtract(_k, one)), Edges(_G)))],
+               domain = Walks(_k, _G)))
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From self = [elem in Trails(k, G)], and knowing or assuming
+        self, derive and return:
+        elem in {W | EdgeSequence(W) in
+                 Injections([0, ..., k-1], Edges(G))}
+                 for {W in Walks(k, G)}.
+        '''
+        from . import trails_membership_unfolding
+        element = self.element
+        _G_sub  = self.domain.graph
+        _k_sub  = self.domain.length
+        return trails_membership_unfolding.instantiate(
+            {G: _G_sub, k: _k_sub, T: element}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on self = [W in Trails(k, G)], derive and return
+        self, knowing or assuming at least one of the following:
+        (1) W in Walks(k, G) AND
+            EdgeSeq(W) in Injections([0,...,k-1], Edges(G))
+        (2) W in {W | EdgeSequence(W) in
+                 Injections([0, ..., k-1], Edges(G))}
+                 for {W in Walks(k, G)}
+        '''
+        _W = self.element
+        _G  = self.domain.graph
+        _k  = self.domain.length
+        injections_set = (
+            Injections(Interval(zero, subtract(_k, one)), Edges(_G)))
+        if (InSet(EdgeSequence(_W), injections_set).proven()
+            and InSet(_W, Walks(_k, _G)).proven()):
+            from . import trails_membership_folding_components
+            return trails_membership_folding_components.instantiate(
+                    {G: _G, k: _k, W: _W},
+                    auto_simplify=False)
+        
+        from . import trails_membership_folding
+        return trails_membership_folding.instantiate(
+            {G: _G, k: _k, W: _W}, auto_simplify=False)
+
+    @relation_prover
+    def deduce_in_bool(self, **defaults_config):
+        from . import trails_membership_is_bool
+        _G_sub = self.domain.graph
+        _k_sub = self.domain.length
+        _T_sub = self.element
+        return trails_membership_is_bool.instantiate(
+            {G:_G_sub, k:_k_sub, T:_T_sub}, auto_simplify=False)
+
+    @prover
+    def derive_element_in_walks(self, **defaults_config):
+        from . import trails_within_walks
+        _G      = self.domain.graph
+        _k      = self.domain.length
+        return (trails_within_walks.instantiate(
+            {G:_G, k:_k}, auto_simplify=False)
+            .derive_superset_membership(self.element, auto_simplify=False))
+
+
+class ClosedTrailsMembership(SetMembership):
+    '''
+    Defines methods that apply to membership in the set of length-k
+    closed trails in the simple graph G, denoted ClosedTrails(k, G).
+    See related membership classes above and below for Trails, Walks,
+    etc.
+    '''
+
+    def __init__(self, element, domain):
+        SetMembership.__init__(self, element, domain)
+
+    def side_effects(self, judgment):
+        '''
+        Yield side-effects when proving 'W in ClosedTrails(k, G)' for
+        k in Natural and G in Graphs.
+        '''
+        yield self.derive_element_in_closed_walks
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = [elem in ClosedTrails(k, G)], deduce and return:
+        [elem in ClosedTrails(k, G)]
+        = elem in
+        {W | EdgeSequence(W) in Injections([0, ..., k-1], Edges(G))}
+        for {W in ClosedWalks(k, G)}.
+        W being in ClosedWalks(k, G) takes care of the requirement that 
+        consecutive elements of the vertex sequence are adjacent in
+        the graph G.
+        '''
+        from . import closed_trails_membership_def
+        element = self.element
+        _G_sub  = self.domain.graph
+        _k_sub  = self.domain.length
+        return closed_trails_membership_def.instantiate(
+                {G: _G_sub, k: _k_sub, T: element },auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self = [elem in ClosedTrails(k, G)],
+        return the expression (NOT a judgment):
+        elem in {W | EdgeSequence(W) in
+                 Injections([0, ..., k-1], Edges(G))}
+        for {W in ClosedWalks(k, G)}
+        '''
+        element = self.element
+        _G      = self.domain.graph
+        _k      = self.domain.length
+        return InSet(element,
+               SetOfAll(W, W,
+               conditions = [
+                   InSet(EdgeSequence(W),
+                   Injections(Interval(zero, subtract(_k, one)), Edges(_G)))],
+               domain = ClosedWalks(_k, _G)))
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From self = [elem in ClsoedTrails(k, G)], and knowing or
+        assuming self, derive and return:
+        elem in {W | EdgeSequence(W) in
+                 Injections([0, ..., k-1], Edges(G))}
+                 for {W in ClosedWalks(k, G)}.
+        '''
+        from . import closed_trails_membership_unfolding
+        element = self.element
+        _G_sub  = self.domain.graph
+        _k_sub  = self.domain.length
+        return closed_trails_membership_unfolding.instantiate(
+            {G: _G_sub, k: _k_sub, T: element}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on self = [W in ClosedTrails(k, G)], derive and return
+        self, knowing or assuming at least one of the following:
+        (1) W in ClosedWalks(k, G) AND
+            EdgeSeq(W) in Injections([0,...,k-1], Edges(G))
+        (2) W in {W | EdgeSequence(W) in
+                 Injections([0, ..., k-1], Edges(G))}
+                 for {W in ClosedWalks(k, G)}
+        '''
+        _W = self.element
+        _G  = self.domain.graph
+        _k  = self.domain.length
+        injections_set = (
+            Injections(Interval(zero, subtract(_k, one)), Edges(_G)))
+        if (InSet(EdgeSequence(_W), injections_set).proven()
+            and InSet(_W, ClosedWalks(_k, _G)).proven()):
+            from . import closed_trails_membership_folding_components
+            return (closed_trails_membership_folding_components
+                    .instantiate({G: _G, k: _k, W: _W},
+                    auto_simplify=False))
+        
+        from . import closed_trails_membership_folding
+        return closed_trails_membership_folding.instantiate(
+            {G: _G, k: _k, W: _W}, auto_simplify=False)
+
+    @relation_prover
+    def deduce_in_bool(self, **defaults_config):
+        from . import closed_trails_membership_is_bool
+        _G_sub = self.domain.graph
+        _k_sub = self.domain.length
+        _T_sub = self.element
+        return closed_trails_membership_is_bool.instantiate(
+            {G:_G_sub, k:_k_sub, T:_T_sub}, auto_simplify=False)
+
+    @prover
+    def derive_element_in_closed_walks(self, **defaults_config):
+        from . import closed_trails_within_closed_walks
+        _G      = self.domain.graph
+        _k      = self.domain.length
+        return (closed_trails_within_closed_walks.instantiate(
+            {G:_G, k:_k}, auto_simplify=False)
+            .derive_superset_membership(self.element, auto_simplify=False))
+
+
+class PathsMembership(SetMembership):
+    '''
+    Defines methods that apply to membership in the set of length-k
+    paths in the simple graph G, denoted Paths(k, G). A path in G is
+    a walk in which no vertex (and thus no edge) is repeated.
+    See related membership classes above for Walks and Trails.
+    '''
+
+    def __init__(self, element, domain):
+        SetMembership.__init__(self, element, domain)
+
+    def side_effects(self, judgment):
+        '''
+        Yield side-effects when proving 'W in Paths(k, G)' for
+        k in Natural and G in Graphs.
+        '''
+        yield self.derive_element_in_trails
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = [elem in Paths(k, G)], deduce and return:
+        [elem in Paths(k, G)]
+        = elem in {W | W in Injections([0, ..., k], Vertices(G))}
+                  for {W in Walks(k, G)}.
+        W being in Walks(k, G) takes care of the requirement that 
+        consecutive elements of the vertex sequence are adjacent in the
+        graph G.
+        '''
+        from . import paths_membership_def
+        element = self.element
+        _G_sub  = self.domain.graph
+        _k_sub  = self.domain.length
+        return paths_membership_def.instantiate(
+                {G: _G_sub, k: _k_sub, P: element },auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self = [elem in Paths(k, G)],
+        return the expression (NOT a judgment):
+        elem in {W | W in Injections([0, ..., k], Vertices(G))}
+                for {W in Walks(k, G)}.
+        '''
+        element = self.element
+        _G      = self.domain.graph
+        _k      = self.domain.length
+        return InSet(element,
+               SetOfAll(W, W,
+               conditions = [
+                   InSet(W,
+                   Injections(Interval(zero, _k), Vertices(_G)))],
+               domain = Walks(_k, _G)))
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From self = [elem in Paths(k, G)], and knowing or assuming
+        self, derive and return:
+        elem in {W | W in Injections([0, ..., k], Vertices(G))}
+                for {W in Walks(k, G)}.
+        '''
+        from . import paths_membership_unfolding
+        element = self.element
+        _G_sub  = self.domain.graph
+        _k_sub  = self.domain.length
+        return paths_membership_unfolding.instantiate(
+            {G: _G_sub, k: _k_sub, P: element}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on self = [P in Paths(k, G)], derive and return
+        self, knowing or assuming at least one of the following:
+        (1) P in Walks(k, G) AND
+            P in Injections([0,...,k], Vertices(G))
+        (2) P in {W | W in Injections([0, ..., k], Vertices(G))}
+                 for {W in Walks(k, G)}
+        '''
+        _W = self.element
+        _G  = self.domain.graph
+        _k  = self.domain.length
+        injections_set = Injections(Interval(zero, _k), Vertices(_G))
+        if (InSet(_W, injections_set).proven()
+            and InSet(_W, Walks(_k, _G)).proven()):
+            from . import paths_membership_folding_components
+            return paths_membership_folding_components.instantiate(
+                    {G: _G, k: _k, W: _W},
+                    auto_simplify=False)
+        from . import paths_membership_folding
+        return paths_membership_folding.instantiate(
+            {G: _G, k: _k, W: _W}, auto_simplify=False)
+
+    @relation_prover
+    def deduce_in_bool(self, **defaults_config):
+        from . import paths_membership_is_bool
+        _G_sub = self.domain.graph
+        _k_sub = self.domain.length
+        _P_sub = self.element
+        return paths_membership_is_bool.instantiate(
+            {G:_G_sub, k:_k_sub, P:_P_sub}, auto_simplify=False)
+
+    @prover
+    def derive_element_in_trails(self, **defaults_config):
+        from . import paths_within_trails
+        _G      = self.domain.graph
+        _k      = self.domain.length
+        return (paths_within_trails.instantiate(
+            {G:_G, k:_k}, auto_simplify=False)
+            .derive_superset_membership(self.element, auto_simplify=False))
+
+
+class CircuitsMembership(SetMembership):
+    '''
+    Defines methods that apply to membership in the set of length-k
+    circuits in the simple graph G, denoted Circuits(k, G). A circuit
+    in G is a closed trail -- i.e., a closed walk in which no edge is
+    repeated.
+    '''
+
+    def __init__(self, element, domain):
+        SetMembership.__init__(self, element, domain)
+
+    def side_effects(self, judgment):
+        '''
+        Yield side-effects when proving 'C in Circuits(k, G)' for
+        Natural k >= 3 and G in Graphs.
+        '''
+        yield self.derive_element_in_closed_trails
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = [elem in Circuits(k, G)], with k >= 3, deduce
+        and return:
+        [elem in Circuits(k, G)] = elem in ClosedWalks(k, G).
+        '''
+        from . import circuits_membership_def
+        element = self.element
+        _G_sub  = self.domain.graph
+        _k_sub  = self.domain.length
+        return circuits_membership_def.instantiate(
+                {G: _G_sub, k: _k_sub, C: element },auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self = [elem in Circuits(k, G)], return the expression
+        (NOT a judgment):
+                           elem in ClosedWalks(k, G).
+        '''
+        element = self.element
+        _G      = self.domain.graph
+        _k      = self.domain.length
+        return InSet(element, ClosedWalks(_k, _G))
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From self = [elem in Circuits(k, G)], and knowing or assuming
+        self and k >= 3, derive and return: elem ClosedWalks(k, G).
+        '''
+        from . import circuits_membership_unfolding
+        element = self.element
+        _G_sub  = self.domain.graph
+        _k_sub  = self.domain.length
+        return circuits_membership_unfolding.instantiate(
+            {G: _G_sub, k: _k_sub, C: element}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on self = [C in Circuits(k, G)], derive and return
+        self, knowing or assuming BOTH of the following:
+        (1) k >= 3
+        (2) C in ClosedTrails(k, G)
+        '''
+        _C = self.element
+        _G  = self.domain.graph
+        _k  = self.domain.length
+        from . import circuits_membership_folding
+        return circuits_membership_folding.instantiate(
+            {G: _G, k: _k, C: _C}, auto_simplify=False)
+
+    @relation_prover
+    def deduce_in_bool(self, **defaults_config):
+        from . import circuits_membership_is_bool
+        _G_sub = self.domain.graph
+        _k_sub = self.domain.length
+        _C_sub = self.element
+        return circuits_membership_is_bool.instantiate(
+            {G:_G_sub, k:_k_sub, C:_C_sub}, auto_simplify=False)
+
+    @prover
+    def derive_element_in_closed_trails(self, **defaults_config):
+        from . import circuits_within_closed_trails
+        _G      = self.domain.graph
+        _k      = self.domain.length
+        return (circuits_within_closed_trails.instantiate(
+            {G:_G, k:_k}, auto_simplify=False)
+            .derive_superset_membership(self.element, auto_simplify=False))
+
+
+class EulerianCircuitsMembership(SetMembership):
+    '''
+    Defines methods that apply to membership in the set of Eulerian
+    circuits in the simple graph G, denoted EulerianCircuits(G). An
+    Eulieran circuit in G is a circuit that uses every edge (and thus
+    the length of an Eulerian circuit in G is always ||G||, i.e., the
+    number of edges in G (also called the size of G).
+    '''
+
+    def __init__(self, element, domain):
+        SetMembership.__init__(self, element, domain)
+
+    def side_effects(self, judgment):
+        '''
+        Yield side-effects when proving 'C in Circuits(k, G)' for
+        Natural k >= 3 and G in Graphs.
+        '''
+        yield self.derive_element_in_closed_walks
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = [elem in EulerianCircuits(G)], deduce and return:
+        [elem in EulerianCircuits(G)] = [elem in Circuits(||G||, G)].
+        '''
+        from . import eulerian_circuits_membership_def
+        element = self.element
+        _G_sub  = self.domain.graph
+        return eulerian_circuits_membership_def.instantiate(
+                {G: _G_sub, C: element },auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self = [elem in EulerianCircuits(G)], return the
+        expression (NOT a judgment): elem in Circuits(||G||, G).
+        '''
+        element = self.element
+        _G      = self.domain.graph
+        return InSet(element, Circuits(Size(_G), _G))
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From self = [elem in EulerianCircuits(G)], and knowing or
+        assuming self, derive and return: elem Circuits(||G||, G).
+        '''
+        from . import eulerian_circuits_membership_unfolding
+        element = self.element
+        _G_sub  = self.domain.graph
+        return eulerian_circuits_membership_unfolding.instantiate(
+            {G: _G_sub, C: element}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        From self = [C in EulerianCircuits(G)], derive and return
+        self, knowing or assuming that: C in Circuits(||G||, G).
+        '''
+        _C = self.element
+        _G  = self.domain.graph
+        from . import eulerian_circuits_membership_folding
+        return eulerian_circuits_membership_folding.instantiate(
+            {G: _G, C: _C}, auto_simplify=False)
+
+    @relation_prover
+    def deduce_in_bool(self, **defaults_config):
+        from . import eulerian_circuits_membership_is_bool
+        _G_sub = self.domain.graph
+        _C_sub = self.element
+        return eulerian_circuits_membership_is_bool.instantiate(
+            {G:_G_sub, C:_C_sub}, auto_simplify=False)
+
+    @prover
+    def derive_element_in_closed_walks(self, **defaults_config):
+        from . import eulerian_circuits_within_closed_walks
+        _G      = self.domain.graph
+        return (eulerian_circuits_within_closed_walks.instantiate(
+            {G:_G}, auto_simplify=False)
+            .derive_superset_membership(self.element, auto_simplify=False))
+
+
+class CyclesMembership(SetMembership):
+    '''
+    Defines methods that apply to membership in the set of length-k
+    cycles in the simple graph G, denoted Cycles(k, G). A cycle in G
+    is circuit (i.e., a closed trail) in which no vertex is repeated.
+    '''
+
+    def __init__(self, element, domain):
+        SetMembership.__init__(self, element, domain)
+
+    def side_effects(self, judgment):
+        '''
+        Yield side-effects when proving 'C in Cycles(k, G)' for
+        Natural k >= 3 and G in Graphs.
+        '''
+        yield self.derive_element_in_circuits
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self = [elem in Cycles(k, G)], with k >= 3, deduce
+        and return:
+            [elem in Cycles(k, G)]
+                = elem in {W in Circuits(k, G) |
+                           W|_{0,1,...,k-1} in Paths(k-1, G)}.
+
+        That is, an elem in Cycles(k, G) is equal to an elem being in
+        the set of Circuits(k, G) whose restriction to domain
+        {0,1,...,k-1} gives a path of length (k-1) in G.
+        '''
+        from . import cycles_membership_def
+        element = self.element
+        _G_sub  = self.domain.graph
+        _k_sub  = self.domain.length
+        return cycles_membership_def.instantiate(
+                {G: _G_sub, k: _k_sub, C: element },auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self = [elem in Cycles(k, G)], return the expression
+        (NOT a judgment):
+            elem in {W in Circuits(k, G) |
+                     W|_{0,1,...,k-1} in Paths(k-1, G)}.
+        '''
+        element = self.element
+        _G      = self.domain.graph
+        _k      = self.domain.length
+        return (InSet(element,
+                SetOfAll(W, W,
+                conditions = [
+                    InSet(Restriction(W, Interval(zero, subtract(_k, one))),
+                          Paths(subtract(_k, one), _G))],
+                domain = Circuits(_k, _G))))
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From self = [elem in Cycles(k, G)], and knowing or assuming
+        self and k >= 3, derive and return:
+            elem in {W in Circuits(k, G) |
+                     W|_{0,1,...,k-1} in Paths(k-1, G)}.
+        '''
+        from . import cycles_membership_unfolding
+        element = self.element
+        _G_sub  = self.domain.graph
+        _k_sub  = self.domain.length
+        return cycles_membership_unfolding.instantiate(
+            {G: _G_sub, k: _k_sub, C: element}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on self = [C in Cycles(k, G)], derive and return
+        self, knowing or assuming BOTH of the following:
+        (1) k >= 3
+        (2) C in {W in Circuits(k, G) |
+                  W|_{0,1,...,k-1} in Paths(k-1, G)}
+        '''
+        _C = self.element
+        _G  = self.domain.graph
+        _k  = self.domain.length
+        from . import cycles_membership_folding
+        return cycles_membership_folding.instantiate(
+            {G: _G, k: _k, C: _C}, auto_simplify=False)
+
+    @relation_prover
+    def deduce_in_bool(self, **defaults_config):
+        from . import cycles_membership_is_bool
+        _G_sub = self.domain.graph
+        _k_sub = self.domain.length
+        _C_sub = self.element
+        return cycles_membership_is_bool.instantiate(
+            {G:_G_sub, k:_k_sub, C:_C_sub}, auto_simplify=False)
+
+    @prover
+    def derive_element_in_circuits(self, **defaults_config):
+        '''
+        Usually called as a side effect when proving C in Cycles(k, G):
+        derive C in Circuits(k, G).
+        '''
+        from . import cycles_within_circuits
+        _G      = self.domain.graph
+        _k      = self.domain.length
+        return (cycles_within_circuits.instantiate(
+            {G:_G, k:_k}, auto_simplify=False)
+            .derive_superset_membership(self.element, auto_simplify=False))
+
diff --git a/packages/proveit/logic/__init__.py b/packages/proveit/logic/__init__.py
index a881ed1b40eb..4b4924fa674c 100644
--- a/packages/proveit/logic/__init__.py
+++ b/packages/proveit/logic/__init__.py
@@ -24,7 +24,7 @@
     NotProperSubset, superset_eq, not_superset_eq, proper_superset,
     not_proper_superset)
 from .sets import (Union, UnionAll, Intersect, IntersectAll, Difference,
-                   SetOfAll, CartProd, CartExp,
+                   SetOfAll, CartProd, CartExp, KPowerSet,
                    PowerSet, Disjoint, Distinct, Card)
 from .sets import (Functions, Injections, Surjections, Bijections,
                    Image, InvImage)
diff --git a/packages/proveit/logic/classes/membership/_theory_nbs_/proofs/double_negated_membership/thm_proof.ipynb b/packages/proveit/logic/classes/membership/_theory_nbs_/proofs/double_negated_membership/thm_proof.ipynb
new file mode 100644
index 000000000000..1179e5e862b6
--- /dev/null
+++ b/packages/proveit/logic/classes/membership/_theory_nbs_/proofs/double_negated_membership/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">classes</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">membership</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#double_negated_membership\">double_negated_membership</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving double_negated_membership"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/classes/membership/_theory_nbs_/theorems.ipynb b/packages/proveit/logic/classes/membership/_theory_nbs_/theorems.ipynb
index 4df16c674158..3860d5f3cb82 100644
--- a/packages/proveit/logic/classes/membership/_theory_nbs_/theorems.ipynb
+++ b/packages/proveit/logic/classes/membership/_theory_nbs_/theorems.ipynb
@@ -63,6 +63,15 @@
     "    (x, C), NotInClass(x, C), conditions=[Not(InClass(x, C))])"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "double_negated_membership = Forall((x, C), Not(NotInClass(x, C)), conditions=[InClass(x, C)])"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
diff --git a/packages/proveit/logic/classes/membership/not_in_class.py b/packages/proveit/logic/classes/membership/not_in_class.py
index 7cd9f6a71d91..e534422d72ef 100644
--- a/packages/proveit/logic/classes/membership/not_in_class.py
+++ b/packages/proveit/logic/classes/membership/not_in_class.py
@@ -2,7 +2,7 @@
                      prover, relation_prover, equality_prover,
                      ProofFailure, UnsatisfiedPrerequisites)
 from proveit.relation import Relation
-from proveit import x, S
+from proveit import x, S, C
 from proveit.util import OrderedSet
 
 
@@ -27,9 +27,9 @@ class NotInClass(Relation):
     def __init__(self, element, domain, *, 
                  operator=None, styles=None):
         '''
-        Create an class nonmembership relation indicating that the
+        Create a class nonmembership relation indicating that the
         given 'element' is NOT in the given 'domain'.  If an 'operator'
-        is provide, use this as the operator instead of
+        is provided, use this as the operator instead of the
         NotInClass._operator_ (e.g., for NotInSet which derives from
         NotInClass but uses a distinct operator).
         '''
@@ -117,7 +117,7 @@ def deduce_in_bool(self, **defaults_config):
         '''
         from . import not_in_class_is_bool
         return not_in_class_is_bool.instantiate(
-                {x: self.element, S: self.domain})
+                {x: self.element, C: self.domain})
 
     @prover
     def unfold_not_in(self, **defaults_config):
@@ -128,7 +128,7 @@ def unfold_not_in(self, **defaults_config):
         # We include the auto_simplify=False to keep the membership
         # result inside the Not() from being reduced to False.
         return unfold_not_in_class.instantiate(
-            {x: self.element, S: self.domain}, auto_simplify=False)
+            {x: self.element, C: self.domain}, auto_simplify=False)
 
     def _build_canonical_form(self):
         '''
@@ -224,7 +224,7 @@ def conclude_negation(self, **defaults_config):
         '''
         from . import double_negated_membership
         return double_negated_membership.instantiate(
-                {x:self.element, S:self.domain})
+                {x:self.element, C:self.domain})
 
     @prover
     def conclude_as_folded(self, **defaults_config):
@@ -233,7 +233,7 @@ def conclude_as_folded(self, **defaults_config):
         '''
         from . import fold_not_in_class
         return fold_not_in_class.instantiate(
-            {x: self.element, S: self.domain})
+            {x: self.element, C: self.domain})
 
     @staticmethod
     def yield_known_nonmemberships(element, assumptions=USE_DEFAULTS):
diff --git a/packages/proveit/logic/sets/__init__.py b/packages/proveit/logic/sets/__init__.py
index 1229367f29cf..18189a528ab9 100644
--- a/packages/proveit/logic/sets/__init__.py
+++ b/packages/proveit/logic/sets/__init__.py
@@ -11,12 +11,13 @@
 from .intersection import Intersect, IntersectAll
 from .subtraction import Difference
 from .comprehension import SetOfAll
-from .power_set import PowerSet
+from .k_power_set import KPowerSet
+from .power_set import PowerSet, KElemSubsets
 from .cartesian_products import CartProd, CartExp
 from .disjointness import Disjoint, Distinct
 from .cardinality import Card
-from .functions import (Functions, Injections, Surjections, Bijections, 
-                        Image, InvImage)
+from .functions import (Functions, Injections, Restriction, Surjections,
+                        Bijections, Image, InvImage)
 
 def is_infinite_set(expr):
     if hasattr(expr, 'is_infinite_set'):
diff --git a/packages/proveit/logic/sets/_sub_theories_.txt b/packages/proveit/logic/sets/_sub_theories_.txt
index e660031b52e5..47ee5afca1e6 100644
--- a/packages/proveit/logic/sets/_sub_theories_.txt
+++ b/packages/proveit/logic/sets/_sub_theories_.txt
@@ -7,6 +7,7 @@ intersection
 subtraction
 comprehension
 power_set
+k_power_set
 cartesian_products
 disjointness
 cardinality
diff --git a/packages/proveit/logic/sets/_theory_nbs_/common.ipynb b/packages/proveit/logic/sets/_theory_nbs_/common.ipynb
index 5b79f873edc2..fe94d670e47f 100644
--- a/packages/proveit/logic/sets/_theory_nbs_/common.ipynb
+++ b/packages/proveit/logic/sets/_theory_nbs_/common.ipynb
@@ -21,7 +21,7 @@
     "from proveit import i, l, m, n, x, y, A, S, fy, Qy\n",
     "from proveit.core_expr_types import y_1_to_n, f__y_1_to_n, Q__y_1_to_n, S_1_to_n, R__y_1_to_n\n",
     "from proveit.logic import (And, Or, Equals, NotEquals, Set, InSet, NotInSet, SubsetEq,\n",
-    "                           SetOfAll, UnionAll, IntersectAll)\n",
+    "                           SetOfAll, UnionAll, IntersectAll, in_bool)\n",
     "from proveit.numbers import one\n",
     "from proveit.logic.sets.empty_set import EmptySetLiteral\n",
     "%begin common"
@@ -165,6 +165,15 @@
     "                                        conditions=[Q__y_1_to_n])"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "each_y_in_S_in_bool = ExprRange(i, in_bool(InSet(IndexedVar(y, i), IndexedVar(S, i))), one, n)"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
diff --git a/packages/proveit/logic/sets/comprehension/_theory_nbs_/demonstrations.ipynb b/packages/proveit/logic/sets/comprehension/_theory_nbs_/demonstrations.ipynb
index 232a531c3957..81fd7283a35f 100644
--- a/packages/proveit/logic/sets/comprehension/_theory_nbs_/demonstrations.ipynb
+++ b/packages/proveit/logic/sets/comprehension/_theory_nbs_/demonstrations.ipynb
@@ -94,6 +94,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
+    "# in cases of a single instance variable, we can use the instance_var attribute\n",
     "squares_of_unit_interval.instance_var"
    ]
   },
@@ -103,7 +104,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "# instance_vars is no longer an attribute, but all_instance_vars() will work\n",
+    "# but all_instance_vars() will work for single or multiple instance variables\n",
     "squares_of_unit_interval.all_instance_vars()"
    ]
   },
@@ -122,6 +123,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
+    "# conditions attribute and all_conditions() function return domains and explicit conditions\n",
     "squares_of_unit_interval.all_conditions()"
    ]
   },
@@ -185,24 +187,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "squares_of_unit_interval.all_domains()"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "len(squares_of_unit_interval.all_instance_vars())"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
+    "# to obtain the instance element\n",
     "squares_of_unit_interval.instance_element"
    ]
   },
diff --git a/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/comprehension_membership_is_bool/allowed_presumptions.txt b/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/comprehension_membership_is_bool/allowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/comprehension_membership_is_bool/disallowed_presumptions.txt b/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/comprehension_membership_is_bool/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/comprehension_membership_is_bool/thm_proof.ipynb b/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/comprehension_membership_is_bool/thm_proof.ipynb
new file mode 100644
index 000000000000..893d29b2a752
--- /dev/null
+++ b/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/comprehension_membership_is_bool/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">comprehension</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#comprehension_membership_is_bool\">comprehension_membership_is_bool</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving comprehension_membership_is_bool"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/comprehension_membership_is_bool_basic/thm_proof.ipynb b/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/comprehension_membership_is_bool_basic/thm_proof.ipynb
new file mode 100644
index 000000000000..7831eff79de7
--- /dev/null
+++ b/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/comprehension_membership_is_bool_basic/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">comprehension</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#comprehension_membership_is_bool_basic\">comprehension_membership_is_bool_basic</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving comprehension_membership_is_bool_basic"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/nonmembership_equiv/thm_proof.ipynb b/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/nonmembership_equiv/thm_proof.ipynb
new file mode 100644
index 000000000000..e0ccda559988
--- /dev/null
+++ b/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/nonmembership_equiv/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">comprehension</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#nonmembership_equiv\">nonmembership_equiv</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving nonmembership_equiv"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/nonmembership_equiv_basic/thm_proof.ipynb b/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/nonmembership_equiv_basic/thm_proof.ipynb
new file mode 100644
index 000000000000..5a81c9545f11
--- /dev/null
+++ b/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/nonmembership_equiv_basic/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">comprehension</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#nonmembership_equiv_basic\">nonmembership_equiv_basic</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving nonmembership_equiv_basic"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/nonmembership_fold/thm_proof.ipynb b/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/nonmembership_fold/thm_proof.ipynb
new file mode 100644
index 000000000000..7e1a6e94c354
--- /dev/null
+++ b/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/nonmembership_fold/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">comprehension</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#nonmembership_fold\">nonmembership_fold</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving nonmembership_fold"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/nonmembership_fold_basic/thm_proof.ipynb b/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/nonmembership_fold_basic/thm_proof.ipynb
new file mode 100644
index 000000000000..dbdc505d2480
--- /dev/null
+++ b/packages/proveit/logic/sets/comprehension/_theory_nbs_/proofs/nonmembership_fold_basic/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">comprehension</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#nonmembership_fold_basic\">nonmembership_fold_basic</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving nonmembership_fold_basic"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/comprehension/_theory_nbs_/theorems.ipynb b/packages/proveit/logic/sets/comprehension/_theory_nbs_/theorems.ipynb
index c5debd15ef42..1aae1aaf96cb 100644
--- a/packages/proveit/logic/sets/comprehension/_theory_nbs_/theorems.ipynb
+++ b/packages/proveit/logic/sets/comprehension/_theory_nbs_/theorems.ipynb
@@ -17,12 +17,12 @@
     "import proveit\n",
     "# Prepare this notebook for defining the theorems of a theory:\n",
     "%theorems_notebook # Keep this at the top following 'import proveit'.\n",
-    "from proveit.logic import And, Implies, Forall, Equals, InSet, SetOfAll, Exists, SubsetEq\n",
-    "from proveit import f, n, x, y, S, Q, R, fx, Qx, Qy, Rx\n",
+    "from proveit import f, i, n, x, y, S, Q, R, fx, Qx, Qy, Rx, ExprRange, Function, IndexedVar\n",
+    "from proveit.logic import And, Equals, Exists, Forall, Implies, in_bool, InSet, Not, NotEquals, NotInSet, Or, SetOfAll, SubsetEq\n",
     "from proveit.core_expr_types import (x_1_to_n, y_1_to_n, f__x_1_to_n, f__y_1_to_n, \n",
     "                                     Q__x_1_to_n, Q__y_1_to_n, R__x_1_to_n, R__y_1_to_n, S_1_to_n)\n",
-    "from proveit.logic.sets import basic_comprehension_y, general_comprehension_fyn\n",
-    "from proveit.numbers import NaturalPos"
+    "from proveit.logic.sets import basic_comprehension_y, each_y_in_S_in_bool, general_comprehension_fyn\n",
+    "from proveit.numbers import one, NaturalPos\n"
    ]
   },
   {
@@ -103,6 +103,21 @@
     "    )"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Forall(n,\n",
+    "    Forall((S_1_to_n, Q, f, x),\n",
+    "        InSet(x, general_comprehension_fyn),\n",
+    "        conditions = [Exists(y_1_to_n, Equals(x, f__y_1_to_n), domains=[S_1_to_n], conditions=[Q__y_1_to_n])]\n",
+    "    ),\n",
+    "    domain=NaturalPos\n",
+    "    )"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -257,6 +272,103 @@
     "        domain=S, conditions=[Qx]))"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "comprehension_membership_is_bool = (\n",
+    "    Forall(n,\n",
+    "    Forall((S_1_to_n, Q, f, x, ExprRange(i, IndexedVar(y, i), one, n)),\n",
+    "       in_bool(InSet(x, general_comprehension_fyn)),\n",
+    "       conditions = [each_y_in_S_in_bool]),\n",
+    "    domain=NaturalPos)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit import Function\n",
+    "from proveit.logic import in_bool\n",
+    "comprehension_membership_is_bool_basic = (\n",
+    "    Forall((S, Q, f, x, y),\n",
+    "       in_bool(InSet(x, SetOfAll(y, y, condition=Function(Q, y), domain=S ))),\n",
+    "       conditions = [in_bool(InSet(y, S))])\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Non-membership Theorems"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "nonmembership_equiv = (\n",
+    "    Forall(n, \n",
+    "    Forall((S_1_to_n, Q, f, x), \n",
+    "    Equals(NotInSet(x, general_comprehension_fyn),\n",
+    "           Forall(y_1_to_n, NotEquals(x, f__y_1_to_n), \n",
+    "    domains=[S_1_to_n], conditions=[Q__y_1_to_n])).with_wrap_before_operator()),\n",
+    "    domain=NaturalPos)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "nonmembership_equiv_basic = (\n",
+    "        Forall((S, Q, x),\n",
+    "               Equals(NotInSet(x, basic_comprehension_y),\n",
+    "                       Or(NotInSet(x, S), Not(Qx)))\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "nonmembership_fold = (\n",
+    "    Forall(n, \n",
+    "    Forall((S_1_to_n, Q, f, x), \n",
+    "          NotInSet(x, general_comprehension_fyn),\n",
+    "          conditions=[Forall(y_1_to_n, NotEquals(x, f__y_1_to_n), \n",
+    "                  domains=[S_1_to_n], conditions=[Q__y_1_to_n])]),\n",
+    "    domain=NaturalPos)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "nonmembership_fold_basic = (\n",
+    "        Forall((S, Q, x),\n",
+    "               NotInSet(x, basic_comprehension_y),\n",
+    "               conditions=[Or(NotInSet(x, S), Not(Qx))]\n",
+    "    )\n",
+    ")"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
diff --git a/packages/proveit/logic/sets/comprehension/set_of_all.py b/packages/proveit/logic/sets/comprehension/set_of_all.py
index f8ed875100a9..e3d7387e1870 100644
--- a/packages/proveit/logic/sets/comprehension/set_of_all.py
+++ b/packages/proveit/logic/sets/comprehension/set_of_all.py
@@ -1,6 +1,7 @@
 from proveit import (
         ExprTuple, Function, Literal, OperationOverInstances,
-        Lambda, composite_expression, relation_prover, defaults)
+        Lambda, composite_expression, relation_prover, defaults,
+        prover)
 from proveit import f, n, x, y, Q, R, S
 
 
@@ -15,8 +16,8 @@ def __init__(self, instance_param_or_params, instance_element,
                  conditions=None, styles=None, _lambda_map=None):
         '''
         Create an expression representing the set of all
-        instance_element for instance parameter(s) such that the conditions
-        are satisfied:
+        instance_element for instance parameter(s) such that the
+        conditions are satisfied:
         {instance_element | conditions}_{instance_param_or_params \in S}
         '''
         OperationOverInstances.__init__(
@@ -32,8 +33,16 @@ def __init__(self, instance_param_or_params, instance_element,
             if not hasattr(self, 'domains') or None in self.domains:
                 raise ValueError("SetOfAll requires domains")
         else:
-            assert False, ("Expecting either 'instance_param' or 'instance_params' "
-                           "to be set")
+            assert False, ("Expecting either 'instance_param' or "
+                           "'instance_params' to be set")
+
+    def membership_object(self, element):
+        from .set_of_all_membership import SetOfAllMembership
+        return SetOfAllMembership(element, self)
+
+    def nonmembership_object(self, element):
+        from .set_of_all_membership import SetOfAllNonmembership
+        return SetOfAllNonmembership(element, self)
 
     def _formatted(self, format_type, fence=False, **kwargs):
         from proveit import ExprRange
@@ -86,6 +95,22 @@ def _formatted(self, format_type, fence=False, **kwargs):
         out_str += '}'
         return out_str
 
+    # @equality_prover('shallow_simplified', 'shallow_simplify')
+    # def shallow_simplification(self, *, must_evaluate=False,
+    #                            **defaults_config):
+    #     '''
+    #     From this forall statement, evaluate it to TRUE or FALSE if
+    #     possible by calling the domain's forall_evaluation method
+
+    #     A possible fix --- play around with this and test 20250321
+    #     possibly look at Operation.shallow_simplification as model.
+    #     Consider a simplification() method??
+    #     '''
+    #     with defaults.temporary() as temp_defaults:
+    #         temp_defaults.preserved_exprs.add(self.condition)
+        
+    #         return OperationOverInstances.shallow_simplification(self)
+
     @relation_prover
     def deduce_superset_eq_relation(self, superset, **defaults_config):
         '''
@@ -130,7 +155,7 @@ def deduce_subset_eq_relation(self, subset, **defaults_config):
     # The below must be updated
     # Being updated gradually by wdc starting 12/21/2021
 
-    @relation_prover
+    @prover
     def unfold_membership(self, element, **defaults_config):
         '''
         From (x in {y | Q(y)})_{y in S}, derive and return
@@ -140,41 +165,69 @@ def unfold_membership(self, element, **defaults_config):
         From (x in {f(y) | ..Q(y)..})_{y in S}, derive and return
         exists_{y in S | ..Q(y)..} x = f(y).
         Also derive x in S, but this is not returned.
+        As an example, consider the set A defined by:
+
+            A = SetOfAll(i, ExprTuple(i, zero),
+                domain = Interval(zero, num(10)))
+
+        which gives a set of tuples like this:
+
+            {(0,0), (1,0), ... (10,0)}.
+
+        Then A.unfold_membership(x, assumptions = [InSet(x, A)])
+        yields the judgment:
+
+            x in A |- Exists_{i in {0, 1, ..., 10}} s.t. x = (i, 0).
+
+        This method might now be obsolete with the establishment of
+        the SetOfAllMemberhip class and related methods.
+
         '''
         from . import (unfold, unfold_basic_comprehension,
                        in_superset_if_in_comprehension)
         from proveit.logic import And
+        from proveit.numbers import num
+
         if len(self.explicit_conditions())==1:
             explicit_conditions = self.explicit_conditions()[0]
         else:
+            # which includes case of no explicit conditions!
             explicit_conditions = And(*self.explicit_conditions())
-        # why is the following line there before testing number of vars
-        _Q_op, _Q_op_sub = Function(Q, self.all_instance_vars()), explicit_conditions
+
+        _Q_op, _Q_op_sub = (
+                Function(Q, self.all_instance_vars()), explicit_conditions)
+
         if (len(self.all_instance_vars()) == 1 and
             self.instance_element == self.instance_var):
-            # simple case of {x | Q(x)}_{x in S};
-            # derive x in S side-effect
-            print("(1) SetOfAll.unfold_membership(): inside first if.")
-            print("_Q_op = ")
-            display(_Q_op)
-            print("_Q_op_sub = ")
-            display(_Q_op_sub)
+            # simple case of {y | Q(y)}_{y in S}
+            # or {y | Q1(y), ..., Qn(y)}_{y in S}: derive (but don't
+            # explicitly return) side-effect (x in S);
+            # we do this here because some cases below do not include
+            # the membership (x in S) in the returned result
             in_superset_if_in_comprehension.instantiate(
                     {S: self.domain, _Q_op: _Q_op_sub,
                      x: element, y: self.instance_var})
-            print("SetOfAll.unfold_membership(): end")
-            if len(self.explicit_conditions())==1:
-                _Q_op, _Q_op_sub = (
-                    Function(Q, self.all_instance_vars()), explicit_conditions)
-            #     return unfold_basic1_cond_comprehension.instantiate(
-            #             {S:self.domain, Q_op:Q_op_sub,
-            #              x:element, y:self.instance_vars[0]})
-            # else:
-            #     return unfold_basic_comprehension.instantiate({S:self.domain, Q_op:Q_op_sub, x:element}, {y:self.instance_vars[0]}, assumptions=assumptions)
-        # else:
-        #     f_op, f_sub = Function(f, self.instance_vars), self.instance_element
-        #     return unfold_comprehension.instantiate({S:self.domain,  Q_op:Q_op_sub, f_op:f_sub, x:element}, {y_multi:self.instance_vars}).derive_conclusion(assumptions)
-
+            _y_sub = self.all_instance_vars()[0]
+            return unfold_basic_comprehension.instantiate(
+                    {S:self.domain, _Q_op:_Q_op_sub, x:element, y:_y_sub})
+        else: 
+            # cases where we have:
+            # (1) multiple instance_vars,
+            # (2) and/or instance_element is not just an instance_var
+            # In fact, this doesn't yet work with multiple instance vars
+            _n_sub = num(len(self.all_instance_vars()))
+            _f_op, _f_sub = (
+                Function(f, self.all_instance_vars()), self.instance_element)
+            _y_sub = self.all_instance_vars()
+            if len(self.explicit_conditions())<=1:
+                should_auto_simplify = True
+                # will simplify vacuous And() and trivial And(Q(y))
+            else:
+                should_auto_simplify = False
+            return unfold.instantiate(
+                {n:_n_sub, S:self.all_domains(),  _Q_op:_Q_op_sub, _f_op:_f_sub,
+                x:element, y:_y_sub},
+                auto_simplify = should_auto_simplify).derive_consequent()
 
 
     """
diff --git a/packages/proveit/logic/sets/comprehension/set_of_all_membership.py b/packages/proveit/logic/sets/comprehension/set_of_all_membership.py
new file mode 100644
index 000000000000..e99cf3cc09c5
--- /dev/null
+++ b/packages/proveit/logic/sets/comprehension/set_of_all_membership.py
@@ -0,0 +1,648 @@
+from proveit import USE_DEFAULTS, equality_prover, prover, Function
+from proveit.logic import And, SetMembership, SetNonmembership
+from proveit.numbers import num
+from proveit import f, n, x, y, S, Q
+
+class SetOfAllMembership(SetMembership):
+    '''
+    Defines methods that apply to membership in a set comprehension,
+    i.e. membership in a SetOfAll construction such as x being in the
+    set of all squares of positive integers:
+
+        InSet(x, SetOfAll(i, Exp(i, two), conditions=[greater(i, zero)],
+                          domain=Integer))
+
+    '''
+
+    def __init__(self, element, domain):
+        SetMembership.__init__(self, element, domain)
+
+    def side_effects(self, judgment):
+        '''
+        Unfold the comprehension set membership as a side-effect.
+        '''
+        yield self.unfold # STILL TO BE ADDRESSED
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        Deduce and return an equality depending on the form of the
+        SetOfAll set comprehension serving as the domain.
+        (1) From [x in {y | Q(y)}_{y in S}], deduce and return
+            [x in {y|Q(y)}_{y in S}] = [(x in S) and Q(x)].
+        (2) From [x in {y | Q1(y), Q2(y),...,Qn(y)}_{y in S}], deduce
+            and return [x in {y | Q1(y), Q2(y),...,Qn(y)}_{y in S}] =
+            [(x in S) and (Q1(y) and Q2(y),..., and Qn(y))].
+        (3) From [x in {f(y) | ..Q(y)..}]_{y in S}], deduce and return
+            [x in {f(y) | ..Q(y)..}]_{y in S}] =
+                exists_{y in S | ..Q(y)..} [x = f(y)].
+        The process for producing the outcome is very similar to the
+        process used in the unfold() method defined further below.
+        '''
+
+        from . import comprehension_def, basic_comprehension
+
+        element = self.element
+
+        if len(self.domain.explicit_conditions())==1:
+            explicit_conditions = self.domain.explicit_conditions()[0]
+        else:
+            # which includes case of no explicit conditions!
+            explicit_conditions = And(*self.domain.explicit_conditions())
+
+        _Q_op, _Q_op_sub = (
+                Function(Q, self.domain.all_instance_vars()),
+                explicit_conditions)
+
+        if (len(self.domain.all_instance_vars()) == 1 and
+            self.domain.instance_element == self.domain.instance_var):
+            # simple case of [x in {y | Q(y)}_{y in S}]
+            # or [x in {y | Q1(y), ..., Qn(y)}_{y in S}]:
+            _S_sub = self.domain.all_domains()[0] # only 1 instance var
+            _y_sub = self.domain.all_instance_vars()
+            return basic_comprehension.instantiate(
+                    {S:_S_sub, _Q_op:_Q_op_sub, x:element, y:_y_sub},
+                    preserved_exprs = {self.domain})
+        else: 
+            # cases where we have:
+            # (1) multiple instance_vars,
+            # (2) and/or instance_element is not just an instance_var
+            # In fact, this doesn't yet work with multiple instance vars
+            _n_sub = num(len(self.domain.all_instance_vars()))
+            _f_op, _f_op_sub = (
+                    Function(f, self.domain.all_instance_vars()),
+                    self.domain.instance_element)
+            _S_sub = self.domain.all_domains()
+            _y_sub = self.domain.all_instance_vars()
+            # if len(self.domain.explicit_conditions())<=1:
+            #     should_auto_simplify = True
+            #     # will simplify vacuous And() and trivial And(Q(y))
+            # else:
+            #     should_auto_simplify = False
+            return comprehension_def.instantiate(
+                {n:_n_sub, S:_S_sub, _Q_op:_Q_op_sub, _f_op:_f_op_sub,
+                x:element, y:_y_sub})
+
+    def as_defined(self):
+        '''
+        TBA. The idea here is to return (but do not derive) the
+        'unfolded' version (i.e. the definition) of a
+        SetOfAllMembership expression, depending on the form of the
+        membership object:
+        (1) From (x in {y | Q(y)})_{y in S}, return [(x in S) and Q(x)].
+        (2) From (x in {y | ..Q(y)..})_{y in S},
+            return [(x in S) and ..Q(x)..].
+        (3) From (x in {f(y) | ..Q(y)..})_{y in S}, return
+            exists_{y in S | ..Q(y)..} x = f(y).
+        In the case of a simple Union() membership, the disjunction of
+        memberships is easily constructed and you might want such a
+        definition without having to instantiate the unfold theorem
+        and actually derive the definition result.
+        In the case of a SetOfAllMembership, the situation is more
+        complicated, with the explicit conditions potentially being
+        quite varied.
+
+        # From the original UnionMembership comments:
+        From self=[elem in (A U B U ...)], return
+        [(element in A) or (element in B) or ...].
+        '''
+
+        # From the original UnionMembership code:
+        # from proveit.logic import Or, InSet
+        # element = self.element
+        # return Or(*self.domain.operands.map_elements(
+        #         lambda subset : InSet(element, subset)))
+
+        raise NotImplementedError(
+                "SetOfAllMembership.as_defined() not yet implemented.")
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        Return an 'unfolded' version of a SetOfAllMembership expression,
+        depending on the form of the membership object:
+        (1) From (x in {y | Q(y)})_{y in S}, derive and return
+            [(x in S) and Q(x)].
+        (2) From (x in {y | ..Q(y)..})_{y in S}, derive and return
+            [(x in S) and ..Q(x)..].
+        (3) From (x in {f(y) | ..Q(y)..})_{y in S}, derive and return
+            exists_{y in S | ..Q(y)..} x = f(y) (and derive x in S,
+            but this is not returned).
+
+        Examples.
+
+        (1) Let A1 = SetOfAll(y, y, y >= 0, domain = Real). Then
+            (x in A1).unfold(assumptions=[x in A1]) derives and returns
+            {x in A1} |- (x in Real) AND (x >= 0)
+
+        (2) Let A2 = SetOfAll(y, y, conditions=[y > 0, y < 11],
+                     domain = Integer). Then
+            (x in A2).unfold(assumptions=[x in A2]) derives and returns
+            {x in A2} |- (x in Integer) AND ((x > 0) AND (x < 11))
+
+        (3) Let A3 = SetOfAll(i, ExprTuple(i, zero),
+                     domain = Interval(zero, num(10))),
+            which gives a set of tuples like this:
+                {(0,0), (1,0), ... (10,0)}
+            Then (x in A3).unfold(assumptions=[x in A3]) derives and
+            returns:
+            x in A3 |- Exists_{i in {0, 1, ..., 10}} s.t. x = (i, 0).
+
+        The method currently returns an error when the underlying
+        SetOfAll set uses multiple instance variables with multiple
+        explicit conditions. The reason for this is unclear but appears
+        to be the result of the instantiation of the unfold theorem
+        producing an antecedent that does not exactly match the
+        original membership object.
+        '''
+
+        from . import (unfold, unfold_basic_comprehension,
+                       in_superset_if_in_comprehension)
+
+        element = self.element
+
+        if len(self.domain.explicit_conditions())==1:
+            explicit_conditions = self.domain.explicit_conditions()[0]
+        else:
+            # which includes case of no explicit conditions!
+            explicit_conditions = And(*self.domain.explicit_conditions())
+        _Q_op, _Q_op_sub = (
+                Function(Q, self.domain.all_instance_vars()),
+                explicit_conditions)
+        if (len(self.domain.all_instance_vars()) == 1 and
+            self.domain.instance_element == self.domain.instance_var):
+            # simple case of [x in {y | Q(y)}_{y in S}]
+            # or [x in {y | Q1(y), ..., Qn(y)}_{y in S}]: derive
+            # (but don't) explicitly return) side-effect (x in S);
+            # We do this here because some cases below do not include
+            # the membership (x in S) in the returned result
+            in_superset_if_in_comprehension.instantiate(
+                    {S: self.domain.domain, _Q_op: _Q_op_sub,
+                     x: element, y: self.domain.instance_var})
+            if len(self.domain.explicit_conditions())==1:
+                _Q_op, _Q_op_sub = (
+                    Function(Q, self.domain.all_instance_vars()),
+                    explicit_conditions)
+                _y_sub = self.domain.all_instance_vars()
+                return unfold_basic_comprehension.instantiate(
+                        {S:self.domain.domain, _Q_op:_Q_op_sub,
+                         x:element, y:_y_sub})
+            else:
+                # multiple explicit conditions, so we use the And(*)
+                # construction from earlier for the _Q_op_sub
+                return unfold_basic_comprehension.instantiate(
+                        {S:self.domain.domain, _Q_op:_Q_op_sub, x:element,
+                         y:self.domain.all_instance_vars()[0]})
+        else: 
+            # cases where we have:
+            # (1) multiple instance_vars,
+            # (2) and/or instance_element is not just an instance_var
+            # In fact, this doesn't yet work with multiple instance vars
+            _n_sub = num(len(self.domain.all_instance_vars()))
+            _f_op, _f_op_sub = (
+                    Function(f, self.domain.all_instance_vars()),
+                    self.domain.instance_element)
+            if len(self.domain.explicit_conditions())<=1:
+                should_auto_simplify = True
+                # will simplify vacuous And() and trivial And(Q(y))
+            else:
+                should_auto_simplify = False
+            return unfold.instantiate(
+                {n:_n_sub, S:self.domain.all_domains(),
+                _Q_op:_Q_op_sub, _f_op:_f_op_sub,
+                x:element, y:self.domain.all_instance_vars()},
+                auto_simplify = should_auto_simplify).derive_consequent()
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        STILL UNDER ACTIVE TESTING as of 4/15/2025
+
+        Called on a SetOfAllMembership object such as
+        self = [elem in SetOfAll()], and depending on the form of self,
+        derive and return self, knowing or assuming the rhs of the
+        SetOfAll comprehension definition theorem (or the condition in
+        the SetOfAll fold theorem):
+        (1) From self = (x in {y}_{y in S}) and knowing or assuming
+            that (x in S), derive and return self.
+        (2) From self = (x in {y | Q(y)}_{y in S}) and knowing or
+            assuming that ((x in S) AND (Q(x))), derive and return self.
+        (3) From self = (x in {y | Q1(y), Q2(y),...,Qn(y)}_{y in S})
+            and knowing or assuming that ((x in S) AND (Q1(x) and ...
+            Qn(x))), derive and return self.
+        (4) From self = (x in {f(y1,...,yn) | Q(y1,...yn)}_{y1 in S1,
+            ...,yn in Sn}) and knowing or assuming that
+            Exists_{y1 in S1,...,yn in Sn | Q(y1,...,yn)} such that
+            [x = f(y1,...,yn)], derive and return self.
+
+        There are some cases for which the method works only when
+        called a second time on the same input. Not sure why.
+
+        This method does not work for cases in which the underlying
+        SetOfAll domain has both multiple instance variables
+        and multiple explicit conditions simultaneously.
+
+        Errors are arising especially with expressions that involve
+        multiple explicit conditions, producing instantiated forms
+        of the applied theorems whose component(s) do not quite
+        match the "natural" or expected assumption(s) one might have
+        or might construct. This turns out to be a thorny problem.
+        '''
+
+        from proveit import defaults
+        # print(f"\nEntering the SetOfAllMembership.conclude() method!\n")
+        from . import (fold, fold_basic_comprehension,
+                       in_superset_if_in_comprehension)
+        element = self.element
+
+        if len(self.domain.explicit_conditions())==1:
+            explicit_conditions = self.domain.explicit_conditions()[0]
+        else:
+            # which includes case of no explicit conditions!
+            explicit_conditions = And(*self.domain.explicit_conditions())
+
+        _Q_op, _Q_op_sub = (
+                Function(Q, self.domain.all_instance_vars()),
+                explicit_conditions)
+
+        if (len(self.domain.all_instance_vars()) == 1 and
+            self.domain.instance_element == self.domain.instance_var):
+            # print(f"Entering IF branch in SetOfAllMembership.conclude().")
+            # cases like [x in {y}_{y in S}]
+            #         or [x in {y | Q(y)}_{y in S}]
+            #         or [x in {y | Q1(y), Q2(y),...,Qn(y)}_{y in S}]
+            _S_sub = self.domain.domain
+            _y_sub = self.domain.all_instance_vars()[0]
+            # temp_inst = fold_basic_comprehension.instantiate(
+            #         {S:_S_sub, _Q_op:_Q_op_sub, x:element,
+            #          y:_y_sub}, preserve_expr = self.domain.condition,
+            #          auto_simplify = False)
+            # display(temp_inst.domain.condition)
+            # defaults._temp_inst = temp_inst
+            # defaults._temp_expr = self.expr 
+
+            # return fold_basic_comprehension.instantiate(
+            #         {S:_S_sub, _Q_op:_Q_op_sub, x:element, y:_y_sub},
+            #          preserved_exprs = {self.domain.condition},
+            #          auto_simplify = True)
+            # testing this alternative 20250412 for changes in Conditional.shallow_simplification
+            return fold_basic_comprehension.instantiate(
+                    {S:_S_sub, _Q_op:_Q_op_sub, x:element, y:_y_sub},
+                     preserved_exprs = {self.domain.condition},
+                     # preserve_expr = self.domain.operands[0].body.condition,
+                     # preserve_expr = self.domain.condition,
+                     auto_simplify = True)
+
+        else: 
+            # print(f"\nEntering ELSE branch in SetOfAllMembership.conclude().\n")
+            # cases where we have:
+            # (1) multiple instance_vars,
+            # (2) and/or instance_element is not just an instance_var
+            # In fact, this doesn't yet work with multiple instance vars
+            _n_sub = num(len(self.domain.all_instance_vars()))
+            _f_op, _f_sub = (
+                    Function(f, self.domain.all_instance_vars()),
+                    self.domain.instance_element)
+            # self.domain.condition == self.domain.domain
+            # if len(self.domain.explicit_conditions())<1: # changing from <= 1 to < 1 20250405
+            if (len(self.domain.conditions) == len(self.domain.explicit_domains())):
+                # want to be able to simplify empty And() created
+                # above for 'explicit_conditions'
+                should_auto_simplify = True
+                # print(f"should_auto_simplify = {should_auto_simplify}")
+                # print()
+            else:
+                # generally don't mess with anything
+                should_auto_simplify = False
+                # print(f"should_auto_simplify = {should_auto_simplify}")
+                # print()
+
+            # BEGIN TESTING
+            # temporarily keeping these try-except blocks;
+            # see further down for "testing" without the try-except
+            # print(f"---- Begin testing section in SetOfAllMembership.conclude() ----")
+            try:
+                temp_inst = fold.instantiate(
+                    {n:_n_sub, S:self.domain.all_domains(), 
+                    _Q_op:_Q_op_sub, _f_op:_f_sub,
+                    x:element, y:self.domain.all_instance_vars()},
+                    # preserved_exprs = {self.domain.condition},
+                    auto_simplify = should_auto_simplify)
+                print("'fold' instantiation gives: ")
+                display(temp_inst)
+                display(temp_inst.expr.antecedent)
+                print(f"assumption:")
+                display(defaults.assumptions[0])
+                print(f"assumption==antecedent = {defaults.assumptions[0]==temp_inst.expr.antecedent}")
+                print()
+                print(f"In the TRY block, the derive_consequent() returns: ")
+                return temp_inst.derive_consequent()
+
+            except Exception as the_exception:
+                print(f"Exception raised under should_auto_simplify = {should_auto_simplify}")
+                print(f"Switching to should_auto_simplify = {not should_auto_simplify}")
+                temp_inst = fold.instantiate(
+                    {n:_n_sub, S:self.domain.all_domains(), 
+                    _Q_op:_Q_op_sub, _f_op:_f_sub,
+                    x:element, y:self.domain.all_instance_vars()},
+                    # preserved_exprs = {self.domain.condition},
+                    auto_simplify = not should_auto_simplify)
+                print("'fold' instantiation gives: ")
+                display(temp_inst)
+                print()
+                print(f"The derive_consequent() returns: ")
+                return temp_inst.derive_consequent()
+
+
+            # temp_inst = fold.instantiate(
+            #         {n:_n_sub, S:self.domain.all_domains(), 
+            #         _Q_op:_Q_op_sub, _f_op:_f_sub,
+            #         x:element, y:self.domain.all_instance_vars()},
+            #         # preserved_exprs = {self.domain.condition},
+            #         auto_simplify = should_auto_simplify)
+
+            # Use the following when testing for when self.domain
+            # conditional's condition no longer being simplified inside
+            # the Conditional.shallow_simplification() method
+            # print()
+            # print(f"preserve_expr = {self.domain.operands[0].body.condition}")
+            # print()
+            # temp_inst = fold.instantiate(
+            #         {n:_n_sub, S:self.domain.all_domains(), 
+            #         _Q_op:_Q_op_sub, _f_op:_f_sub,
+            #         x:element, y:self.domain.all_instance_vars()},
+            #         preserve_expr = self.domain.operands[0].body.condition,
+            #         auto_simplify = True)
+            # print("'fold' instantiation gives: ")
+            # display(temp_inst)
+            # print()
+            # print(f"assumption==antecedent = {defaults.assumptions[0]==temp_inst.expr.antecedent}")
+            # print()
+            # print(f"And the derive_consequent() returns: ")
+            # return temp_inst.derive_consequent()
+
+            # END TESTING
+
+            # this block will not be reached while the testing
+            # block above are un-commented-out
+            return fold.instantiate(
+                {n:_n_sub, S:self.domain.all_domains(), 
+                _Q_op:_Q_op_sub, _f_op:_f_sub,
+                x:element, y:self.domain.all_instance_vars()},
+                # preserved_exprs = {self.domain.condition},
+                auto_simplify = should_auto_simplify).derive_consequent()
+
+    @prover
+    def deduce_in_bool(self, **defaults_config):
+        '''
+        Deduce that self = (x in SetOfAll()) is in Bool. Used as 
+        helper function elsewhere and not usually explicitly called
+        by the user.
+        '''
+        from . import (comprehension_membership_is_bool,
+                       comprehension_membership_is_bool_basic)
+
+        element = self.element
+
+        if len(self.domain.explicit_conditions())==1:
+            explicit_conditions = self.domain.explicit_conditions()[0]
+        else:
+            # which includes case of no explicit conditions!
+            explicit_conditions = And(*self.domain.explicit_conditions())
+        _Q_op, _Q_op_sub = (
+                Function(Q, self.domain.all_instance_vars()),
+                explicit_conditions)
+        if (len(self.domain.all_instance_vars()) == 1 and
+            self.domain.instance_element == self.domain.instance_var):
+            # simple case of [x in {y | Q(y)}_{y in S}]
+            # or [x in {y | Q1(y), ..., Qn(y)}_{y in S}]:
+            _S_sub = self.domain.domain # since only 1 instance var
+            _y_sub = self.domain.all_instance_vars()[0]
+            return comprehension_membership_is_bool_basic.instantiate(
+                    {S:self.domain.domain, _Q_op:_Q_op_sub,
+                         x:element, y:_y_sub})
+        else: 
+            # cases where we have:
+            # (1) multiple instance_vars,
+            # (2) and/or instance_element is not just an instance_var
+            # In fact, this doesn't yet work with multiple instance vars [?]
+            _n_sub = num(len(self.domain.all_instance_vars()))
+            _S_sub = self.domain.all_domains()
+            _f_op, _f_op_sub = (
+                    Function(f, self.domain.all_instance_vars()),
+                    self.domain.instance_element)
+            _y_op_sub  = self.domain.all_instance_vars()
+            if len(self.domain.explicit_conditions())<=1:
+                should_auto_simplify = True
+                # will simplify vacuous And() and trivial And(Q(y))
+            else:
+                should_auto_simplify = False
+            return comprehension_membership_is_bool.instantiate(
+                {n:_n_sub, S:_S_sub, _Q_op:_Q_op_sub, _f_op:_f_op_sub,
+                x:element, y:_y_op_sub},
+                auto_simplify = should_auto_simplify)
+
+
+class SetOfAllNonmembership(SetNonmembership):
+    '''
+    Defines methods that apply to non-membership in a set comprehension,
+    i.e. non-membership in a SetOfAll construction.
+    '''
+
+    def __init__(self, element, domain):
+        SetNonmembership.__init__(self, element, domain)
+
+    def side_effects(self, judgment):
+        '''
+        Currently no side-effects for comprehension non-membership.
+        '''
+        return
+        yield
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        Deduce and return an equality depending on the form of the
+        SetOfAll set comprehension serving as the (non)-domain.
+        (1) From [x NotIn {y | Q(y)}_{y in S}], deduce and return
+            [x NotIn {y|Q(y)}_{y in S}] = [(x NotIn S) OR Not(Q(x))].
+        (2) From [x NotIn {y | Q1(y), Q2(y),...,Qn(y)}_{y in S}],
+            deduce and return
+            [x NotIn {y | Q1(y), Q2(y),...,Qn(y)}_{y in S}] =
+            [(x NotIn S) OR Not(Q1(y)) and Q2(y),..., and Qn(y))].
+        (3) From [x NotIn {f(y) | Q1(y),...,Qn(y)}]_{y in S}],
+            deduce and return
+            [x NotIn {f(y) | Q1(y),...,Qn(y)}]_{y in S}] = 
+            Forall_{y in S | Q1(y),...,Qn(y)}[x NotEqual f(y)].
+        In cases (2) and (3), the Q1(y),...,Qn(y) are interpreted
+        as Q(y) = And(Q1(y),...,Qn(y)).
+
+        Note: this method raises an error when the SetOfAllNonmembership
+        expression has both multiple instance variables and multiple
+        explicit conditions simultaneously.
+        '''
+
+        from . import nonmembership_equiv, nonmembership_equiv_basic
+
+        element = self.element
+
+        if len(self.domain.explicit_conditions())==1:
+            explicit_conditions = self.domain.explicit_conditions()[0]
+        else:
+            # which includes case of no explicit conditions!
+            explicit_conditions = And(*self.domain.explicit_conditions())
+
+        _Q_op, _Q_op_sub = (
+                Function(Q, self.domain.all_instance_vars()),
+                explicit_conditions)
+
+        if (len(self.domain.all_instance_vars()) == 1 and
+            self.domain.instance_element == self.domain.instance_var):
+            # simple case of [x in {y | Q(y)}_{y in S}]
+            # or [x in {y | Q1(y), ..., Qn(y)}_{y in S}]:
+            _S_sub = self.domain.all_domains()[0] # only 1 instance var
+            _y_sub = self.domain.all_instance_vars()
+            return nonmembership_equiv_basic.instantiate(
+                    {S:_S_sub, _Q_op:_Q_op_sub, x:element, y:_y_sub})
+        else: 
+            # cases where we have:
+            # (1) multiple instance_vars,
+            # (2) and/or instance_element is not just an instance_var
+            # In fact, this doesn't yet work with multiple instance vars
+            _n_sub = num(len(self.domain.all_instance_vars()))
+            _f_op, _f_op_sub = (
+                    Function(f, self.domain.all_instance_vars()),
+                    self.domain.instance_element)
+            _S_sub = self.domain.all_domains()
+            _y_sub = self.domain.all_instance_vars()
+            # if len(self.domain.explicit_conditions())<=1:
+            #     should_auto_simplify = True
+            #     # will simplify vacuous And() and trivial And(Q(y))
+            # else:
+            #     should_auto_simplify = False
+            return nonmembership_equiv.instantiate(
+                {n:_n_sub, S:_S_sub, _Q_op:_Q_op_sub, _f_op:_f_op_sub,
+                x:element, y:_y_sub})
+
+    def as_defined(self):
+        '''
+        TBA. The idea here is to return (but do not derive) the
+        'unfolded' version (i.e. the definition) of a
+        SetOfAllNonmembership expression, depending on the form of the
+        membership object:
+        (1) From (x NotIn {y | Q(y)})_{y in S}, return
+            [(x NotIn S) OR Not(Q(x))].
+        (2) From (x NotIn {y | ..Q(y)..})_{y in S},
+            return [(x NotIn S) OR (..Not(Q(x))..)].
+        (3) From (x NotIn {f(y) | ..Q(y)..})_{y in S}, return
+            Forall_{y in S | ...Q(y)...}[x NotEqual f(y)].
+        In the case of a simple Union() membership, the disjunction of
+        memberships is easily constructed and you might want such a
+        definition without having to instantiate the unfold theorem
+        and actually derive the definition result.
+        In the case of a SetOfAllNonmembership, the situation is more
+        complicated, with the explicit conditions potentially being
+        quite varied.
+        # From original UnionNonmembership:
+        From self=[elem not in (A U B U ...)], return
+        [(element not in A) and (element not in B) and ...].
+        '''
+        # code from original UnionNonmembership:
+        # from proveit.logic import And, NotInSet
+        # element = self.element
+        # return And(*self.domain.operands.map_elements(
+        #         lambda subset : NotInSet(element, subset)))
+
+        raise NotImplementedError(
+                "SetOfAllNonmembership.as_defined() not yet implemented.")
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on a SetOfAllNonmembership object such as
+        self = [elem NotIn SetOfAll()], and depending on the form of
+        the SetOfAll set comprehension serving as the (non)-domain,
+        derive and return self, knowing or assuming the rhs of the
+        SetOfAllNonmembership definition theorem:
+        (1) From self = (x NotIn {y}_{y in S}) and knowing or
+            assuming [(x NotIn S)], derive and return self.
+        (2) From self = (x NotIn {y | Q(y)}_{y in S}) and knowing or
+            assuming that ((x NotIn S) OR (Not(Q(x))), derive and
+            return self.
+        (3) From self = (x in {y | Q1(y), Q2(y),...,Qn(y)}_{y in S})
+            and knowing or assuming that
+            [(x NotIn S) OR Not(Q1(y)) and Q2(y),..., and Qn(y))],
+            derive and return self.
+        (4) From self = (x NotIn {f(y1,...,yn) | Q(y1,...yn)}_{y1 in S1,
+            ...,yn in Sn}) and knowing or assuming that
+            Forall_{y1 in S1,...,yn in Sn | Q(y1,...,yn)}[x NotEqual
+            f(y1,...,yn)], derive and return self.
+
+        There are some cases for which the method works only when
+        called a second time on the same input. Not sure why.
+
+        This method does not work for cases in which the underlying
+        SetOfAll (non)-domain has both multiple instance variables
+        and multiple explicit conditions simultaneously.
+        '''
+
+        from . import (nonmembership_fold, nonmembership_fold_basic)
+        element = self.element
+
+        if len(self.domain.explicit_conditions())==1:
+            explicit_conditions = self.domain.explicit_conditions()[0]
+        else:
+            # which includes case of no explicit conditions!
+            explicit_conditions = And(*self.domain.explicit_conditions())
+        _Q_op, _Q_op_sub = (
+                Function(Q, self.domain.all_instance_vars()),
+                explicit_conditions)
+
+        if (len(self.domain.all_instance_vars()) == 1 and
+            self.domain.instance_element == self.domain.instance_var):
+            # cases like [x NotIn {y}_{y in S}]
+            #       or [x NotIn {y | Q(y)}_{y in S}]
+            #       or [x NotIn {y | Q1(y), Q2(y),...,Qn(y)}_{y in S}]
+            _S_sub = self.domain.domain
+            _y_sub = self.domain.all_instance_vars()[0]
+            return nonmembership_fold_basic.instantiate(
+                    {S:_S_sub, _Q_op:_Q_op_sub, x:element,
+                     y:_y_sub})
+        else: 
+            # cases where we have:
+            # (1) multiple instance_vars,
+            # (2) and/or instance_element is not just an instance_var
+            # In fact, this doesn't yet work with multiple instance vars
+            _n_sub = num(len(self.domain.all_instance_vars()))
+            _f_op, _f_sub = (
+                    Function(f, self.domain.all_instance_vars()),
+                    self.domain.instance_element)
+
+            if (len(self.domain.conditions) == len(self.domain.explicit_domains())):
+                # want to be able to simplify any empty And() created
+                # above for 'explicit_conditions'
+                should_auto_simplify = True
+            else:
+                # otherwise, generally don't mess with anything
+                should_auto_simplify = False
+
+            # See the try-except block below as current alternative
+            # return nonmembership_fold.instantiate(
+            #     {n:_n_sub, S:self.domain.all_domains(), 
+            #     _Q_op:_Q_op_sub, _f_op:_f_sub,
+            #     x:element, y:self.domain.all_instance_vars()},
+            #     auto_simplify = should_auto_simplify)
+
+            try:
+                return nonmembership_fold.instantiate(
+                        {n:_n_sub, S:self.domain.all_domains(), 
+                        _Q_op:_Q_op_sub, _f_op:_f_sub,
+                        x:element, y:self.domain.all_instance_vars()},
+                        auto_simplify = should_auto_simplify)
+
+            except Exception as the_exception:
+                return nonmembership_fold.instantiate(
+                        {n:_n_sub, S:self.domain.all_domains(), 
+                        _Q_op:_Q_op_sub, _f_op:_f_sub,
+                        x:element, y:self.domain.all_instance_vars()},
+                        auto_simplify = not should_auto_simplify)
diff --git a/packages/proveit/logic/sets/functions/__init__.py b/packages/proveit/logic/sets/functions/__init__.py
index b6ceda0a2202..8ff4448a3652 100644
--- a/packages/proveit/logic/sets/functions/__init__.py
+++ b/packages/proveit/logic/sets/functions/__init__.py
@@ -1,4 +1,5 @@
 from .functions import Functions
+from .restriction import Restriction 
 from .images import Image, InvImage
 from .injections import Injections
 from .surjections import Surjections
diff --git a/packages/proveit/logic/sets/functions/_theory_nbs_/axioms.ipynb b/packages/proveit/logic/sets/functions/_theory_nbs_/axioms.ipynb
index 6bbc9385e62b..7b25c1c1f575 100644
--- a/packages/proveit/logic/sets/functions/_theory_nbs_/axioms.ipynb
+++ b/packages/proveit/logic/sets/functions/_theory_nbs_/axioms.ipynb
@@ -18,9 +18,10 @@
     "# Prepare this notebook for defining the axioms of a theory:\n",
     "%axioms_notebook # Keep this at the top following 'import proveit'.\n",
     "\n",
-    "from proveit import f, x, A, B, fx\n",
-    "from proveit.logic import Forall, Equals, InSet\n",
-    "from proveit.logic.sets import Functions"
+    "from proveit import f, x, A, B, R, fx, Conditional, ConditionalSet, Function\n",
+    "from proveit.logic import And, Equals, FALSE, Forall, InSet, NotInSet\n",
+    "from proveit.logic.sets import Functions, Restriction, SubsetEq\n",
+    "from proveit.logic.sets.functions import undefined"
    ]
   },
   {
@@ -36,7 +37,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "A function from a domain $A$ to codomain $B$ must map any element in $A$ to an element in $B$."
+    "Specifying a function $f:A\\rightarrow B$ with domain $A$ and codomain $B$ means two things: (1) for all $x \\in A$ we have $f(x) \\in B$; and (2) if $x \\notin A$, then $f(x)$ is undefined (otherwise we should re-define the domain $A$)."
    ]
   },
   {
@@ -47,7 +48,35 @@
    "source": [
     "functions_def = Forall(\n",
     "    (f, A, B), Equals(InSet(f, Functions(A, B)),\n",
-    "                      Forall(x, InSet(fx, B), domain=A)))"
+    "                      And(Forall(x, InSet(fx, B), domain=A),\n",
+    "    Forall(x, Equals(fx, undefined), conditions = [NotInSet(x, A)]))))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The restriction of a function $f:A \\rightarrow B$ to some new domain $R \\subseteq A$ creates a new function $f\\vert_{R}:R \\rightarrow B$ whose outputs agree with the original function on the restricted domain:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "restriction_def = (\n",
+    "    Forall((A, B, R),\n",
+    "    Forall(f,\n",
+    "    Forall(x,\n",
+    "           Equals(\n",
+    "           Function(Restriction(f, R), x),\n",
+    "           ConditionalSet(Conditional(fx, InSet(x, R)), Conditional(undefined, NotInSet(x, R)))\n",
+    "           )),\n",
+    "    domain = Functions(A, B)),\n",
+    "    conditions = [SubsetEq(R, A)]\n",
+    "    )\n",
+    ")"
    ]
   },
   {
diff --git a/packages/proveit/logic/sets/functions/_theory_nbs_/common.ipynb b/packages/proveit/logic/sets/functions/_theory_nbs_/common.ipynb
index 8058fa985890..50981aceb5a5 100644
--- a/packages/proveit/logic/sets/functions/_theory_nbs_/common.ipynb
+++ b/packages/proveit/logic/sets/functions/_theory_nbs_/common.ipynb
@@ -18,7 +18,7 @@
     "# Prepare this notebook for defining the common expressions of a theory:\n",
     "%common_expressions_notebook # Keep this at the top following 'import proveit'.\n",
     "\n",
-    "# from ... import ..."
+    "from proveit import Literal"
    ]
   },
   {
@@ -30,12 +30,21 @@
     "%begin common"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "An undefined “UNDEF” literal, for use in defining function values for inputs outside the given domain of the function."
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
-   "source": []
+   "source": [
+    "undefined = Literal('UNDEF', r'\\textrm{UNDEF}')"
+   ]
   },
   {
    "cell_type": "code",
diff --git a/packages/proveit/logic/sets/functions/_theory_nbs_/demonstrations.ipynb b/packages/proveit/logic/sets/functions/_theory_nbs_/demonstrations.ipynb
index a813f7cdbdf5..afa48194b5e9 100644
--- a/packages/proveit/logic/sets/functions/_theory_nbs_/demonstrations.ipynb
+++ b/packages/proveit/logic/sets/functions/_theory_nbs_/demonstrations.ipynb
@@ -15,15 +15,119 @@
    "outputs": [],
    "source": [
     "import proveit\n",
+    "from proveit import f, x, A, B, C, R, fx\n",
+    "from proveit.logic import Forall, Functions, InSet\n",
+    "from proveit.logic.sets import Restriction\n",
     "%begin demonstrations"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### $\\texttt{Functions}(A, B)$, the set of all functions $f:A \\rightarrow B$ with domain $A$ and codomain $B$"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(f, Functions(A, B))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(f, Functions(A, B)).definition()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(f, Functions(A, B)).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(f, Functions(A, B)).conclude(\n",
+    "        assumptions = [Forall(x, InSet(fx, B), domain = A)]\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(f, Functions(A, B)).unfold(\n",
+    "        assumptions = [InSet(f, Functions(A, B))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### $f\\vert_{R}$, the restriction of $f:A \\rightarrow B$ to new domain $R \\subseteq A$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Restriction(f, R)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit.logic.sets.functions import restriction_def\n",
+    "restriction_def"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
-   "source": []
+   "source": [
+    "from proveit.logic import SubsetEq\n",
+    "restriction_def_inst = restriction_def.instantiate({A:A, B:B, R:R, f:f},\n",
+    "        assumptions = [SubsetEq(R, A), InSet(f, Functions(A, B))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "And notice the `known_memberships` dictionary, which might be useful in constructing some Restriction() methods:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet.known_memberships[f]"
+   ]
   },
   {
    "cell_type": "code",
diff --git a/packages/proveit/logic/sets/functions/_theory_nbs_/proofs/restriction_membership/thm_proof.ipynb b/packages/proveit/logic/sets/functions/_theory_nbs_/proofs/restriction_membership/thm_proof.ipynb
new file mode 100644
index 000000000000..f7233f262891
--- /dev/null
+++ b/packages/proveit/logic/sets/functions/_theory_nbs_/proofs/restriction_membership/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">functions</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#restriction_membership\">restriction_membership</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving restriction_membership"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/functions/_theory_nbs_/theorems.ipynb b/packages/proveit/logic/sets/functions/_theory_nbs_/theorems.ipynb
index f2e38fcd91bd..f6dee8dd4dd0 100644
--- a/packages/proveit/logic/sets/functions/_theory_nbs_/theorems.ipynb
+++ b/packages/proveit/logic/sets/functions/_theory_nbs_/theorems.ipynb
@@ -18,10 +18,11 @@
     "# Prepare this notebook for defining the theorems of a theory:\n",
     "%theorems_notebook # Keep this at the top following 'import proveit'.\n",
     "\n",
-    "from proveit import Lambda, Conditional\n",
-    "from proveit import f, x, A, B, fx\n",
-    "from proveit.logic import Forall, Equals, InSet\n",
-    "from proveit.logic.sets import Functions"
+    "from proveit import Lambda, Conditional, ConditionalSet, Function\n",
+    "from proveit import f, x, A, B, R, fx\n",
+    "from proveit.logic import Forall, Equals, InSet, NotInSet, SubsetEq\n",
+    "from proveit.logic.sets import Functions, Restriction\n",
+    "from proveit.logic.sets.functions import undefined"
    ]
   },
   {
@@ -67,6 +68,20 @@
     "                                   Functions(A, B))))"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "restriction_membership = (\n",
+    "    Forall((A, B, R),\n",
+    "    Forall(f, InSet(Restriction(f, R), Functions(R, B)),\n",
+    "           domain = Functions(A, B)),\n",
+    "    conditions = [SubsetEq(R, A)])\n",
+    ")"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
diff --git a/packages/proveit/logic/sets/functions/restriction.py b/packages/proveit/logic/sets/functions/restriction.py
new file mode 100644
index 000000000000..ac5d883f51a1
--- /dev/null
+++ b/packages/proveit/logic/sets/functions/restriction.py
@@ -0,0 +1,40 @@
+from proveit import Function, Literal, Operation
+
+class Restriction(Function):
+    '''
+    Given a function f: A -> B, with domain A and co-domain B,
+    and subset S of A, Restriction(f, S) represents the restriction
+    of the function f to domain S --- that is, g = Restriction(f, S)
+    is a new function such that g(x) = f(x) for all x in S and is
+    undefined for x not in S.
+    Restriction(f, S) is an element of the set of Functions(S, B).
+    The axiomatic definition includes the constraint that S be a
+    subset of the original domain A.
+    Future work: consider (re-)defining Restriction(f, S) to be
+    Restriction(f, A, B, S) so as to explicitly include the original
+    domain and co-domain of the function f.
+    '''
+
+    # The literal operator for the Restriction function
+    # (see further below for actual latex and string implementations).
+    _operator_ = Literal('Restriction', r'\textrm{Restriction}',
+                         theory=__file__)
+    
+    def __init__(self, f, domain, *, styles=None):
+        # Represent f|_{S}, the restriction of function f: A -> B to
+        # the restricted domain S (where S is a subset of A).
+        Operation.__init__(
+                self, Restriction._operator_, (f, domain), 
+                styles=styles)
+        self.fxn = f
+        self.domain = domain
+    
+    def latex(self, **kwargs):
+        fxn_str = self.fxn.latex(fence=True)
+        domain_str = self.domain.latex(fence=True)
+        return (fxn_str + r'\vert_{'+ domain_str + r'}')
+
+    def string(self, **kwargs):
+        fxn_str    = self.fxn.string(fence=True)
+        domain_str = self.domain.string(fence=True)
+        return (fxn_str + r'_{' + domain_str + r'}')
diff --git a/packages/proveit/logic/sets/k_power_set/__init__.py b/packages/proveit/logic/sets/k_power_set/__init__.py
new file mode 100644
index 000000000000..05868d6c61e2
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/__init__.py
@@ -0,0 +1,9 @@
+from .k_power_set import KPowerSet
+
+# KEEP THE FOLLOWING IN __init__.py FOR THEORY PACKAGES.
+#  Make additions above, or add to sys.modules[__name__].__dict__ below.
+# This allows us to import common expression, axioms, and theorems of
+# the theory package directly from the package.
+import sys
+from proveit._core_.theory import TheoryPackage
+sys.modules[__name__] = TheoryPackage(__name__, __file__, locals())
diff --git a/packages/proveit/logic/sets/k_power_set/_brief_description_.txt b/packages/proveit/logic/sets/k_power_set/_brief_description_.txt
new file mode 100644
index 000000000000..9e2196ca9a06
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_brief_description_.txt
@@ -0,0 +1 @@
+Define the set of all k-element subsets of another set.
diff --git a/packages/proveit/logic/sets/k_power_set/_sub_theories_.txt b/packages/proveit/logic/sets/k_power_set/_sub_theories_.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/__init__.py b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/axioms.ipynb b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/axioms.ipynb
new file mode 100644
index 000000000000..0e2223cd8d80
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/axioms.ipynb
@@ -0,0 +1,70 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Axioms for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.logic.sets.k_power_set</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the axioms of a theory:\n",
+    "%axioms_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "from proveit import k, A, S\n",
+    "from proveit.logic import And, Equals, Forall\n",
+    "from proveit.logic.sets import Card, InSet, KPowerSet, SubsetEq\n",
+    "from proveit.numbers import LessEq, Natural"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin axioms"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "k_power_set_def = (\n",
+    "    Forall((A, S),\n",
+    "    Forall(k, Equals(InSet(A, KPowerSet(S, k)), And(SubsetEq(A, S), Equals(Card(A), k))),\n",
+    "    conditions = [LessEq(k, Card(S))],\n",
+    "    domain = Natural)\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end axioms"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/common.ipynb b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/common.ipynb
new file mode 100644
index 000000000000..e8662e0c65bd
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/common.ipynb
@@ -0,0 +1,59 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Common expressions for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.logic.sets.k_power_set</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the common expressions of a theory:\n",
+    "%common_expressions_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "# from ... import ..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin common"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end common"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/demonstrations.ipynb b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/demonstrations.ipynb
new file mode 100644
index 000000000000..08eb6da8d651
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/demonstrations.ipynb
@@ -0,0 +1,229 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Demonstrations for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.logic.sets.k_power_set</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "%begin demonstrations\n",
+    "\n",
+    "from proveit import A\n",
+    "from proveit.logic import And, Equals, NotEquals\n",
+    "from proveit.logic.sets import (\n",
+    "        Card, InSet, KPowerSet, NotInSet, NotSubsetEq, Set, SubsetEq)\n",
+    "from proveit.numbers import one, two, three, four"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### `KPowerSet(S)`, the Set of k-Element Subsets of Set $S$:\n",
+    "\n",
+    "### $[S]^{k} = \\{A | (A \\subseteq S) \\land |A|=k\\}$"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### MISC. TESTING"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Basic Constructions"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "KPowerSet(Set(one, two, three), two)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(A, KPowerSet(Set(one, two, three), two))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Method Testing"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `KPowerSetMembership.definition()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# somewhat surprisingly, we have to assume the correct cardinality for the enumerated set\n",
+    "InSet(A, KPowerSet(Set(one, two, three), two)).definition(assumptions=[Equals(Card(Set(one, two, three)), three)])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `KPowerSetMembership.as_defined()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(A, KPowerSet(Set(one, two, three), two)).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `KPowerSetMembership.unfold()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(A, KPowerSet(Set(one, two, three), two)).unfold(\n",
+    "    assumptions=[InSet(A, KPowerSet(Set(one, two, three), two)), Equals(Card(Set(one, two, three)), three)])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `KPowerSetMembership.conclude()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(A, KPowerSet(Set(one, two, three), two)).conclude(\n",
+    "    assumptions=[And(SubsetEq(A, Set(one, two, three)), Equals(Card(A), two)), Equals(Card(Set(one, two, three)), three)])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `KPowerSetNonmembership.definition()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(A, KPowerSet(Set(one, two, three), two)).definition(assumptions = [Equals(Card(Set(one, two, three)), three)])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `KPowerSetNonmembership.as_defined()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(A, KPowerSet(Set(one, two, three), two)).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `KPowerSetNonmembership.unfold()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(A, KPowerSet(Set(one, two, three), two)).unfold(\n",
+    "    assumptions = [NotInSet(A, KPowerSet(Set(one, two, three), two)), Equals(Card(Set(one, two, three)), three)]\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `KPowerSetNonmembership.conclude()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(A, KPowerSet(Set(one, two, three), two)).conclude(\n",
+    "    assumptions = [And(NotSubsetEq(A, Set(one, two, three)), NotEquals(Card(A), two)),\n",
+    "                   Equals(Card(Set(one, two, three)), three)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end demonstrations"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_k_power_set/allowed_presumptions.txt b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_k_power_set/allowed_presumptions.txt
new file mode 100644
index 000000000000..df2d242c76be
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_k_power_set/allowed_presumptions.txt
@@ -0,0 +1 @@
+proveit.logic.equality.lhs_via_equality
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_k_power_set/disallowed_presumptions.txt b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_k_power_set/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_k_power_set/thm_proof.ipynb b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_k_power_set/thm_proof.ipynb
new file mode 100644
index 000000000000..8c3a1ca90b8e
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_k_power_set/thm_proof.ipynb
@@ -0,0 +1,95 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">k_power_set</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#fold_k_power_set\">fold_k_power_set</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory\n",
+    "\n",
+    "from proveit import defaults\n",
+    "from proveit.logic.sets.k_power_set import k_power_set_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving fold_k_power_set"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "defaults.assumptions = fold_k_power_set.all_conditions()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "k_power_set_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "k_power_set_def_inst = k_power_set_def.instantiate().instantiate()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "k_power_set_def_inst.derive_left_via_equality()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%qed"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_not_in_k_power_set/allowed_presumptions.txt b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_not_in_k_power_set/allowed_presumptions.txt
new file mode 100644
index 000000000000..f715d6503bb1
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_not_in_k_power_set/allowed_presumptions.txt
@@ -0,0 +1,2 @@
+proveit.logic.sets.k_power_set.not_in_k_power_set_equiv
+proveit.logic.equality.lhs_via_equality
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_not_in_k_power_set/disallowed_presumptions.txt b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_not_in_k_power_set/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_not_in_k_power_set/thm_proof.ipynb b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_not_in_k_power_set/thm_proof.ipynb
new file mode 100644
index 000000000000..158dd8208b3a
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/fold_not_in_k_power_set/thm_proof.ipynb
@@ -0,0 +1,86 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">k_power_set</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#fold_not_in_k_power_set\">fold_not_in_k_power_set</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory\n",
+    "\n",
+    "from proveit import defaults\n",
+    "from proveit.logic.sets.k_power_set import not_in_k_power_set_equiv"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving fold_not_in_k_power_set"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "defaults.assumptions = fold_not_in_k_power_set.all_conditions()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_k_power_set_equiv_inst = not_in_k_power_set_equiv.instantiate().instantiate()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_k_power_set_equiv_inst.derive_left_via_equality()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%qed"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/not_in_k_power_set_equiv/allowed_presumptions.txt b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/not_in_k_power_set_equiv/allowed_presumptions.txt
new file mode 100644
index 000000000000..c4a4e2ab8a68
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/not_in_k_power_set_equiv/allowed_presumptions.txt
@@ -0,0 +1,6 @@
+proveit.logic.equality.sub_right_side_into
+proveit.logic.booleans.conjunction.demorgans_law_or_to_and_bin
+proveit.logic.booleans.negation.closure
+proveit.logic.booleans.disjunction.binary_closure
+proveit.logic.booleans.implication.contrapose_neg_antecedent
+proveit.logic.sets.inclusion.subset_eq_is_bool
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/not_in_k_power_set_equiv/disallowed_presumptions.txt b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/not_in_k_power_set_equiv/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/not_in_k_power_set_equiv/thm_proof.ipynb b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/not_in_k_power_set_equiv/thm_proof.ipynb
new file mode 100644
index 000000000000..6cfbc0f14035
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/not_in_k_power_set_equiv/thm_proof.ipynb
@@ -0,0 +1,202 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">k_power_set</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#not_in_k_power_set_equiv\">not_in_k_power_set_equiv</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory\n",
+    "\n",
+    "from proveit import k, x, A, S, defaults\n",
+    "from proveit.logic import Equals, Not, Or\n",
+    "from proveit.logic.sets import SubsetEq\n",
+    "from proveit.logic.sets.membership import not_in_set_def\n",
+    "from proveit.logic.sets.k_power_set import k_power_set_def, KPowerSet"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving not_in_k_power_set_equiv"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Prove-It's message to simply execute \"%qed\" is just teasing us, apparently.\n",
+    "\n",
+    "General chalkboard-mathematics proof consists of a sequence of transformations:\n",
+    "\n",
+    "$\\begin{align}\n",
+    "A \\notin [S]^{k}\n",
+    "&=\n",
+    "\\lnot(A \\in [S]^{k}) & \\text{[not\\_in\\_set\\_def]}\\\\\n",
+    "&=\n",
+    "\\lnot\\big((A \\subseteq S)\\land(|A| = k)\\big) & \\text{[k\\_power\\_set\\_def]}\\\\\n",
+    "&=\n",
+    "(\\lnot(A \\subseteq S)\\lor \\lnot(|A| = k)) & \\text{[De Morgan's Law(s)]}\\\\\n",
+    "&=\n",
+    "((A \\not\\subseteq S)\\lor (|A| \\ne k)) & \\text{[Negation Definitions]}\n",
+    "\\end{align}$\n",
+    "\n",
+    "Currently we have no good way to complete the proof. In particular, there appears to be no good way to “apply De Morgan’s” law in the desired way, in part because we need an equality-based theorem."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "defaults.assumptions = not_in_k_power_set_equiv.all_conditions()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "a_not_in_k_power_set = not_in_k_power_set_equiv.instance_expr.instance_expr.lhs"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_set_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "a_not_k_power_set_01 = not_in_set_def.instantiate({x:A, S:KPowerSet(S, k)})"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "k_power_set_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "k_power_set_def_inst = k_power_set_def.instantiate().instantiate()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "a_not_k_power_set_02 = k_power_set_def_inst.sub_right_side_into(a_not_k_power_set_01)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "A_subseteq_S = k_power_set_def_inst.rhs.operands[0]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "card_A_eq_k = k_power_set_def_inst.rhs.operands[1]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dm_01 = a_not_k_power_set_02.rhs.operand.conclude_via_demorgans(assumptions = [Not(Or(Not(A_subseteq_S), Not(card_A_eq_k)))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dm_01.assumptions[0]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dm_02 = dm_01.as_implication(dm_01.assumptions[0])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dm_03 = dm_02.contrapose()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dm_04 = dm_03.derive_consequent(assumptions = [dm_03.antecedent])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_k_power_set/allowed_presumptions.txt b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_k_power_set/allowed_presumptions.txt
new file mode 100644
index 000000000000..cc2c81dedc51
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_k_power_set/allowed_presumptions.txt
@@ -0,0 +1 @@
+proveit.logic.equality.rhs_via_equality
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_k_power_set/disallowed_presumptions.txt b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_k_power_set/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_k_power_set/thm_proof.ipynb b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_k_power_set/thm_proof.ipynb
new file mode 100644
index 000000000000..e37dbec9931d
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_k_power_set/thm_proof.ipynb
@@ -0,0 +1,95 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">k_power_set</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#unfold_k_power_set\">unfold_k_power_set</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory\n",
+    "\n",
+    "from proveit import defaults\n",
+    "from proveit.logic.sets.k_power_set import k_power_set_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving unfold_k_power_set"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "defaults.assumptions = unfold_k_power_set.all_conditions()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "k_power_set_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "k_power_set_def_inst = k_power_set_def.instantiate().instantiate()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "k_power_set_def_inst.derive_right_via_equality()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%qed"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_not_in_k_power_set/allowed_presumptions.txt b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_not_in_k_power_set/allowed_presumptions.txt
new file mode 100644
index 000000000000..5744b061c28f
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_not_in_k_power_set/allowed_presumptions.txt
@@ -0,0 +1,2 @@
+proveit.logic.sets.k_power_set.not_in_k_power_set_equiv
+proveit.logic.equality.rhs_via_equality
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_not_in_k_power_set/disallowed_presumptions.txt b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_not_in_k_power_set/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_not_in_k_power_set/thm_proof.ipynb b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_not_in_k_power_set/thm_proof.ipynb
new file mode 100644
index 000000000000..17905d367079
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/proofs/unfold_not_in_k_power_set/thm_proof.ipynb
@@ -0,0 +1,95 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">k_power_set</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#unfold_not_in_k_power_set\">unfold_not_in_k_power_set</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory\n",
+    "\n",
+    "from proveit import defaults\n",
+    "from proveit.logic.sets.k_power_set import not_in_k_power_set_equiv"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving unfold_not_in_k_power_set"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "defaults.assumptions = unfold_not_in_k_power_set.all_conditions()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_k_power_set_equiv"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_k_power_set_equiv_inst = not_in_k_power_set_equiv.instantiate().instantiate()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_k_power_set_equiv_inst.derive_right_via_equality()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%qed"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/theorems.ipynb b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/theorems.ipynb
new file mode 100644
index 000000000000..c7e4e2541a35
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/theorems.ipynb
@@ -0,0 +1,138 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theorems (or conjectures) for the theory of <a class=\"ProveItLink\" href=\"theory.ipynb\">proveit.logic.sets.k_power_set</a>\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "# Prepare this notebook for defining the theorems of a theory:\n",
+    "%theorems_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "from proveit import k, A, S\n",
+    "from proveit.logic import And, Equals, Forall, NotEquals, Or\n",
+    "from proveit.logic.sets import (\n",
+    "        Card, InSet, KPowerSet, NotInSet, NotSubsetEq, PowerSet, SubsetEq)\n",
+    "from proveit.numbers import LessEq, Natural"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%begin theorems"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "unfold_k_power_set = (\n",
+    "    Forall((A, S),\n",
+    "    Forall(k, And(SubsetEq(A, S), Equals(Card(A), k)),\n",
+    "    conditions = [LessEq(k, Card(S)), InSet(A, KPowerSet(S, k))],\n",
+    "    domain = Natural)\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "fold_k_power_set = (\n",
+    "    Forall((A, S),\n",
+    "    Forall(k, InSet(A, KPowerSet(S, k)),\n",
+    "    conditions = [LessEq(k, Card(S)), And(SubsetEq(A, S), Equals(Card(A), k))],\n",
+    "    domain = Natural)\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_k_power_set_equiv = (\n",
+    "    Forall((A, S),\n",
+    "    Forall(k, Equals(NotInSet(A, KPowerSet(S, k)), Or(NotSubsetEq(A, S), NotEquals(Card(A), k))),\n",
+    "    conditions = [LessEq(k, Card(S))],\n",
+    "    domain = Natural)\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "unfold_not_in_k_power_set = (\n",
+    "    Forall((A, S),\n",
+    "    Forall(k, Or(NotSubsetEq(A, S), NotEquals(Card(A), k)),\n",
+    "    conditions = [LessEq(k, Card(S)), NotInSet(A, KPowerSet(S, k))],\n",
+    "    domain = Natural)\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "fold_not_in_k_power_set = (\n",
+    "    Forall((A, S),\n",
+    "    Forall(k, NotInSet(A, KPowerSet(S, k)),\n",
+    "    conditions = [LessEq(k, Card(S)), Or(NotSubsetEq(A, S), NotEquals(Card(A), k))],\n",
+    "    domain = Natural)\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end theorems"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/k_power_set/_theory_nbs_/theory.ipynb b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/theory.ipynb
new file mode 100644
index 000000000000..acc0374c963f
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/_theory_nbs_/theory.ipynb
@@ -0,0 +1,33 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Theory of <a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../_theory_nbs_/theory.ipynb\">sets</a>.k_power_set\n",
+    "========\n",
+    "\n",
+    "Provide description here."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "%theory # toggles between interactive and static modes"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/k_power_set/k_power_set.py b/packages/proveit/logic/sets/k_power_set/k_power_set.py
new file mode 100644
index 000000000000..d163422691c1
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/k_power_set.py
@@ -0,0 +1,44 @@
+from proveit import Literal, Function
+from proveit import n, x
+
+
+class KPowerSet(Function):
+    '''
+    Given a set S and a natural number k <= |S|, KPowerSet(S, k)
+    represents the set of all k-element subsets of S, denoted [S]^{k}.
+    Such sets are similar in principle to the PowerSet of S (which
+    contains all the elements of [S]^{k}) and arise often enough in
+    set theory and combinatorics to have somewhat standard notation.
+    In Prove-It, [S]^{k} arises in the context of graph theory,
+    using k = 2 to describe the superset of possible graph edges in
+    a simple graph.
+    '''
+
+    # operator for the KPowerSet function.
+    _operator_ = Literal(string_format='KPowerSet', theory=__file__)
+
+    def __init__(self, src_set, k, *, styles=None):
+        '''
+        Create the representation of the set of all k-element subsets
+        of the set src_set.
+        '''
+        Function.__init__(self, KPowerSet._operator_, (src_set, k),
+                          styles=styles)
+
+    def string(self, **kwargs):
+        return (
+            '[' + self.operands[0].string() + ']^'
+                + self.operands[1].string())
+
+    def latex(self, **kwargs):
+        return (
+            r'\left[' + self.operands[0].latex()
+                      + r'\right]^{' + self.operands[1].latex() + r'}')
+
+    def membership_object(self, element):
+        from .k_power_set_membership import KPowerSetMembership
+        return KPowerSetMembership(element, self)
+
+    def nonmembership_object(self, element):
+        from .k_power_set_membership import KPowerSetNonmembership
+        return KPowerSetNonmembership(element, self)
diff --git a/packages/proveit/logic/sets/k_power_set/k_power_set_membership.py b/packages/proveit/logic/sets/k_power_set/k_power_set_membership.py
new file mode 100644
index 000000000000..27c605987229
--- /dev/null
+++ b/packages/proveit/logic/sets/k_power_set/k_power_set_membership.py
@@ -0,0 +1,148 @@
+from proveit import k, A, S, equality_prover, prover
+from proveit.logic import (
+        And, Equals, NotEquals, Or, SetMembership, SetNonmembership)
+from proveit.logic.sets import Card, NotSubsetEq, SubsetEq
+
+
+class KPowerSetMembership(SetMembership):
+    '''
+    Defines methods that apply to membership in the set of
+    k-element subsets of a set.
+    '''
+
+    def __init__(self, element, domain):
+        SetMembership.__init__(self, element, domain)
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        Deduce and return 
+        [elem in KPowerSet(S, k)] = [SubsetEq(elem, S) AND |elem|=k]
+        where self = [element in KPowerSet(S)].
+        '''
+        from . import k_power_set_def
+        element = self.element
+        operands = self.domain.operands
+        _A_sub = element
+        _S_sub = operands[0]
+        _k_sub = operands[1]
+        return k_power_set_def.instantiate(
+                {A:_A_sub, S: _S_sub, k:_k_sub}, auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self=[elem in KPowerSet(S, k)], return
+        [SubsetEq(elem, S) AND |elem|=k] (i.e. not a Judgment).
+        '''
+        element = self.element
+        superset = self.domain.operands[0]
+        subset_size = self.domain.operands[1]
+        return And(SubsetEq(element, superset),
+                   Equals(Card(element), subset_size))
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From TRUE or assumed self = [elem in KPowerSet(S, k)],
+        derive and return [SubsetEq(elem, S) AND |elem| = k].
+        '''
+        from . import unfold_k_power_set
+        element = self.element
+        operands = self.domain.operands
+        _A_sub = element
+        _S_sub = operands[0]
+        _k_sub = operands[1]
+        return unfold_k_power_set.instantiate(
+            {A:_A_sub, S: _S_sub, k:_k_sub}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on self = [elem in KPowerSet(S, k)], and knowing or
+        assuming [SubsetEq(elem, S) AND |elem| =k], derive and
+        return self.
+        '''
+        from . import fold_k_power_set
+        element = self.element
+        operands = self.domain.operands
+        _A_sub = element
+        _S_sub = operands[0]
+        _k_sub = operands[1]
+        return fold_k_power_set.instantiate(
+            {A:_A_sub, S: _S_sub, k:_k_sub}, auto_simplify=False)
+
+
+class KPowerSetNonmembership(SetNonmembership):
+    '''
+    Defines methods that apply to non-membership in the set of
+    k-element subsets of a set.
+    '''
+
+    def __init__(self, element, domain):
+        SetNonmembership.__init__(self, element, domain)
+
+    def side_effects(self, judgment):
+        '''
+        Currently no side-effects for KPowerSet nonmembership.
+        '''
+        return
+        yield
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self=[elem not in KPowerSet(S, k)], deduce and return
+            |- [elem not in KPowerSet(S, k)] =
+               [NotSubsetEq(elem, S) OR |elem| != k].
+        '''
+        from . import not_in_k_power_set_equiv
+        element = self.element
+        operands = self.domain.operands
+        _A_sub = element
+        _S_sub = operands[0]
+        _k_sub = operands[1]
+        return not_in_k_power_set_equiv.instantiate(
+            {A:_A_sub, S: _S_sub, k:_k_sub}, auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self=[elem not in KPowerSet(S, k)], return
+        [NotSubsetEq(elem, S) OR |elem| != k] (i.e., not a Judgment).
+        '''
+        element = self.element
+        superset = self.domain.operands[0]
+        subset_size = self.domain.operands[1]
+        return Or(NotSubsetEq(element, superset),
+                   NotEquals(Card(element), subset_size))
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From TRUE or assumed self = [elem not in KPowerSet(S, k)],
+        derive and return [NotSubsetEq(elem, S) OR |elem| != k].
+        '''
+        from . import unfold_not_in_k_power_set
+        element = self.element
+        operands = self.domain.operands
+        _A_sub = element
+        _S_sub = operands[0]
+        _k_sub = operands[1]
+        return unfold_not_in_k_power_set.instantiate(
+            {A:_A_sub, S: _S_sub, k:_k_sub}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on the self = [elem not in KPowerSet(S, k)], from known
+        or assumed [NotSubsetEq(elem, S) OR |elem| != k], derive and 
+        return self.
+        '''
+        from . import fold_not_in_k_power_set
+        element = self.element
+        operands = self.domain.operands
+        _A_sub = element
+        _S_sub = operands[0]
+        _k_sub = operands[1]
+        return fold_not_in_k_power_set.instantiate(
+            {A:_A_sub, S: _S_sub, k:_k_sub}, auto_simplify=False)
+
diff --git a/packages/proveit/logic/sets/power_set/__init__.py b/packages/proveit/logic/sets/power_set/__init__.py
index 6f40f95018c6..450b5a76e967 100644
--- a/packages/proveit/logic/sets/power_set/__init__.py
+++ b/packages/proveit/logic/sets/power_set/__init__.py
@@ -1,4 +1,5 @@
 from .power_set import PowerSet
+from .k_elem_subsets import KElemSubsets
 
 
 # KEEP THE FOLLOWING IN __init__.py FOR THEORY PACKAGES.
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/axioms.ipynb b/packages/proveit/logic/sets/power_set/_theory_nbs_/axioms.ipynb
index 1fb12ff6c876..97058c48a7c9 100644
--- a/packages/proveit/logic/sets/power_set/_theory_nbs_/axioms.ipynb
+++ b/packages/proveit/logic/sets/power_set/_theory_nbs_/axioms.ipynb
@@ -18,7 +18,8 @@
     "# Prepare this notebook for defining the axioms of a theory:\n",
     "%axioms_notebook # Keep this at the top following 'import proveit'.\n",
     "from proveit import x, S\n",
-    "from proveit.logic import Equals, Forall, InSet, PowerSet, SubsetEq"
+    "from proveit.numbers import LessEq, Natural\n",
+    "from proveit.logic import Equals, Forall, InSet, Not, NotInSet, PowerSet, SubsetEq"
    ]
   },
   {
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/demonstrations.ipynb b/packages/proveit/logic/sets/power_set/_theory_nbs_/demonstrations.ipynb
index 70492f4f0f08..6b2fff2efa5e 100644
--- a/packages/proveit/logic/sets/power_set/_theory_nbs_/demonstrations.ipynb
+++ b/packages/proveit/logic/sets/power_set/_theory_nbs_/demonstrations.ipynb
@@ -15,11 +15,33 @@
    "outputs": [],
    "source": [
     "import proveit\n",
-    "from proveit.logic import PowerSet, Set\n",
-    "from proveit.numbers import one, two\n",
+    "from proveit import A\n",
+    "from proveit.logic import InSet, NotInSet, NotSubsetEq, PowerSet, Set, SubsetEq\n",
+    "from proveit.numbers import one, two, three\n",
     "%begin demonstrations"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Power Set: $\\mathcal{P}(S)$"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### MISC. TESTING"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Basic Constructions"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -29,6 +51,152 @@
     "PowerSet(Set(one, two))"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(A, PowerSet(Set(one, two)))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Method Testing"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `PowerSetMembership.definition()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(A, PowerSet(Set(one, two, three))).definition()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `PowerSetMembership.as_defined()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(A, PowerSet(Set(one, two, three))).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `PowerSetMembership.unfold()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(A, PowerSet(Set(one, two, three))).unfold(assumptions = [InSet(A, PowerSet(Set(one, two, three)))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `PowerSetMembership.conclude()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(A, PowerSet(Set(one, two, three))).conclude(assumptions = [SubsetEq(A, Set(one, two, three))])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `PowerSetNonmembership.definition()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(A, PowerSet(Set(one, two, three))).definition()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `PowerSetNonmembership.as_defined()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(A, PowerSet(Set(one, two, three))).as_defined()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `PowerSetNonmembership.unfold()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(A, PowerSet(Set(one, two, three))).unfold(\n",
+    "    assumptions = [NotInSet(A, PowerSet(Set(one, two, three)))]\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Testing `PowerSetNonmembership.conclude()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "NotInSet(A, PowerSet(Set(one, two, three))).conclude(assumptions = [NotSubsetEq(A, Set(one, two, three))])"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_not_in_power_set/allowed_presumptions.txt b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_not_in_power_set/allowed_presumptions.txt
new file mode 100644
index 000000000000..d9e192377716
--- /dev/null
+++ b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_not_in_power_set/allowed_presumptions.txt
@@ -0,0 +1,2 @@
+proveit.logic.sets.power_set.not_in_power_set_equiv
+proveit.logic.equality.lhs_via_equality
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_not_in_power_set/disallowed_presumptions.txt b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_not_in_power_set/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_not_in_power_set/thm_proof.ipynb b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_not_in_power_set/thm_proof.ipynb
new file mode 100644
index 000000000000..1da84a8c05bf
--- /dev/null
+++ b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_not_in_power_set/thm_proof.ipynb
@@ -0,0 +1,95 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">power_set</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#fold_not_in_power_set\">fold_not_in_power_set</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory\n",
+    "\n",
+    "from proveit import defaults\n",
+    "from proveit.logic.sets.power_set import not_in_power_set_equiv"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving fold_not_in_power_set"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "defaults.assumptions = fold_not_in_power_set.all_conditions()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_power_set_equiv"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_power_set_equiv_inst = not_in_power_set_equiv.instantiate()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_power_set_equiv_inst.derive_left_via_equality()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%qed"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_power_set/allowed_presumptions.txt b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_power_set/allowed_presumptions.txt
new file mode 100644
index 000000000000..df2d242c76be
--- /dev/null
+++ b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_power_set/allowed_presumptions.txt
@@ -0,0 +1 @@
+proveit.logic.equality.lhs_via_equality
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_power_set/disallowed_presumptions.txt b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_power_set/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_power_set/thm_proof.ipynb b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_power_set/thm_proof.ipynb
new file mode 100644
index 000000000000..51bd510e64e4
--- /dev/null
+++ b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/fold_power_set/thm_proof.ipynb
@@ -0,0 +1,95 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">power_set</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#fold_power_set\">fold_power_set</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory\n",
+    "\n",
+    "from proveit import defaults\n",
+    "from proveit.logic.sets.power_set import power_set_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving fold_power_set"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "defaults.assumptions = fold_power_set.all_conditions()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "power_set_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "power_set_def_inst = power_set_def.instantiate()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "power_set_def_inst.derive_left_via_equality()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%qed"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/not_in_power_set_equiv/allowed_presumptions.txt b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/not_in_power_set_equiv/allowed_presumptions.txt
new file mode 100644
index 000000000000..7aff58fec040
--- /dev/null
+++ b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/not_in_power_set_equiv/allowed_presumptions.txt
@@ -0,0 +1,2 @@
+proveit.logic.equality.sub_right_side_into
+proveit.logic.equality.sub_left_side_into
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/not_in_power_set_equiv/disallowed_presumptions.txt b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/not_in_power_set_equiv/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/not_in_power_set_equiv/thm_proof.ipynb b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/not_in_power_set_equiv/thm_proof.ipynb
new file mode 100644
index 000000000000..148bce242fdb
--- /dev/null
+++ b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/not_in_power_set_equiv/thm_proof.ipynb
@@ -0,0 +1,136 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">power_set</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#not_in_power_set_equiv\">not_in_power_set_equiv</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory\n",
+    "\n",
+    "from proveit import x, A, B, S, defaults\n",
+    "from proveit.logic.sets import PowerSet\n",
+    "from proveit.logic.sets.inclusion import not_subset_eq_def\n",
+    "from proveit.logic.sets.membership import not_in_set_def\n",
+    "from proveit.logic.sets.power_set import power_set_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving not_in_power_set_equiv"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "defaults.assumptions = not_in_power_set_equiv.all_conditions()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_set_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_set_def_inst = not_in_set_def.instantiate({x:x, S:PowerSet(S)})"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "power_set_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "power_set_def_inst = power_set_def.instantiate()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "x_not_in_power_set_eq_not_x_subset = power_set_def_inst.sub_right_side_into(not_in_set_def_inst)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_subset_eq_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_subset_eq_def_inst = not_subset_eq_def.instantiate({A:x, B:S})"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_subset_eq_def_inst.sub_left_side_into(x_not_in_power_set_eq_not_x_subset)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%qed"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_not_in_power_set/allowed_presumptions.txt b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_not_in_power_set/allowed_presumptions.txt
new file mode 100644
index 000000000000..841553163c28
--- /dev/null
+++ b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_not_in_power_set/allowed_presumptions.txt
@@ -0,0 +1,2 @@
+proveit.logic.sets.power_set.not_in_power_set_equiv
+proveit.logic.equality.rhs_via_equality
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_not_in_power_set/disallowed_presumptions.txt b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_not_in_power_set/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_not_in_power_set/thm_proof.ipynb b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_not_in_power_set/thm_proof.ipynb
new file mode 100644
index 000000000000..e95c3312e22d
--- /dev/null
+++ b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_not_in_power_set/thm_proof.ipynb
@@ -0,0 +1,95 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">power_set</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#unfold_not_in_power_set\">unfold_not_in_power_set</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory\n",
+    "\n",
+    "from proveit import defaults\n",
+    "from proveit.logic.sets.power_set import not_in_power_set_equiv"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving unfold_not_in_power_set"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "defaults.assumptions = unfold_not_in_power_set.all_conditions()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_power_set_equiv"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_power_set_equiv_inst = not_in_power_set_equiv.instantiate()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_power_set_equiv_inst.derive_right_via_equality()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%qed"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_power_set/allowed_presumptions.txt b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_power_set/allowed_presumptions.txt
new file mode 100644
index 000000000000..cc2c81dedc51
--- /dev/null
+++ b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_power_set/allowed_presumptions.txt
@@ -0,0 +1 @@
+proveit.logic.equality.rhs_via_equality
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_power_set/disallowed_presumptions.txt b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_power_set/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_power_set/thm_proof.ipynb b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_power_set/thm_proof.ipynb
new file mode 100644
index 000000000000..7847228125cc
--- /dev/null
+++ b/packages/proveit/logic/sets/power_set/_theory_nbs_/proofs/unfold_power_set/thm_proof.ipynb
@@ -0,0 +1,95 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">logic</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">power_set</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#unfold_power_set\">unfold_power_set</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory\n",
+    "\n",
+    "from proveit import defaults\n",
+    "from proveit.logic.sets.power_set import power_set_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving unfold_power_set"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "defaults.assumptions = unfold_power_set.all_conditions()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "power_set_def"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "power_set_def_inst = power_set_def.instantiate()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "power_set_def_inst.derive_right_via_equality()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%qed"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/logic/sets/power_set/_theory_nbs_/theorems.ipynb b/packages/proveit/logic/sets/power_set/_theory_nbs_/theorems.ipynb
index 338e87eb081b..6e2c1ad39159 100644
--- a/packages/proveit/logic/sets/power_set/_theory_nbs_/theorems.ipynb
+++ b/packages/proveit/logic/sets/power_set/_theory_nbs_/theorems.ipynb
@@ -16,7 +16,12 @@
    "source": [
     "import proveit\n",
     "# Prepare this notebook for defining the theorems of a theory:\n",
-    "%theorems_notebook # Keep this at the top following 'import proveit'."
+    "%theorems_notebook # Keep this at the top following 'import proveit'.\n",
+    "\n",
+    "from proveit import k, x, A, S\n",
+    "from proveit.logic import And, Equals, Forall\n",
+    "from proveit.logic.sets import Card, InSet, NotInSet, NotSubsetEq, PowerSet, SubsetEq\n",
+    "from proveit.numbers import LessEq, Natural"
    ]
   },
   {
@@ -33,7 +38,49 @@
    "execution_count": null,
    "metadata": {},
    "outputs": [],
-   "source": []
+   "source": [
+    "unfold_power_set = Forall((x, S), SubsetEq(x, S),\n",
+    "       conditions = [InSet(x, PowerSet(S))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "fold_power_set = Forall((x, S), InSet(x, PowerSet(S)),\n",
+    "       conditions = [SubsetEq(x, S)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "not_in_power_set_equiv = Forall((x, S), Equals(NotInSet(x, PowerSet(S)), NotSubsetEq(x, S)))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "unfold_not_in_power_set = Forall((x, S), NotSubsetEq(x, S),\n",
+    "        conditions = [NotInSet(x, PowerSet(S))])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "fold_not_in_power_set = Forall((x, S), NotInSet(x, PowerSet(S)),\n",
+    "        conditions = [NotSubsetEq(x, S)])"
+   ]
   },
   {
    "cell_type": "code",
diff --git a/packages/proveit/logic/sets/power_set/k_elem_subsets.py b/packages/proveit/logic/sets/power_set/k_elem_subsets.py
new file mode 100644
index 000000000000..e4002ddef826
--- /dev/null
+++ b/packages/proveit/logic/sets/power_set/k_elem_subsets.py
@@ -0,0 +1,43 @@
+from proveit import Literal, Function
+from proveit import n, x
+
+
+class KElemSubsets(Function):
+    '''
+    Given some set S, KElemSubsets(S) represents the set of all
+    k-element subsets of S, denoted [S]^{k}. Such sets are similar
+    in principle to the PowerSet of S (which contains [S]^{k}) and
+    arise often enough in set theory and combinatorics to have
+    somewhat standard notation. In Prove-It, [S]^{k} arises in the
+    context of graph theory, using k = 2 to describe the superset
+    of possible graph edges in a simple graph.
+    '''
+
+    # operator for the KElemSubsets function.
+    _operator_ = Literal(string_format='KElemSubsets', theory=__file__)
+
+    def __init__(self, src_set, k, *, styles=None):
+        '''
+        Create the representation of the set of all k-element subsets
+        of the set src_set.
+        '''
+        Function.__init__(self, KElemSubsets._operator_, (src_set, k),
+                          styles=styles)
+
+    def string(self, **kwargs):
+        return (
+            '[' + self.operands[0].string() + ']^'
+                + self.operands[1].string())
+
+    def latex(self, **kwargs):
+        return (
+            r'\left[' + self.operands[0].latex()
+                      + r'\right]^{' + self.operands[1].latex() + r'}')
+
+    def membership_object(self, element):
+        from .k_elem_subsets_membership import KElemSubsetsMembership
+        return KElemSubsetsMembership(element, self)
+
+    def nonmembership_object(self, element):
+        from .k_elem_subsets_membership import KElemSubsetsNonmembership
+        return KElemSubsetsNonmembership(element, self)
diff --git a/packages/proveit/logic/sets/power_set/k_elem_subsets_membership.py b/packages/proveit/logic/sets/power_set/k_elem_subsets_membership.py
new file mode 100644
index 000000000000..fbca6909c7fa
--- /dev/null
+++ b/packages/proveit/logic/sets/power_set/k_elem_subsets_membership.py
@@ -0,0 +1,24 @@
+# from proveit import USE_DEFAULTS, equality_prover, prover
+from proveit.logic import SetMembership, SetNonmembership
+# from proveit.numbers import num
+# from proveit import m, A, x
+
+
+class KElemSubsetsMembership(SetMembership):
+    '''
+    Defines methods that apply to membership in the set of
+    k-element subsets of a set.
+    '''
+
+    def __init__(self, element, domain):
+        SetMembership.__init__(self, element, domain)
+
+
+class KElemSubsetsNonmembership(SetNonmembership):
+    '''
+    Defines methods that apply to non-membership in the set of
+    k-element subsets of a set.
+    '''
+
+    def __init__(self, element, domain):
+        SetNonmembership.__init__(self, element, domain)
diff --git a/packages/proveit/logic/sets/power_set/power_set.py b/packages/proveit/logic/sets/power_set/power_set.py
index 234be816515a..f74ebab018ba 100644
--- a/packages/proveit/logic/sets/power_set/power_set.py
+++ b/packages/proveit/logic/sets/power_set/power_set.py
@@ -16,14 +16,10 @@ def __init__(self, operand, *, styles=None):
         Function.__init__(self, PowerSet._operator_, operand,
                           styles=styles)
 
-    """
-    # Needs implementation
-
     def membership_object(self, element):
-        from .powerset_membership import PowerSetMembership
+        from .power_set_membership import PowerSetMembership
         return PowerSetMembership(element, self)
 
     def nonmembership_object(self, element):
-        from .powerset_membership import PowerSetNonmembership
+        from .power_set_membership import PowerSetNonmembership
         return PowerSetNonmembership(element, self)
-    """
diff --git a/packages/proveit/logic/sets/power_set/power_set_membership.py b/packages/proveit/logic/sets/power_set/power_set_membership.py
new file mode 100644
index 000000000000..d13a5fab06d6
--- /dev/null
+++ b/packages/proveit/logic/sets/power_set/power_set_membership.py
@@ -0,0 +1,134 @@
+from proveit import x, S, equality_prover, prover
+from proveit.logic import SetMembership, SetNonmembership
+
+
+class PowerSetMembership(SetMembership):
+    '''
+    Defines methods that apply to membership in the power set,
+    PowerSet(S), of a set S.
+    '''
+
+    def __init__(self, element, domain):
+        SetMembership.__init__(self, element, domain)
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        Deduce and return 
+            [element in PowerSet(S)] = [SubsetEq(element, S)]
+        where self = [element in PowerSet(S)].
+        '''
+        from . import power_set_def
+        element = self.element
+        operand = self.domain.operand
+        _x_sub = element
+        _S_sub = operand
+        return power_set_def.instantiate(
+                {x: _x_sub, S: _S_sub}, auto_simplify=False)
+
+    def as_defined(self):
+        '''
+        From self=[elem in PowerSet(S)], return [SubsetEq(element, S)]
+        (i.e., not a Judgment).
+        '''
+        from proveit.logic import SubsetEq
+        element = self.element
+        return SubsetEq(element, self.domain.operand)
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From TRUE or assumed self = [element in PowerSet(S)],
+        derive and return [SubsetEq(element, S)].
+        '''
+        from . import unfold_power_set
+        element = self.element
+        operand = self.domain.operand
+        _x_sub = element
+        _S_sub = operand
+        return unfold_power_set.instantiate(
+            {x: _x_sub, S: _S_sub}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on self = [elem in PowerSet(S)], and knowing or
+        assuming [SubsetEq(elem, S)], derive and
+        return self.
+        '''
+        from . import fold_power_set
+        element = self.element
+        operand = self.domain.operand
+        _x_sub = element
+        _S_sub = operand
+        return fold_power_set.instantiate(
+            {x: _x_sub, S: _S_sub}, auto_simplify=False)
+
+
+class PowerSetNonmembership(SetNonmembership):
+    '''
+    Defines methods that apply to non-membership in the power set,
+    PowerSet(S), of a set S.
+    '''
+
+    def __init__(self, element, domain):
+        SetNonmembership.__init__(self, element, domain)
+
+    def side_effects(self, judgment):
+        '''
+        Currently no side-effects for PowerSet nonmembership.
+        '''
+        return
+        yield
+
+    @equality_prover('defined', 'define')
+    def definition(self, **defaults_config):
+        '''
+        From self=[elem not in PowerSet(S)], deduce and return
+            |- [elem not in PowerSet(S)] = [NotSubsetEq(elem, S)].
+        '''
+        from . import not_in_power_set_equiv
+        element = self.element
+        operand = self.domain.operand
+        _x_sub = element
+        _S_sub = operand
+        return not_in_power_set_equiv.instantiate(
+            {x: _x_sub, S: _S_sub}, auto_simplify=False)
+
+
+    def as_defined(self):
+        '''
+        From self=[elem not in PowerSet(S)], return
+        [NotSubsetEq(elem, S)] (i.e., not as a Judgment).
+        '''
+        from proveit.logic import NotSubsetEq
+        element = self.element
+        return NotSubsetEq(element, self.domain.operand)
+
+    @prover
+    def unfold(self, **defaults_config):
+        '''
+        From TRUE or assumed self = [element not in PowerSet(S)],
+        derive and return [NotSubsetEq(element, S)].
+        '''
+        from . import unfold_not_in_power_set
+        element = self.element
+        operand = self.domain.operand
+        _x_sub = element
+        _S_sub = operand
+        return unfold_not_in_power_set.instantiate(
+            {x: _x_sub, S: _S_sub}, auto_simplify=False)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Called on the self = [elem not in PowerSet(S)], from known
+        or assumed [NotSubsetEq(elem, S)], derive and return self.
+        '''
+        from . import fold_not_in_power_set
+        element = self.element
+        operand = self.domain.operand
+        _x_sub = element
+        _S_sub = operand
+        return fold_not_in_power_set.instantiate(
+            {x: _x_sub, S: _S_sub})
diff --git a/packages/proveit/numbers/__init__.py b/packages/proveit/numbers/__init__.py
index 03a471ce5102..4c25a2184db5 100644
--- a/packages/proveit/numbers/__init__.py
+++ b/packages/proveit/numbers/__init__.py
@@ -2,9 +2,9 @@
 
 from .number_sets import (
     ZeroSet, Natural, NaturalPos,
-    Integer, IntegerNonZero, IntegerNeg, IntegerNonPos,
-    Rational, RationalNonZero, RationalPos, RationalNeg, RationalNonNeg,
-    RationalNonPos,
+    Integer, IntegerEven, IntegerNonZero, IntegerNeg, IntegerNonPos,
+    IntegerOdd, Rational, RationalNonZero, RationalPos, RationalNeg,
+    RationalNonNeg, RationalNonPos,
     Real, RealNonZero, RealNeg, RealPos, RealNonNeg, RealNonPos,
     Complex, ComplexNonZero, Conjugate,
     complex_polar_coordinates, 
@@ -57,11 +57,14 @@
 import proveit
 
 
-if proveit.defaults.running_theory_notebook is None:
+if proveit.defaults.running_theory_notebook is None: # might include here 2 is even, 4 is even, etc
     # Import some fundamental theorems without quantifiers when not 
     # running an common/axioms/theorems theory notebook.
     from .number_sets.natural_numbers import zero_in_nats
-    from .number_sets.integers import zero_is_int, zero_is_nonpos_int
+    from .number_sets.integers import (zero_is_int, zero_is_nonpos_int,
+            zero_is_even, two_is_even, four_is_even, six_is_even,
+            eight_is_even, one_is_odd, three_is_odd, five_is_odd,
+            seven_is_odd, nine_is_odd)
     from .number_sets.rational_numbers import (
             zero_is_rational, zero_is_nonneg_rational, 
             zero_is_nonpos_rational)
diff --git a/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_even_closure/thm_proof.ipynb b/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_even_closure/thm_proof.ipynb
new file mode 100644
index 000000000000..ade3c3a62f17
--- /dev/null
+++ b/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_even_closure/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">addition</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#add_int_even_closure\">add_int_even_closure</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving add_int_even_closure"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_even_closure_bin/thm_proof.ipynb b/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_even_closure_bin/thm_proof.ipynb
new file mode 100644
index 000000000000..6667c22293e4
--- /dev/null
+++ b/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_even_closure_bin/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">addition</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#add_int_even_closure_bin\">add_int_even_closure_bin</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving add_int_even_closure_bin"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_even_from_even_odd/thm_proof.ipynb b/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_even_from_even_odd/thm_proof.ipynb
new file mode 100644
index 000000000000..68f60b241c7f
--- /dev/null
+++ b/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_even_from_even_odd/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">addition</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#add_int_even_from_even_odd\">add_int_even_from_even_odd</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving add_int_even_from_even_odd"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_even_from_odd_bin/thm_proof.ipynb b/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_even_from_odd_bin/thm_proof.ipynb
new file mode 100644
index 000000000000..c797ce4185e1
--- /dev/null
+++ b/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_even_from_odd_bin/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">addition</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#add_int_even_from_odd_bin\">add_int_even_from_odd_bin</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving add_int_even_from_odd_bin"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_odd_from_even_and_odd_bin/thm_proof.ipynb b/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_odd_from_even_and_odd_bin/thm_proof.ipynb
new file mode 100644
index 000000000000..aa302d8c17f0
--- /dev/null
+++ b/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_odd_from_even_and_odd_bin/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">addition</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#add_int_odd_from_even_and_odd_bin\">add_int_odd_from_even_and_odd_bin</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving add_int_odd_from_even_and_odd_bin"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_odd_from_odd_and_even_bin/thm_proof.ipynb b/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_odd_from_odd_and_even_bin/thm_proof.ipynb
new file mode 100644
index 000000000000..9ed79aad02c2
--- /dev/null
+++ b/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_odd_from_odd_and_even_bin/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">addition</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#add_int_odd_from_odd_and_even_bin\">add_int_odd_from_odd_and_even_bin</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving add_int_odd_from_odd_and_even_bin"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_odd_from_odd_odd/thm_proof.ipynb b/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_odd_from_odd_odd/thm_proof.ipynb
new file mode 100644
index 000000000000..982f25fbee7c
--- /dev/null
+++ b/packages/proveit/numbers/addition/_theory_nbs_/proofs/add_int_odd_from_odd_odd/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">addition</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#add_int_odd_from_odd_odd\">add_int_odd_from_odd_odd</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving add_int_odd_from_odd_odd"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/addition/_theory_nbs_/theorems.ipynb b/packages/proveit/numbers/addition/_theory_nbs_/theorems.ipynb
index 9407d00b593d..2d416cebab9d 100644
--- a/packages/proveit/numbers/addition/_theory_nbs_/theorems.ipynb
+++ b/packages/proveit/numbers/addition/_theory_nbs_/theorems.ipynb
@@ -28,7 +28,7 @@
     "    Add, Neg, Mult, frac, Exp, \n",
     "    Less, LessEq, greater, greater_eq,\n",
     "    ZeroSet, Natural, NaturalPos,\n",
-    "    Integer, IntegerNonZero, IntegerNeg, IntegerNonPos,\n",
+    "    Integer, IntegerEven, IntegerNonZero, IntegerNeg, IntegerNonPos, IntegerOdd,\n",
     "    Rational, RationalNonZero, RationalPos, RationalNeg,\n",
     "    RationalNonNeg, RationalNonPos,\n",
     "    Real, RealNonZero, RealPos, RealNeg, RealNonNeg, RealNonPos,\n",
@@ -162,6 +162,24 @@
     "add_int_nonpos_closure = Forall(i, Forall(a_1_to_i, InSet(Add(a_1_to_i), IntegerNonPos), domain=IntegerNonPos), domain = Natural)"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "add_int_even_closure_bin = Forall((a, b), InSet(Add(a, b), IntegerEven), domain = IntegerEven)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "add_int_even_closure = Forall(i, Forall(a_1_to_i, InSet(Add(a_1_to_i), IntegerEven), domain = IntegerEven), domain = NaturalPos)"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -188,6 +206,61 @@
     "                             domain=Natural)"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "add_int_even_from_odd_bin = Forall((a, b), InSet(Add(a, b), IntegerEven), domain = IntegerOdd)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "add_int_even_from_even_odd = (\n",
+    "    Forall(i,\n",
+    "           Forall(a_1_to_i, InSet(Add(a_1_to_i), IntegerEven), domain = IntegerOdd),\n",
+    "    condition=greater_eq(i, zero),\n",
+    "    domain=IntegerEven)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "add_int_odd_from_even_and_odd_bin = Forall((a, b), InSet(Add(a, b), IntegerOdd), domains = [IntegerEven, IntegerOdd])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "add_int_odd_from_odd_and_even_bin = Forall((a, b), InSet(Add(a, b), IntegerOdd), domains = [IntegerOdd, IntegerEven])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "add_int_odd_from_odd_odd = (\n",
+    "    Forall(i,\n",
+    "           Forall(a_1_to_i, InSet(Add(a_1_to_i), IntegerOdd), domain = IntegerOdd),\n",
+    "    condition=greater(i, zero),\n",
+    "    domain=IntegerOdd)\n",
+    ")"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
diff --git a/packages/proveit/numbers/addition/add.py b/packages/proveit/numbers/addition/add.py
index 424a080ff231..c57c0a35eeb0 100644
--- a/packages/proveit/numbers/addition/add.py
+++ b/packages/proveit/numbers/addition/add.py
@@ -3,7 +3,7 @@
 from collections import deque, Counter
 
 from proveit import (Expression, Judgment, Literal, Operation, ExprTuple,
-                     ExprRange, defaults, StyleOptions, 
+                     ExprRange, defaults, StyleOptions,
                      prover, relation_prover, equality_prover,
                      auto_prover, auto_relation_prover, auto_equality_prover,
                      maybe_fenced_latex, ProofFailure, InnerExpr,
@@ -15,7 +15,7 @@
                            EvaluationError, InSet)
 from proveit.logic.irreducible_value import is_irreducible_value
 from proveit.numbers import (NumberOperation, standard_number_set,
-                             pos_number_set, neg_number_set, 
+                             pos_number_set, neg_number_set,
                              nonneg_number_set, nonpos_number_set,
                              nonzero_number_set,
                              union_number_set, deduce_number_set,
@@ -23,26 +23,27 @@
 from proveit.numbers.numerals.decimals import DIGITS
 import proveit.numbers.numerals.decimals
 from proveit.abstract_algebra.generic_methods import (
-        apply_commutation_thm, apply_association_thm, 
+        apply_commutation_thm, apply_association_thm,
         apply_disassociation_thm, group_commutation, pairwise_evaluation,
         deduce_equality_via_commutation, generic_permutation,
         sorting_operands, sorting_and_combining_like_operands,
         common_likeness_key)
 from proveit import TransRelUpdater
-from proveit.numbers import (ZeroSet, Integer, IntegerNeg, IntegerNonPos,
+from proveit.numbers import (ZeroSet, Integer, IntegerEven,
+                             IntegerNeg, IntegerNonPos, IntegerOdd,
                              Natural, NaturalPos, IntegerNonZero,
                              Rational, RationalPos, RationalNonZero,
                              RationalNeg, RationalNonNeg,
                              RationalNonPos,
                              Real, RealPos, RealNeg, RealNonNeg,
-                             RealNonPos, RealNonZero, Complex, 
+                             RealNonPos, RealNonZero, Complex,
                              ComplexNonZero)
 
 
 class Add(NumberOperation):
     # operator of the Add operation
     _operator_ = Literal(string_format='+', theory=__file__)
-    
+
     # The 'order_key' simplification directive can be used
     # to sort terms in a particular order.  By default, there
     # is no sorting -- it keeps the original order as much as
@@ -57,7 +58,7 @@ def __init__(self, *operands, styles=None):
         r'''
         Add together any number of operands.
         '''
-        NumberOperation.__init__(self, Add._operator_, operands, 
+        NumberOperation.__init__(self, Add._operator_, operands,
                                  styles=styles)
         self.terms = self.operands
 
@@ -82,9 +83,9 @@ def _formatted(self, format_type, **kwargs):
         '''
         from proveit import ExprRange
         from proveit.numbers import Neg
-        
-        # Where should we use subtraction notation 
-        subtraction_positions = []        
+
+        # Where should we use subtraction notation
+        subtraction_positions = []
         for _k, operand in enumerate(self.operands.entries):
             if isinstance(operand, Neg):
                 if operand.use_subtraction_notation():
@@ -93,7 +94,7 @@ def _formatted(self, format_type, **kwargs):
                 if isinstance(operand.body, Neg):
                     if operand.body.use_subtraction_notation():
                         subtraction_positions.append(_k)
-        
+
         if len(subtraction_positions) > 0:
             operators = []
             operands = list(self.operands.entries)
@@ -158,7 +159,7 @@ def _formatted(self, format_type, **kwargs):
 
     def remake_constructor(self):
         from proveit.numbers import Neg
-        if (self.operands.is_double() 
+        if (self.operands.is_double()
                 and isinstance(self.operands[1], Neg)
                 and self.operands[1].use_subtraction_notation()):
             return 'subtract'  # use a different constructor if using the subtraction style
@@ -170,7 +171,7 @@ def remake_arguments(self):
         that could be used to recreate the Operation.
         '''
         from proveit.numbers import Neg
-        if (self.operands.is_double() 
+        if (self.operands.is_double()
                 and isinstance(self.operands[1], Neg)
                 and self.operands[1].use_subtraction_notation()):
             yield self.operands[0]
@@ -219,12 +220,12 @@ def equality_side_effects(self, judgment):
 
     def _build_canonical_form(self):
         '''
-        Returns a form of this Add with operands in their canonical 
-        forms, nested addition is ungrouped, and "common" terms that 
-        are the same except coefficient factors are combined, and 
+        Returns a form of this Add with operands in their canonical
+        forms, nested addition is ungrouped, and "common" terms that
+        are the same except coefficient factors are combined, and
         these terms are all ordered deterministically according to
         hash values of the non-coefficient parts of the terms.
-        
+
         Example: (2/3)*a*b + c + 1 - (-1/4)*a*b + c + (1/3) ->
                  (4/3) + (11/12) a*b + 2 c
         The order of the terms is arbitrary but deterministic
@@ -300,9 +301,9 @@ def gen_coef_and_remainders(expr):
             return sum_as_expr
         terms = []
         for remainder in sorted(remainder_to_rational_coef.keys(), key=hash):
-            if remainder==one: 
+            if remainder==one:
                 # We'll save the constant (numeric rational) for the
-                # last term (consistent with the 'quick simplified' 
+                # last term (consistent with the 'quick simplified'
                 # form).
                 continue
             coef = remainder_to_rational_coef[remainder].canonical_form()
@@ -325,8 +326,8 @@ def gen_coef_and_remainders(expr):
             return terms[0]
         else:
             return Add(*terms)
-        
-        return Add(*sorted([operand.canonical_form() for operand 
+
+        return Add(*sorted([operand.canonical_form() for operand
                            in self.operands.entries], key=hash))
 
     @prover
@@ -386,7 +387,7 @@ def conversion_to_multiplication(self, **defaults_config):
         from proveit.numbers.multiplication import (
             mult_def_rev, repeated_addition_to_mult)
         operands = self.operands
-        if (operands.num_entries() == 1 
+        if (operands.num_entries() == 1
                 and isinstance(operands[0], ExprRange)
                 and operands[0].is_parameter_independent):
             expr_range = operands[0]
@@ -426,7 +427,7 @@ def cancelations(self, **defaults_config):
         from proveit.numbers import Neg
         expr = self
 
-        # A convenience to allow successive update to the equation via 
+        # A convenience to allow successive update to the equation via
         # transitivities. (starting with self=self).
         eq = TransRelUpdater(self)
 
@@ -434,7 +435,7 @@ def cancelations(self, **defaults_config):
         for _i, operand in enumerate(self.operands.entries):
             if isinstance(operand, Neg):
                 neg_operand_indices.setdefault(
-                        operand.operand.canonical_form(), 
+                        operand.operand.canonical_form(),
                         OrderedSet()).add(_i)
 
         canceled_indices = []
@@ -448,13 +449,13 @@ def cancelations(self, **defaults_config):
                 if len(indices) == 0:
                     # no more indices to use in the future
                     neg_operand_indices.pop(operand_cf)
-                # By finding where i and j will be inserted into the 
-                # canceled_indices array, we can figure out how much 
-                # they need to shift by to compensate for previous 
+                # By finding where i and j will be inserted into the
+                # canceled_indices array, we can figure out how much
+                # they need to shift by to compensate for previous
                 # cancelations.
                 i_shift = bisect.bisect_left(canceled_indices, _i)
                 j_shift = bisect.bisect_left(canceled_indices, _j)
-                # Insert the last one first so we don't need to 
+                # Insert the last one first so we don't need to
                 # compensate:
                 if _i < _j:
                     canceled_indices.insert(j_shift, _j)
@@ -505,7 +506,7 @@ def cancelation(self, idx1, idx2, **defaults_config):
 
         if self.operands.is_double():
             return basic_thm.instantiate({a:_a, b:_b})
-        elif (not self.operands.contains_range() 
+        elif (not self.operands.contains_range()
                 and self.operands.num_entries() == 3):
             # _k is the 3rd index, completing i and j in the set {0,1,2}.
             _k = {0, 1, 2}.difference([idx1, idx2]).pop()
@@ -522,7 +523,7 @@ def cancelation(self, idx1, idx2, **defaults_config):
             return general_thm.instantiate(
                 {i: _i, j: _j, k: _k, a: _a, b: _b, c: _c, d: _d, e: _e})
 
-    @equality_prover('eliminated_zeros', 'eliminate_zeros')   
+    @equality_prover('eliminated_zeros', 'eliminate_zeros')
     def zero_eliminations(self, **defaults_config):
         '''
         Derive and return this Add expression equal to a form in which
@@ -579,24 +580,24 @@ def shallow_simplification(self, *, must_evaluate=False,
         '''
         Returns a proven simplification equation for this Add
         expression assuming the operands have been simplified.
-        
+
         Perform a number of possible simplifications of an Add
-        expression after the operands have been simplified.  
+        expression after the operands have been simplified.
         Disassociate grouped terms, eliminate zero terms,
         cancel common terms that are subtracted, combine like terms,
         convert repeated addition to multiplication, etc.
         '''
-        from proveit.numbers import (one, Add, Div, Neg, Mult, 
+        from proveit.numbers import (one, Add, Div, Neg, Mult,
                                      is_numeric_int,
                                      is_numeric_rational)
         from proveit.numbers.multiplication.mult import (
                 coefficient_and_remainder)
         from . import empty_addition, unary_add_reduction
-        
+
         if self.operands.num_entries() == 0:
             # [+]() = 0
             return empty_addition
-        
+
         if self.operands.is_single():
             return unary_add_reduction.instantiate({a:self.operands[0]},
                                                     preserve_all=True)
@@ -608,11 +609,11 @@ def shallow_simplification(self, *, must_evaluate=False,
             neg_distribution = negated.distribution(auto_simplify=False)
             neg_factored = neg_distribution.derive_reversed()
             return neg_factored.inner_expr().rhs.simplify()
-        
+
         expr = self
         # for convenience updating our equation
         eq = TransRelUpdater(expr)
-        
+
         if Add._simplification_directives_.ungroup:
             # ungroup the expression (disassociate nested additions).
             _n = 0
@@ -637,7 +638,7 @@ def shallow_simplification(self, *, must_evaluate=False,
                             _n, preserve_all=True))
                 length = expr.operands.num_entries()
                 _n += 1
-        
+
         # See if there are any parameter-independent expression
         # ranges to be converted to multiplication:
         #  x + x + ..(n-3)x.. + x = x*n
@@ -679,7 +680,7 @@ def shallow_simplification(self, *, must_evaluate=False,
         # If all operands are irreducible, perform the evaluation.
         terms = self.terms
         if all(is_irreducible_value(term) for term in terms):
-            if self.operands.is_double():                
+            if self.operands.is_double():
                 if all(is_numeric_int(term) for term in terms):
                     # Evaluate the addition of two literal integers.
                     evaluation = self._integer_binary_eval()
@@ -694,7 +695,7 @@ def shallow_simplification(self, *, must_evaluate=False,
                     # appropriate.
                     pass
             else:
-                # Do a pairwise addition of irreducible terms.         
+                # Do a pairwise addition of irreducible terms.
                 return pairwise_evaluation(self)
 
         order_key_fn = Add._simplification_directives_.order_key_fn
@@ -713,14 +714,14 @@ def shallow_simplification(self, *, must_evaluate=False,
                     else coefficient_and_remainder(term)[1])
             # Sort and combine like operands.
             expr = eq.update(sorting_and_combining_like_operands(
-                    expr, order_key_fn=order_key_fn, 
+                    expr, order_key_fn=order_key_fn,
                     likeness_key_fn=likeness_key_fn,
                     preserve_likeness_keys=True, auto_simplify=True))
         else:
             # See if we should reorder the terms.
             expr = eq.update(sorting_operands(expr, order_key_fn=order_key_fn,
                                               auto_simplify=False))
-        
+
         if expr != self:
             # Try starting over with a call to shallow_simplification
             # (an evaluation may already be known).
@@ -732,7 +733,7 @@ def shallow_simplification(self, *, must_evaluate=False,
             raise NotImplementedError(
                 "Addition evaluation only implemented for rationals: %s"
                 %self)
-        
+
         if must_evaluate:
             # The simplification of the operands may not have
             # worked hard enough.  Let's work harder if we
@@ -744,7 +745,7 @@ def shallow_simplification(self, *, must_evaluate=False,
             # irreductible values.
             return self.evaluation()
         return eq.relation
-    
+
     def quick_simplified(self):
         '''
         Return a simplified version of this Add expression
@@ -756,13 +757,13 @@ def quick_simplified(self):
         and expression ranges are well-formed.
         This quick-n-dirty approach can be good
         enough for the purposes of displaying expressions involving
-        expression ranges.  See also the quick_simplified_index 
+        expression ranges.  See also the quick_simplified_index
         function defined in number_operation.py.
         '''
         from proveit.numbers import is_numeric_int, num, Neg
-        
-        # Extract any literal integers and expand nested sums.  
-        # While we are at it, determing the extremal shifts at the 
+
+        # Extract any literal integers and expand nested sums.
+        # While we are at it, determing the extremal shifts at the
         # heads and tails of expression ranges.
         terms = [] # split into sign, abs_term pairs
         int_sum = 0
@@ -773,7 +774,7 @@ def quick_simplified(self):
         # Map non-shiftted tail to earliest shift of ExprRange terms:
         earliest_tail_shift = dict() # (Lambda, index expr) -> int
         all_abs_terms = set()
-        
+
         def expandable_range(term):
             if not isinstance(term, ExprRange):
                 return False
@@ -784,7 +785,7 @@ def expandable_range(term):
             if isinstance(term.body, Add) or  isinstance(term.body, Neg):
                 return False
             return True
-            
+
         while len(remaining_terms):
             term = remaining_terms.popleft()
             if term == Neg:
@@ -818,7 +819,7 @@ def expandable_range(term):
             all_abs_terms.add(term)
             terms.append((sign, term))
         term = None
-        
+
         # Extend the "latest heads" and "earliest tails" if there
         # are individual terms that match them so we can maximize
         # cancelations.
@@ -837,7 +838,7 @@ def expandable_range(term):
                     else:
                         break
                 extremal_shifts[(lambda_map, base)] = shift
-        
+
         # Expand ExprRange heads and tails to their extremal shifts
         if len(latest_head_shift) > 0:
             old_terms = terms
@@ -862,9 +863,9 @@ def expandable_range(term):
                         continue # already expanded all elements.
                     start_index = Add(start_base,
                                       num(latest_start)).quick_simplified()
-                    end_index = Add(end_base, 
+                    end_index = Add(end_base,
                                     num(earliest_end)).quick_simplified()
-                    terms.append((sign, ExprRange(abs_term.parameter, 
+                    terms.append((sign, ExprRange(abs_term.parameter,
                                                   abs_term.body,
                                                   start_index, end_index)))
                     shift = earliest_end + 1
@@ -875,7 +876,7 @@ def expandable_range(term):
                         shift += 1
                 else:
                     terms.append((sign, abs_term))
-        
+
         # Do cancelations of opposite terms.
         # Also check for clearly empty ExprRanges.
         abs_term_to_neg_idx = dict()
@@ -900,9 +901,9 @@ def expandable_range(term):
                 else:
                     # Store for possible cancelation ahead.
                     abs_term_to_pos_idx[abs_term] = idx
-        terms = [term for idx, term in enumerate(terms) if 
+        terms = [term for idx, term in enumerate(terms) if
                  idx not in cancelation_indices]
-        
+
         # Re-extend heads and tails of ExprRanges that may have been
         # split apart, now that we've had a chance to find cancelations.
         if len(latest_head_shift) > 0 and len(terms) > 0:
@@ -912,39 +913,39 @@ def expandable_range(term):
             following_term = None
             for sign, abs_term in reversed(old_terms):
                 if following_term is not None:
-                    if (sign==following_term[0] 
+                    if (sign==following_term[0]
                             and expandable_range(following_term[1])):
                         # See if the current term prepends the head of
                         # the following range.
                         start_base, start_shift = split_int_shift(
                                 following_term[1].true_start_index)
                         lambda_map = following_term[1].lambda_map
-                        index = Add(start_base, 
+                        index = Add(start_base,
                                     num(start_shift-1)).quick_simplified()
                         if abs_term == lambda_map.apply(index):
                             # Prepend the head.
                             following_term[1] = ExprRange(
-                                    following_term[1].parameter, 
+                                    following_term[1].parameter,
                                     following_term[1].body,
                                     index, following_term[1].true_end_index)
                             continue
                     reversed_terms.append(following_term)
                 following_term = [sign, abs_term]
             reversed_terms.append(following_term) # get the last one
-            
+
             # Try to extend tails, reversing again.
             terms = []
             prev_term = None
             for sign, abs_term in reversed(reversed_terms):
                 if prev_term is not None:
-                    if (sign==prev_term[0] and 
+                    if (sign==prev_term[0] and
                             expandable_range(prev_term[1])):
                         # See if the current term extends the tail of
                         # the previous range.
                         end_base, end_shift = split_int_shift(
                                 prev_term[1].true_end_index)
                         lambda_map = prev_term[1].lambda_map
-                        index = Add(end_base, 
+                        index = Add(end_base,
                                     num(end_shift+1)).quick_simplified()
                         if abs_term == lambda_map.apply(index):
                             # Extend the tail.
@@ -955,7 +956,7 @@ def expandable_range(term):
                     terms.append(prev_term)
                 prev_term = [sign, abs_term]
             terms.append(prev_term) # get the last one
-        
+
         # Merge the sign and abs_term into each term.
         for idx, (sign, abs_term) in enumerate(terms):
             if sign==1:
@@ -966,7 +967,7 @@ def expandable_range(term):
                     terms[idx] = Neg(Add(abs_term))
                 else:
                     terms[idx] = Neg(abs_term)
-        
+
         if int_sum == 0:
             if len(terms) == 0:
                 return num(0)
@@ -980,10 +981,10 @@ def expandable_range(term):
 
     def quick_simplification(self):
         '''
-        Return a simplification of this Add expression without any 
+        Return a simplification of this Add expression without any
         proof.  See Add.quick_simplified for more details.
         '''
-        return Equals(self, self.quick_simplified())        
+        return Equals(self, self.quick_simplified())
 
     def _integer_binary_eval(self):
         '''
@@ -1034,7 +1035,7 @@ def _rational_binary_eval(self):
         or a (possibly negated) fraction with no common divisors between
         the numerator and denominator other than 1.
         '''
-        from proveit.numbers import (Div, num, is_numeric_rational, 
+        from proveit.numbers import (Div, num, is_numeric_rational,
                                      numeric_rational_ints)
         from . import rational_pair_addition
         terms = self.terms
@@ -1056,7 +1057,7 @@ def _rational_binary_eval(self):
         # numerator and denominator and then reduce it to an
         # irreducible form.
         return rational_pair_addition.instantiate(
-                {a:_a, b:_b, c:_c, d:_d}, 
+                {a:_a, b:_b, c:_c, d:_d},
                 auto_simplify=True, replacements=replacements,
                 preserve_expr=self)
 
@@ -1122,7 +1123,7 @@ def deduce_in_number_set(self, number_set, **defaults_config):
         from proveit.numbers import (zero, one, Neg, greater,
                                      Less, LessEq, greater_eq)
         from proveit.logic import InSet
-        
+
         if number_set == ZeroSet:
             if self.operands.is_double():
                 return add_pkg.add_zero_closure_bin.instantiate(
@@ -1158,7 +1159,7 @@ def deduce_in_number_set(self, number_set, **defaults_config):
             _a = self.operands
             _i = _a.num_elements()
             return add_pkg.add_complex_closure.instantiate({i: _i, a: _a})
-        
+
         # Prove what we can in preparation.
         for operand in self.operands:
             deduce_number_set(operand)
@@ -1172,43 +1173,60 @@ def deduce_in_number_set(self, number_set, **defaults_config):
                     return add_pkg.add_nat_closure_bin.instantiate(
                         {a: self.operands[0], b: self.operands[1]})
                 _a = self.operands
-                _i = _a.num_elements()                
-                add_pkg.add_nat_closure.instantiate({i: _i, a: _a})
+                _i = _a.num_elements()
+                return add_pkg.add_nat_closure.instantiate({i: _i, a: _a})
             if number_set == NaturalPos:
                 if self.operands.is_double():
                     return add_pkg.add_nat_pos_closure_bin.instantiate(
                         {a: self.operands[0], b: self.operands[1]})
                 _a = self.operands
-                _i = _a.num_elements()                
-                add_pkg.add_nat_pos_closure.instantiate({i: _i, a: _a})
+                _i = _a.num_elements()
+                return add_pkg.add_nat_pos_closure.instantiate({i: _i, a: _a})
             if number_set == IntegerNeg:
                 if self.operands.is_double():
                     return add_pkg.add_int_neg_closure_bin.instantiate(
                         {a: self.operands[0], b: self.operands[1]})
                 _a = self.operands
-                _i = _a.num_elements()                
-                add_pkg.add_int_neg_closure.instantiate({i: _i, a: _a})
+                _i = _a.num_elements()
+                return add_pkg.add_int_neg_closure.instantiate({i: _i, a: _a})
             if number_set == IntegerNonPos:
                 if self.operands.is_double():
                     return add_pkg.add_int_nonpos_closure_bin.instantiate(
                         {a: self.operands[0], b: self.operands[1]})
                 _a = self.operands
-                _i = _a.num_elements()                
-                add_pkg.add_int_nonpos_closure.instantiate({i: _i, a: _a})
+                _i = _a.num_elements()
+                return (add_pkg.add_int_nonpos_closure
+                        .instantiate({i: _i, a: _a}))
+            if number_set == IntegerEven:
+                if self.operands.is_double():
+                    return add_pkg.add_int_even_closure_bin.instantiate(
+                        {a: self.operands[0], b: self.operands[1]})
+                _a = self.operands
+                _i = _a.num_elements()
+                return add_pkg.add_int_even_closure.instantiate({i: _i, a: _a})
+            if number_set == IntegerOdd:
+                if (InSet(self.operands.num_elements(), IntegerOdd)
+                        .readily_provable()):
+                    _a =  self.operands
+                    _i = _a.num_elements()
+                    return (add_pkg.add_int_odd_from_odd_odd
+                            .instantiate({i: _i, a: _a}))
             if number_set == RationalPos:
                 if self.operands.is_double():
                     return add_pkg.add_rational_pos_closure_bin.instantiate(
                         {a: self.operands[0], b: self.operands[1]})
                 _a = self.operands
-                _i = _a.num_elements()                
-                add_pkg.add_rational_pos_closure.instantiate({i: _i, a: _a})
+                _i = _a.num_elements()
+                return (add_pkg.add_rational_pos_closure
+                        .instantiate({i: _i, a: _a}))
             if number_set == RationalNeg:
                 if self.operands.is_double():
                     return add_pkg.add_rational_neg_closure_bin.instantiate(
                         {a: self.operands[0], b: self.operands[1]})
                 _a = self.operands
-                _i = _a.num_elements()                
-                add_pkg.add_rational_neg_closure.instantiate({i: _i, a: _a})
+                _i = _a.num_elements()
+                return (add_pkg.add_rational_neg_closure
+                        .instantiate({i: _i, a: _a}))
             if number_set == RationalNonNeg:
                 if self.operands.is_double():
                     return add_pkg.add_rational_nonneg_closure_bin.instantiate(
@@ -1228,14 +1246,14 @@ def deduce_in_number_set(self, number_set, **defaults_config):
                     return add_pkg.add_real_pos_closure_bin.instantiate(
                         {a: self.operands[0], b: self.operands[1]})
                 _a = self.operands
-                _i = _a.num_elements()                
+                _i = _a.num_elements()
                 return add_pkg.add_real_pos_closure.instantiate({i: _i, a: _a})
             if number_set == RealNeg:
                 if self.operands.is_double():
                     return add_pkg.add_real_neg_closure_bin.instantiate(
                         {a: self.operands[0], b: self.operands[1]})
                 _a = self.operands
-                _i = _a.num_elements()                
+                _i = _a.num_elements()
                 return add_pkg.add_real_neg_closure.instantiate({i: _i, a: _a})
             if number_set == RealNonNeg:
                 if self.operands.is_double():
@@ -1252,6 +1270,37 @@ def deduce_in_number_set(self, number_set, **defaults_config):
                 _i = _a.num_elements()
                 return add_pkg.add_real_nonpos_closure.instantiate({i:_i, a: _a})
 
+        # Handle special even/odd integer cases:
+        # (1) even + odd = odd, odd + even = odd
+        # (2) sum of even number of odds is even
+        # Note: case of odd number of odds (being odd) handled earlier.
+        if number_set == IntegerOdd:
+            if self.operands.is_double():
+                if (InSet(self.operands[0], IntegerEven).readily_provable()
+                    and
+                    InSet(self.operands[1], IntegerOdd).readily_provable()):
+                    _a = self.operands[0]
+                    _b = self.operands[1]
+                    return (add_pkg.add_int_odd_from_even_and_odd_bin
+                            .instantiate({a: _a, b: _b}))
+                if (InSet(self.operands[0], IntegerOdd).readily_provable()
+                    and
+                    InSet(self.operands[1], IntegerEven).readily_provable()):
+                    _a = self.operands[0]
+                    _b = self.operands[1]
+                    return (add_pkg.add_int_odd_from_odd_and_even_bin
+                            .instantiate({a: _a, b: _b}))
+        if number_set == IntegerEven:
+            if (all(InSet(operand, IntegerOdd).proven() for
+                    operand in self.operands)
+                and InSet(self.operands.num_elements(), IntegerEven)
+                    .readily_provable()):
+                _a = self.operands
+                _i = self.operands.num_elements()
+                return (add_pkg.add_int_even_from_even_odd
+                        .instantiate({i:_i, a:_a}))
+
+
         # Try special case where one term is positive and the
         # rest are non-negative.
         if number_set in {NaturalPos, RationalPos, RealPos}:
@@ -1330,7 +1379,7 @@ def deduce_in_number_set(self, number_set, **defaults_config):
                                    RealNeg:Real}
             major_number_set = to_major_number_set[number_set]
         if number_set in {IntegerNonPos, RationalNonPos, RealNonPos}:
-            # Prove it is non-positive and prove it is in the 
+            # Prove it is non-positive and prove it is in the
             # corresponding major number set (Integer, Rational, Real)
             self.deduce_non_positive(try_deduce_number_set=False)
             to_major_number_set = {IntegerNonPos:Integer,
@@ -1338,7 +1387,7 @@ def deduce_in_number_set(self, number_set, **defaults_config):
                                    RealNonPos:Real}
             major_number_set = to_major_number_set[number_set]
         if number_set in {Natural, RationalNonNeg, RealNonNeg}:
-            # Prove it is non-positive and prove it is in the 
+            # Prove it is non-positive and prove it is in the
             # corresponding major number set (Integer, Rational, Real)
             self.deduce_non_negative(try_deduce_number_set=False)
             to_major_number_set = {Natural:Integer,
@@ -1366,7 +1415,7 @@ def deduce_positive(self, *, try_deduce_number_set=True,
         if greater(self, zero).proven():
             # Already known (don't use readily_provable).
             return greater(self, zero).prove()
-        
+
         if self.terms.is_double() and isinstance(self.terms[1], Neg):
             # (a - b) with a > b => (a - b) is positive
             _a, _b = self.terms[0], self.terms[1].operand
@@ -1392,7 +1441,7 @@ def deduce_negative(self, *, try_deduce_number_set=True,
         if Less(self, zero).proven():
             # Already known (don't use readily_provable).
             return Less(self, zero).prove()
-        
+
         if self.terms.is_double() and isinstance(self.terms[1], Neg):
             # (a - b) with a < b => (a - b) is negative
             _a, _b = self.terms[0], self.terms[1].operand
@@ -1417,7 +1466,7 @@ def deduce_non_positive(self, *, try_deduce_number_set=True,
         if LessEq(self, zero).proven():
             # Already known (don't use readily_provable).
             return LessEq(self, zero).prove()
-        
+
         if self.terms.is_double() and isinstance(self.terms[1], Neg):
             # (a - b) with a <= b => (a - b) is non-positive
             _a, _b = self.terms[0], self.terms[1].operand
@@ -1442,7 +1491,7 @@ def deduce_non_negative(self, *, try_deduce_number_set=True,
         if greater_eq(self, zero).proven():
             # Already known (don't use readily_provable).
             return greater_eq(self, zero).prove()
-        
+
         if self.terms.is_double() and isinstance(self.terms[1], Neg):
             # (a - b) with a >= b => (a - b) is non-negative
             _a, _b = self.terms[0], self.terms[1].operand
@@ -1482,9 +1531,9 @@ def readily_provable_number_set(self):
             if RealNeg.readily_includes(operand_ns):
                 any_negative = True
             list_of_operand_sets.append(operand_ns)
-        # Find the number set that 
+        # Find the number set that
         number_set = union_number_set(*list_of_operand_sets)
-        
+
         if number_set in nonzero_to_full_number_set:
             # Adding non-zero operands provides no guarantee that the
             # result will be non-zero (unlike adding positives,
@@ -1522,7 +1571,7 @@ def readily_provable_number_set(self):
                     restriction = nonpos_number_set # non-positive
                 elif NotEquals(_a, _b).readily_provable():
                     restriction = nonzero_number_set # non-zero
-        
+
         # Use the positive, negative, non-negative, non-positive, or
         # non-zero restriction.
         if restriction is not None:
@@ -1564,15 +1613,15 @@ def deduce_difference_in_natural_pos(self, **defaults_config):
 
     def index(self, the_term, also_return_num=False):
         '''
-        Return the starting index of the_term, which may be a single 
-        operand, a list of consecutive operands, or a Add expression 
+        Return the starting index of the_term, which may be a single
+        operand, a list of consecutive operands, or a Add expression
         that represents the sum of the list of consecutive operands.
-        If also_return_num is True, return a tuple of the index and 
+        If also_return_num is True, return a tuple of the index and
         number of operands for the_term.
         '''
         if isinstance(the_term, Add):
             the_term = the_term.operands.entries
-        if (hasattr(the_term, '__getitem__') and 
+        if (hasattr(the_term, '__getitem__') and
                 hasattr(the_term, '__len__')):
             # multiple operands in the_term
             first_term = the_term[0]
@@ -1608,11 +1657,11 @@ def readily_factorable(self, factor):
         return True
 
     @auto_equality_prover('factorized', 'factor')
-    def factorization(self, the_factors, pull="left", 
+    def factorization(self, the_factors, pull="left",
                       group_factors=True, group_remainder=True,
                       **defaults_config):
         '''
-        Factor out the factor(s) from this sum, pulling it either to 
+        Factor out the factor(s) from this sum, pulling it either to
         the "left" or "right".
         If group_factors is True, the factors are grouped
         together as a sub-product.
@@ -1681,11 +1730,11 @@ def factorization(self, the_factors, pull="left",
         _j = _b.num_elements()
         _k = _c.num_elements()
         distribution = distribute_through_sum.instantiate(
-            {i: _i, j: _j, k: _k, a: _a, b: _b, c: _c}, 
+            {i: _i, j: _j, k: _k, a: _a, b: _b, c: _c},
             preserve_expr=expr, replacements=replacements)
         eq.update(distribution.derive_reversed())
         return eq.relation
-    
+
     @equality_prover('combined_terms', 'combine_terms')
     def combining_terms(self, **defaults_config):
         '''
@@ -1745,13 +1794,13 @@ def combining_operands(self, **defaults_config):
         Combine terms, adding their literal, rational coeffiicents.
         Alias for `combining_terms`.
         '''
-        return self.combining_terms() 
+        return self.combining_terms()
 
     @equality_prover('commuted', 'commute')
-    def commutation(self, init_idx=None, final_idx=None, 
+    def commutation(self, init_idx=None, final_idx=None,
                     **defaults_config):
         '''
-        Given numerical operands, deduce that this expression is equal 
+        Given numerical operands, deduce that this expression is equal
         to a form in which the operand
         at index init_idx has been moved to final_idx.
         For example, (a + b + ... + y + z) = (a + ... + y + b + z)
@@ -1761,16 +1810,16 @@ def commutation(self, init_idx=None, final_idx=None,
         return apply_commutation_thm(
             self, init_idx, final_idx, commutation,
             leftward_commutation, rightward_commutation)
-    
+
     @equality_prover('group_commuted', 'group_commute')
     def group_commutation(self, init_idx, final_idx, length,
                           disassociate=True, **defaults_config):
         '''
         Given numerical operands, deduce that this expression is equal
-        to a form in which the operands at indices 
-        [init_idx, init_idx+length) have been moved to 
+        to a form in which the operands at indices
+        [init_idx, init_idx+length) have been moved to
         [final_idx. final_idx+length).
-        It will do this by performing association first.  
+        It will do this by performing association first.
         If disassocate is True, it will be disassociated afterwards.
         '''
         return group_commutation(
@@ -1780,7 +1829,7 @@ def group_commutation(self, init_idx, final_idx, length,
     def permutation_move(self, init_idx=None, final_idx=None,
                          **defaults_config):
         '''
-        Given numerical operands, deduce that this expression is equal 
+        Given numerical operands, deduce that this expression is equal
         to a form in which the operand
         at index init_idx has been moved to final_idx.
         For example, (a + b + ... + y + z) = (a + ... + y + b + z)
@@ -1805,10 +1854,10 @@ def permutation(self, new_order=None, cycles=None, **defaults_config):
     @equality_prover('associated', 'associate')
     def association(self, start_idx, length, **defaults_config):
         '''
-        Given numerical operands, deduce that this expression is equal 
+        Given numerical operands, deduce that this expression is equal
         to a form in which operands in the
         range [start_idx, start_idx+length) are grouped together.
-        For example, (a + b + ... + y + z) = 
+        For example, (a + b + ... + y + z) =
             (a + b ... + (l + ... + m) + ... + y + z)
         '''
         from . import association
@@ -1831,10 +1880,10 @@ def association(self, start_idx, length, **defaults_config):
     @equality_prover('disassociated', 'disassociate')
     def disassociation(self, idx, **defaults_config):
         '''
-        Given numerical operands, deduce that this expression is equal 
+        Given numerical operands, deduce that this expression is equal
         to a form in which the operand
         at index idx is no longer grouped together.
-        For example, (a + b ... + (l + ... + m) + ... + y+ z) 
+        For example, (a + b ... + (l + ... + m) + ... + y+ z)
             = (a + b + ... + y + z)
         '''
         from proveit.core_expr_types import Len
@@ -1884,7 +1933,7 @@ def bound_via_term_bound(self, term_relation, **defaults_config):
         one of its terms.  For example
             a + b + c + d < a + z + c + d   given   b < z.
 
-        Also see NumberOperation.deduce_bound.            
+        Also see NumberOperation.deduce_bound.
         '''
         from proveit.numbers import NumberOrderingRelation, Less
         if isinstance(term_relation, Judgment):
@@ -1935,12 +1984,12 @@ def bound_via_term_bound(self, term_relation, **defaults_config):
             expr = eq.update(thm.instantiate(
                     {i: _i, j: _j, a: _a, b: _b, x: _x, y: _y}))
         if num > 1 and isinstance(expr.terms[idx], Add):
-            expr = eq.update(expr.disassociation(idx))            
+            expr = eq.update(expr.disassociation(idx))
         relation = eq.relation
         if relation.lhs != self:
             relation = relation.with_direction_reversed()
         assert relation.lhs == self
-        return relation    
+        return relation
 
     @relation_prover
     def bound_by_term(self, term_or_idx, use_weak_bound=False,
@@ -1948,10 +1997,10 @@ def bound_by_term(self, term_or_idx, use_weak_bound=False,
         '''
         Deduce that this sum is bound by the given term (or term at
         the given index).
-        
+
         For example,
         a + b + c + d ≥ b provided that a ≥ 0, c ≥ 0, and d ≥ 0.
-        
+
         To use this method, we must know that the
         other terms are all in RealPos, RealNeg, RealNonNeg, or
         RealNonPos and will call
@@ -1964,7 +2013,7 @@ def bound_by_term(self, term_or_idx, use_weak_bound=False,
         from proveit.numbers import RealPos, RealNeg, RealNonNeg, RealNonPos
         relevant_number_sets = {RealPos, RealNeg, RealNonNeg, RealNonPos}
         for _k, term_entry in enumerate(self.terms.entries):
-            if _k == term_or_idx or term_entry == term_or_idx: 
+            if _k == term_or_idx or term_entry == term_or_idx:
                 # skip the term doing the bounding.
                 continue
             for number_set in list(relevant_number_sets):
@@ -1972,7 +2021,7 @@ def bound_by_term(self, term_or_idx, use_weak_bound=False,
                     in_number_set = And(ExprRange(
                             term_entry.parameter,
                             InSet(term_entry.body, number_set),
-                            term_entry.true_start_index, 
+                            term_entry.true_start_index,
                             term_entry.true_end_index))
                 else:
                     in_number_set = InSet(term_entry, number_set)
@@ -2040,10 +2089,10 @@ def deduce_strong_upper_bound_by_term(
 
     def _deduce_specific_bound_by_term(self, thm, term_or_idx):
         '''
-        Helper method for 
+        Helper method for
         deduce_weak_lower_bound_by_term,
-        deduce_weak_upper_bound_by_term, 
-        deduce_strong_lower_bound_by_term, and 
+        deduce_weak_upper_bound_by_term,
+        deduce_strong_lower_bound_by_term, and
         deduce_strong_lower_bound_by_term.
         '''
         if isinstance(term_or_idx, Expression):
@@ -2063,7 +2112,7 @@ def _deduce_specific_bound_by_term(self, thm, term_or_idx):
         _c = self.terms[idx + 1:]
         _i = _a.num_elements()
         _j = _c.num_elements()
-        return thm.instantiate({i: _i, j: _j, a: _a, b: _b, c: _c})        
+        return thm.instantiate({i: _i, j: _j, a: _a, b: _b, c: _c})
 
     @relation_prover
     def deduce_not_equal(self, other, *, try_deduce_number_set=True,
@@ -2164,7 +2213,7 @@ def split_int_shift(expr):
             shift = expr.terms[-1].as_int()
             if expr.terms.num_entries() == 1:
                 return shift
-            elif (expr.terms.num_entries() == 2 and 
+            elif (expr.terms.num_entries() == 2 and
                     not isinstance(expr.terms[0], ExprRange)):
                 return expr.terms[0], shift
             else:
diff --git a/packages/proveit/numbers/multiplication/_theory_nbs_/proofs/mult_int_even_from_ints_with_any_even/thm_proof.ipynb b/packages/proveit/numbers/multiplication/_theory_nbs_/proofs/mult_int_even_from_ints_with_any_even/thm_proof.ipynb
new file mode 100644
index 000000000000..3d6b481f29bd
--- /dev/null
+++ b/packages/proveit/numbers/multiplication/_theory_nbs_/proofs/mult_int_even_from_ints_with_any_even/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">multiplication</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#mult_int_even_from_ints_with_any_even\">mult_int_even_from_ints_with_any_even</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving mult_int_even_from_ints_with_any_even"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/multiplication/_theory_nbs_/proofs/mult_int_even_from_left_even_bin/thm_proof.ipynb b/packages/proveit/numbers/multiplication/_theory_nbs_/proofs/mult_int_even_from_left_even_bin/thm_proof.ipynb
new file mode 100644
index 000000000000..9e76d8fc8e1c
--- /dev/null
+++ b/packages/proveit/numbers/multiplication/_theory_nbs_/proofs/mult_int_even_from_left_even_bin/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">multiplication</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#mult_int_even_from_left_even_bin\">mult_int_even_from_left_even_bin</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving mult_int_even_from_left_even_bin"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/multiplication/_theory_nbs_/proofs/mult_int_even_from_right_even_bin/thm_proof.ipynb b/packages/proveit/numbers/multiplication/_theory_nbs_/proofs/mult_int_even_from_right_even_bin/thm_proof.ipynb
new file mode 100644
index 000000000000..8ef661ba851e
--- /dev/null
+++ b/packages/proveit/numbers/multiplication/_theory_nbs_/proofs/mult_int_even_from_right_even_bin/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">multiplication</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#mult_int_even_from_right_even_bin\">mult_int_even_from_right_even_bin</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving mult_int_even_from_right_even_bin"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/multiplication/_theory_nbs_/proofs/mult_int_odd_from_all_odd/thm_proof.ipynb b/packages/proveit/numbers/multiplication/_theory_nbs_/proofs/mult_int_odd_from_all_odd/thm_proof.ipynb
new file mode 100644
index 000000000000..6cc62e92dbd7
--- /dev/null
+++ b/packages/proveit/numbers/multiplication/_theory_nbs_/proofs/mult_int_odd_from_all_odd/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">multiplication</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#mult_int_odd_from_all_odd\">mult_int_odd_from_all_odd</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving mult_int_odd_from_all_odd"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/multiplication/_theory_nbs_/theorems.ipynb b/packages/proveit/numbers/multiplication/_theory_nbs_/theorems.ipynb
index b344f8f2f303..e5a79ed62896 100644
--- a/packages/proveit/numbers/multiplication/_theory_nbs_/theorems.ipynb
+++ b/packages/proveit/numbers/multiplication/_theory_nbs_/theorems.ipynb
@@ -26,11 +26,12 @@
     "from proveit.core_expr_types import (\n",
     "    a_1_to_i, a_1_to_n, b_1_to_j, c_1_to_j, c_1_to_k, d_1_to_k)\n",
     "\n",
-    "from proveit.logic import Or, Forall, Set, InSet, Equals, NotEquals, Implies, Boolean\n",
-    "from proveit.numbers import (ZeroSet, Natural, NaturalPos, Integer, IntegerNonZero, IntegerNeg, IntegerNonPos,\n",
-    "                             Rational, RationalPos, RationalNeg, RationalNonNeg, RationalNonPos, RationalNonZero,\n",
-    "                             Real, RealPos, RealNeg, RealNonNeg, RealNonPos, RealNonZero,\n",
-    "                             Complex, ComplexNonZero)\n",
+    "from proveit.logic import And, Or, Forall, Set, InSet, Equals, NotEquals, Implies, Boolean\n",
+    "from proveit.numbers import (ZeroSet, Natural, NaturalPos, Integer, IntegerEven,\n",
+    "        IntegerNonZero, IntegerNeg, IntegerNonPos, IntegerOdd,\n",
+    "        Rational, RationalPos, RationalNeg, RationalNonNeg, RationalNonPos, RationalNonZero,\n",
+    "        Real, RealPos, RealNeg, RealNonNeg, RealNonPos, RealNonZero,\n",
+    "        Complex, ComplexNonZero)\n",
     "from proveit.numbers import (Mult, Neg, Add, subtract, Abs, Sum, num, zero, one, two, Exp,\n",
     "                            Less, LessEq, greater, greater_eq)\n",
     "from proveit.numbers.summation import summation_b1toj_fQ\n",
@@ -247,6 +248,59 @@
     "           domain=Natural)"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "mult_int_even_from_left_even_bin = (\n",
+    "        Forall((a, b), InSet(Mult(a, b), IntegerEven), domains = [IntegerEven, Integer])\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "mult_int_even_from_right_even_bin = (\n",
+    "        Forall((a, b), InSet(Mult(a, b), IntegerEven), domains = [Integer, IntegerEven])\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "mult_int_even_from_ints_with_any_even = (\n",
+    "    Forall(n, \n",
+    "           Forall(a_1_to_n, InSet(Mult(a_1_to_n), IntegerEven),\n",
+    "                  condition=Or(*ExprTuple(a_1_to_n).map_elements(lambda x : InSet(x, IntegerEven))),\n",
+    "                  domain = Integer),\n",
+    "           domain=NaturalPos)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit.logic import And\n",
+    "from proveit.numbers import IntegerOdd\n",
+    "mult_int_odd_from_all_odd = (\n",
+    "    Forall(n, \n",
+    "           Forall(a_1_to_n, InSet(Mult(a_1_to_n), IntegerOdd),\n",
+    "                  domain = IntegerOdd),\n",
+    "           domain=Natural)\n",
+    ")"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
diff --git a/packages/proveit/numbers/number_sets/__init__.py b/packages/proveit/numbers/number_sets/__init__.py
index 953641ce9c77..ab18fc7b3c39 100644
--- a/packages/proveit/numbers/number_sets/__init__.py
+++ b/packages/proveit/numbers/number_sets/__init__.py
@@ -1,6 +1,6 @@
 from .integers import (
-        Integer, IntegerNeg, IntegerNonZero, IntegerNonPos,
-        Interval, infinity)
+        Integer, IntegerEven, IntegerNeg, IntegerNonZero,
+        IntegerNonPos, IntegerOdd, Interval, infinity)
 from .natural_numbers import ZeroSet, Natural, NaturalPos
 from .rational_numbers import (Rational, RationalNonZero,
                                RationalPos, RationalNeg,
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/axioms.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/axioms.ipynb
index f0d3b9a1ee3c..d9470e34406b 100644
--- a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/axioms.ipynb
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/axioms.ipynb
@@ -17,11 +17,11 @@
     "import proveit\n",
     "# Prepare this notebook for defining the axioms of a theory:\n",
     "%axioms_notebook # Keep this at the top following 'import proveit'.\n",
-    "from proveit.logic import And, Equals, Forall, Iff, Implies, InSet, SetEquiv, SetOfAll, SubsetProper, Union\n",
-    "from proveit.numbers import Complex, Integer, Interval, Natural, NaturalPos, Neg\n",
-    "from proveit.numbers import Add, greater, Less, LessEq, number_ordering\n",
-    "from proveit.numbers import zero, one, num\n",
-    "from proveit import a, b, n, m, x, y, S\n",
+    "from proveit.logic import And, Difference, Equals, Forall, Iff, Implies, InSet, SetEquiv, SetOfAll, SubsetProper, Union\n",
+    "from proveit.numbers import Complex, Integer, IntegerEven, IntegerOdd, Interval, Natural, NaturalPos, Neg\n",
+    "from proveit.numbers import Add, greater, Less, LessEq, Mult, number_ordering\n",
+    "from proveit.numbers import zero, one, two, num\n",
+    "from proveit import a, b, n, m, x, y, z, S\n",
     "%begin axioms"
    ]
   },
@@ -80,6 +80,31 @@
     "#add_by_zero = Forall(x, Equals(Add(x, zero), x), domain=Complex)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Define some significant subsets of the integers: Evens, Odds, etc."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "even_integers_def = Equals(IntegerEven, SetOfAll(z, Mult(two, z), domain = Integer))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "odd_integers_def = Equals(IntegerOdd, Difference(Integer, IntegerEven))"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/common.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/common.ipynb
index 0a9a13fb199b..4c5ad859eb51 100644
--- a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/common.ipynb
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/common.ipynb
@@ -18,7 +18,8 @@
     "# Prepare this notebook for defining the common expressions of a theory:\n",
     "%common_expressions_notebook # Keep this at the top following 'import proveit'.\n",
     "from proveit import Literal\n",
-    "from proveit.numbers.number_sets.integers.integer import IntegerSet, IntegerNegSet, IntegerNonZeroSet, IntegerNonPosSet\n",
+    "from proveit.numbers.number_sets.integers.integer import (\n",
+    "        IntegerSet, IntegerEvenSet, IntegerNegSet, IntegerNonZeroSet, IntegerNonPosSet, IntegerOddSet)\n",
     "%begin common"
    ]
   },
@@ -31,6 +32,15 @@
     "Integer = IntegerSet()"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "IntegerEven = IntegerEvenSet()"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -58,6 +68,15 @@
     "IntegerNonPos = IntegerNonPosSet()"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "IntegerOdd = IntegerOddSet()"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/demonstrations.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/demonstrations.ipynb
index ffdc655aabda..8955e4f9810a 100644
--- a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/demonstrations.ipynb
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/demonstrations.ipynb
@@ -15,12 +15,13 @@
    "outputs": [],
    "source": [
     "import proveit\n",
-    "from proveit import a, b, c, d, n, x, ProofFailure\n",
-    "from proveit.logic import Boolean, NotEquals, InSet, NotInSet, Or\n",
-    "from proveit.numbers import Neg, Less, LessEq, greater, greater_eq, number_ordering\n",
+    "from proveit import a, b, c, d, n, x, y, z, ProofFailure\n",
+    "from proveit.logic import Boolean, Equals, Exists, NotEquals, InSet, NotInSet, Or\n",
+    "from proveit.numbers import (Add, Neg, Less, LessEq, greater, greater_eq,\n",
+    "        number_ordering, Mult, num, subtract)\n",
     "from proveit.numbers import (zero, one, two, three, four, five, six, seven,\n",
-    "                             eight, nine, Integer, IntegerNonZero, IntegerNeg, IntegerNonPos,\n",
-    "                             Interval, Natural, NaturalPos)\n",
+    "                             eight, nine, Integer, IntegerEven, IntegerNonZero, IntegerNeg, IntegerNonPos,\n",
+    "                             IntegerOdd, Interval, Natural, NaturalPos, Rational, Real)\n",
     "%begin demonstrations"
    ]
   },
@@ -769,13 +770,424 @@
     "InSet(x, Integer).prove(assumptions=[InSet(x, IntegerNonPos)])"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Even Integers (`IntegerEven`), Even Integer Membership (`IntegerEvenMembership`), and superset/subsets of even integers"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(x, IntegerEven)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(InSet(x, IntegerEven), Boolean).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit.numbers import Complex\n",
+    "InSet(x, Complex).prove(assumptions=[InSet(x, IntegerEven)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(x, Integer).prove(assumptions=[InSet(x, IntegerEven)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(x, Rational).prove(assumptions=[InSet(x, IntegerEven)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(x, Real).prove(assumptions=[InSet(x, IntegerEven)])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The positive single-digit even integers are “known” (as automatically imported theorems) to be members of the IntegerEven set:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(zero, IntegerEven).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(two, IntegerEven).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(four, IntegerEven).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(six, IntegerEven).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(eight, IntegerEven).prove()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "More generally, $x$ is an even integer if and only if there exists an integer $z$ such that $x = 2z$:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Exists(z, Equals(x, Mult(two, z)), domain = Integer).prove(assumptions=[InSet(x, IntegerEven)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(x, IntegerEven).prove(assumptions = [Exists(z, Equals(x, Mult(two, z)), domain = Integer)])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "If we know that $10$ can be expressed as $2\\cdot 5$:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Equals(Mult(two, five), num(10)).prove()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "that isn't quite enough by itself in the current system to prove automatically that $10$ is an even integer:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "try:\n",
+    "    InSet(num(10), IntegerEven).prove()\n",
+    "except Exception as the_exception:\n",
+    "    print(f\"Exception: {the_exception}\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "and we cannot even jump to automatically proving that there then exists an integer $z$ such that $10 = 2z$:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "try:\n",
+    "    Exists(z, Equals(num(10), Mult(two, z)), domain = Integer).prove()\n",
+    "except Exception as the_exception:\n",
+    "    print(f\"Exception: {the_exception}\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "but we can manually prompt Prove-It to prove that there exists an integer z such that $10 = 2z$, using the `conclude_via_example()` method:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Exists(z, Equals(num(10), Mult(two, z)), domain = Integer).conclude_via_example(five)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "after which we can automatically prove that $10$ is even:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(num(10), IntegerEven).prove()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We can do this more generally as well. Let's assume that $k = 2z$ for some integer $z$, without explicitly specifying the value of $z$:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit import k\n",
+    "assumption_about_k = Equals(k, Mult(two, z))\n",
+    "assumption_about_z = InSet(z, Integer)\n",
+    "display(assumption_about_k)\n",
+    "display(assumption_about_z)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Then we can derive the existential claim, using `conclude_by_example`::"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Exists(z, Equals(k, Mult(two, z)), domain = Integer).conclude_via_example(z, assumptions = [assumption_about_k, assumption_about_z])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "After which, we can prove the set membership:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(k, IntegerEven).prove(assumptions = [assumption_about_k, assumption_about_z])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "##### Closure of the even integers under addition."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Add(a, b).deduce_in_number_set(IntegerEven, assumptions = [InSet(a, IntegerEven), InSet(b, IntegerEven)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Add(a, b, c).deduce_in_number_set(\n",
+    "    IntegerEven, assumptions = [InSet(a, IntegerEven), InSet(b, IntegerEven), InSet(c, IntegerEven)])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We cannot yet automatically and directly prove that the sum of evens is even"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit import d, e\n",
+    "assumption_about_d = InSet(d, IntegerEven)\n",
+    "assumption_about_e = InSet(e, IntegerEven)\n",
+    "InSet(Add(d, e), IntegerEven).prove(assumptions = [assumption_about_d, assumption_about_e])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Multiplication with even integers.\n",
+    "\n",
+    "In a product of integers, if any of the operands is even, the product is even."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(Mult(a, b), IntegerEven).prove(assumptions = [InSet(a, IntegerEven), InSet(b, Integer)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(Mult(a, b), IntegerEven).prove(assumptions = [InSet(a, Integer), InSet(b, IntegerEven)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit import a, b, c, d, e, f \n",
+    "temp_assumptions = [InSet(a, Integer), InSet(b, Integer), InSet(c, IntegerEven),\n",
+    "                    InSet(d, Integer), InSet(e, Integer), InSet(f, Integer)]\n",
+    "InSet(Mult(a, b, c, d, e, f), IntegerEven).prove(assumptions = temp_assumptions)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The negation of an even is still even."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(Neg(a), IntegerEven).prove(assumptions = [InSet(a, IntegerEven)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(Neg(six), IntegerEven).prove()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Odd Integers (`IntegerOdd`), Odd Integer Membership (`IntegerOddMembership`), and subsets of odd integers"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(y, IntegerOdd)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(InSet(y, IntegerOdd), Boolean).prove()"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
-    "%end demonstrations"
+    "InSet(y, Integer).prove(assumptions=[InSet(y, IntegerOdd)])"
    ]
   },
   {
@@ -783,7 +1195,304 @@
    "execution_count": null,
    "metadata": {},
    "outputs": [],
-   "source": []
+   "source": [
+    "InSet(y, Rational).prove(assumptions=[InSet(y, IntegerOdd)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(y, Real).prove(assumptions=[InSet(y, IntegerOdd)])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The positive single-digit odd integers are “known” (as automatically imported theorems) to be members of the IntegerOdd set:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(one, IntegerOdd).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(three, IntegerOdd).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(five, IntegerOdd).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(seven, IntegerOdd).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(nine, IntegerOdd).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit import k\n",
+    "assumption_about_k = Equals(k, Add(Mult(two, z), one))\n",
+    "assumption_about_z = InSet(z, Integer)\n",
+    "display(assumption_about_k)\n",
+    "display(assumption_about_z)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Then we can derive the existential claim:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Exists(z, Equals(k, Add(Mult(two, z), one)), domain = Integer).conclude_via_example(\n",
+    "    z, assumptions = [assumption_about_k, assumption_about_z])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "After which, we can prove the set membership:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(k, IntegerOdd).prove(assumptions = [assumption_about_k, assumption_about_z])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Even + Odd should be Odd, and Odd + Even should be Odd"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "temp_assumptions = [InSet(a, IntegerEven), InSet(b, IntegerOdd)]\n",
+    "Add(a, b).deduce_in_number_set(IntegerOdd, assumptions=temp_assumptions)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "temp_assumptions = [InSet(a, IntegerEven), InSet(b, IntegerOdd)]\n",
+    "Add(b, a).deduce_in_number_set(IntegerOdd, assumptions=temp_assumptions)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The sum of an even number of odd integers should be even. For example:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit import a, b, c, d, e, f \n",
+    "from proveit.numbers import six\n",
+    "temp_assumptions = [InSet(a, IntegerOdd), InSet(b, IntegerOdd), InSet(c, IntegerOdd),\n",
+    "                    InSet(d, IntegerOdd), InSet(e, IntegerOdd), InSet(f, IntegerOdd), InSet(six, IntegerEven)]\n",
+    "Add(a, b, c, d, e, f).deduce_in_number_set(IntegerEven, assumptions = temp_assumptions)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The sum of an odd number of odd integers should be odd. For example:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit import a, b, c, d, e \n",
+    "from proveit.numbers import five\n",
+    "temp_assumptions = [InSet(a, IntegerOdd), InSet(b, IntegerOdd), InSet(c, IntegerOdd),\n",
+    "                    InSet(d, IntegerOdd), InSet(e, IntegerOdd), InSet(five, IntegerOdd)]\n",
+    "Add(a, b, c, d, e).deduce_in_number_set(IntegerOdd, assumptions = temp_assumptions)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The negation of an odd is still odd:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(Neg(a), IntegerOdd).prove(assumptions = [InSet(a, IntegerOdd)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(Neg(five), IntegerOdd).prove()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "And a product of all odd integers is odd:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from proveit import a, b, c, d, e\n",
+    "temp_assumptions = [InSet(a, IntegerOdd), InSet(b, IntegerOdd), InSet(c, IntegerOdd),\n",
+    "                    InSet(d, IntegerOdd), InSet(e, IntegerOdd)]\n",
+    "InSet(Mult(a, b, c, d, e), IntegerOdd).prove(assumptions = temp_assumptions)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Odds and Evens: Alternations and Successors"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The successor of an even integer is always an odd integer. The successor of an odd integer is always even. Earlier we proved that $10 \\in \\mathbb{E}$. If we know or can establish that  $11 = 10 + 1$, we can prove automatically that $11 \\in \\mathbb{O}$. And once $11$ is known to be odd, we can establish that $12 = 11 + 1$ and that $12 \\in \\mathbb{E}$, _etc._:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Equals(num(11), Add(num(10), one)).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(num(11), IntegerOdd).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Equals(num(12), Add(num(11), one)).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(num(12), IntegerEven).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Equals(num(13), Add(num(12), one)).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "InSet(num(13), IntegerOdd).prove()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%end demonstrations"
+   ]
   }
  ],
  "metadata": {
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/double_int_is_even/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/double_int_is_even/thm_proof.ipynb
new file mode 100644
index 000000000000..04d5fcf83f47
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/double_int_is_even/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#double_int_is_even\">double_int_is_even</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving double_int_is_even"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/double_int_plus_one_is_odd/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/double_int_plus_one_is_odd/thm_proof.ipynb
new file mode 100644
index 000000000000..be9e27ac560e
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/double_int_plus_one_is_odd/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#double_int_plus_one_is_odd\">double_int_plus_one_is_odd</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving double_int_plus_one_is_odd"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/eight_is_even/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/eight_is_even/thm_proof.ipynb
new file mode 100644
index 000000000000..8e906ba6d186
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/eight_is_even/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#eight_is_even\">eight_is_even</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving eight_is_even"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_has_odd_successor/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_has_odd_successor/thm_proof.ipynb
new file mode 100644
index 000000000000..cc1fd516d193
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_has_odd_successor/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#even_int_has_odd_successor\">even_int_has_odd_successor</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving even_int_has_odd_successor"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_is_double_int/allowed_presumptions.txt b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_is_double_int/allowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_is_double_int/disallowed_presumptions.txt b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_is_double_int/disallowed_presumptions.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_is_double_int/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_is_double_int/thm_proof.ipynb
new file mode 100644
index 000000000000..8ab055b78fcb
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_is_double_int/thm_proof.ipynb
@@ -0,0 +1,64 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#even_int_is_double_int\">even_int_is_double_int</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory\n",
+    "from proveit import defaults"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving even_int_is_double_int"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "defaults.assumptions = even_int_is_double_int.all_conditions()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof will likely rely on updates to the `SetOfAll` and `SetOfAllMembership` classes currently available the QEC-development branch."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_membership_is_bool/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_membership_is_bool/thm_proof.ipynb
new file mode 100644
index 000000000000..3873221d9ee3
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_membership_is_bool/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#even_int_membership_is_bool\">even_int_membership_is_bool</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving even_int_membership_is_bool"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_within_int/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_within_int/thm_proof.ipynb
new file mode 100644
index 000000000000..dc0afc6b8928
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/even_int_within_int/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#even_int_within_int\">even_int_within_int</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving even_int_within_int"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/five_is_odd/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/five_is_odd/thm_proof.ipynb
new file mode 100644
index 000000000000..b03edac2c6f5
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/five_is_odd/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#five_is_odd\">five_is_odd</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving five_is_odd"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/four_is_even/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/four_is_even/thm_proof.ipynb
new file mode 100644
index 000000000000..26a5884916b8
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/four_is_even/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#four_is_even\">four_is_even</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving four_is_even"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/neg_even_is_even/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/neg_even_is_even/thm_proof.ipynb
new file mode 100644
index 000000000000..8cb86bc1ab89
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/neg_even_is_even/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#neg_even_is_even\">neg_even_is_even</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving neg_even_is_even"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/neg_odd_is_odd/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/neg_odd_is_odd/thm_proof.ipynb
new file mode 100644
index 000000000000..ea90a58b0284
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/neg_odd_is_odd/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#neg_odd_is_odd\">neg_odd_is_odd</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving neg_odd_is_odd"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/nine_is_odd/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/nine_is_odd/thm_proof.ipynb
new file mode 100644
index 000000000000..314d7970b4ed
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/nine_is_odd/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#nine_is_odd\">nine_is_odd</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving nine_is_odd"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/odd_int_has_even_successor/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/odd_int_has_even_successor/thm_proof.ipynb
new file mode 100644
index 000000000000..83569f095c00
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/odd_int_has_even_successor/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#odd_int_has_even_successor\">odd_int_has_even_successor</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving odd_int_has_even_successor"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/odd_int_is_double_int_plus_one/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/odd_int_is_double_int_plus_one/thm_proof.ipynb
new file mode 100644
index 000000000000..f0b6cbc6ef4b
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/odd_int_is_double_int_plus_one/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#odd_int_is_double_int_plus_one\">odd_int_is_double_int_plus_one</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving odd_int_is_double_int_plus_one"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/odd_int_membership_is_bool/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/odd_int_membership_is_bool/thm_proof.ipynb
new file mode 100644
index 000000000000..40e9291592b2
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/odd_int_membership_is_bool/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#odd_int_membership_is_bool\">odd_int_membership_is_bool</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving odd_int_membership_is_bool"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/odd_int_within_int/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/odd_int_within_int/thm_proof.ipynb
new file mode 100644
index 000000000000..a2c95c83fbbd
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/odd_int_within_int/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#odd_int_within_int\">odd_int_within_int</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving odd_int_within_int"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/one_is_odd/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/one_is_odd/thm_proof.ipynb
new file mode 100644
index 000000000000..c226f3910cee
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/one_is_odd/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#one_is_odd\">one_is_odd</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving one_is_odd"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/seven_is_odd/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/seven_is_odd/thm_proof.ipynb
new file mode 100644
index 000000000000..c3a71d258963
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/seven_is_odd/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#seven_is_odd\">seven_is_odd</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving seven_is_odd"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/six_is_even/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/six_is_even/thm_proof.ipynb
new file mode 100644
index 000000000000..99e21ba6c537
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/six_is_even/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#six_is_even\">six_is_even</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving six_is_even"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/three_is_odd/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/three_is_odd/thm_proof.ipynb
new file mode 100644
index 000000000000..5ed81344e29c
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/three_is_odd/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#three_is_odd\">three_is_odd</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving three_is_odd"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/two_is_even/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/two_is_even/thm_proof.ipynb
new file mode 100644
index 000000000000..57fd79adb17d
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/two_is_even/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#two_is_even\">two_is_even</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving two_is_even"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/zero_is_even/thm_proof.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/zero_is_even/thm_proof.ipynb
new file mode 100644
index 000000000000..89eba4e7ee9a
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/proofs/zero_is_even/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">integers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#zero_is_even\">zero_is_even</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving zero_is_even"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/theorems.ipynb b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/theorems.ipynb
index 0871ba5728a1..82e4f81c9cbe 100644
--- a/packages/proveit/numbers/number_sets/integers/_theory_nbs_/theorems.ipynb
+++ b/packages/proveit/numbers/number_sets/integers/_theory_nbs_/theorems.ipynb
@@ -18,15 +18,17 @@
     "# Prepare this notebook for defining the theorems of a theory:\n",
     "%theorems_notebook # Keep this at the top following 'import proveit'.\n",
     "from proveit import Operation, IndexedVar\n",
-    "from proveit import a, b, c, d, e, f, g, h, i, m, n, x, y, S, P\n",
+    "from proveit import a, b, c, d, e, f, g, h, i, m, n, x, y, z, S, P\n",
     "from proveit.core_expr_types import (y_1_to_n)\n",
-    "from proveit.logic import (And, Equals, Forall, Implies, in_bool, InSet, NotInSet, Set,\n",
+    "from proveit.logic import (And, Equals, Exists, Forall, Implies, in_bool, InSet, NotInSet, Set,\n",
     "                           NotEquals, Or, ProperSubset, SubsetEq, SetEquiv, EmptySet)\n",
     "from proveit.logic.sets import Card, Disjoint, x_equals_any_y\n",
-    "from proveit.numbers import zero, one, num, frac, Neg\n",
+    "from proveit.numbers import (zero, one, two, three, four, five, six, seven, eight, nine,\n",
+    "        num, frac, Neg)\n",
     "from proveit.numbers import (ZeroSet, Natural, NaturalPos, \n",
-    "                             Integer, IntegerNeg, IntegerNonZero, IntegerNonPos, Interval, Complex)\n",
-    "from proveit.numbers import Add, subtract, greater, greater_eq, Less, LessEq, number_ordering\n",
+    "                             Integer, IntegerEven, IntegerNeg, IntegerNonZero, IntegerNonPos,\n",
+    "                             IntegerOdd, Interval, Complex)\n",
+    "from proveit.numbers import Add, subtract, greater, greater_eq, Less, LessEq, Mult, number_ordering\n",
     "from proveit.numbers import Pzero, Pone, Pm, P_mAddOne, Pn"
    ]
   },
@@ -75,6 +77,96 @@
     "zero_set_within_nonpos_int = ProperSubset(ZeroSet, IntegerNonPos)"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "zero_is_even = InSet(zero, IntegerEven)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "two_is_even = InSet(two, IntegerEven)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "four_is_even = InSet(four, IntegerEven)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "six_is_even = InSet(six, IntegerEven)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "eight_is_even = InSet(eight, IntegerEven)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "one_is_odd = InSet(one, IntegerOdd)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "three_is_odd = InSet(three, IntegerOdd)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "five_is_odd = InSet(five, IntegerOdd)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "seven_is_odd = InSet(seven, IntegerOdd)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "nine_is_odd = InSet(nine, IntegerOdd)"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -379,6 +471,24 @@
     "zero_is_int = InSet(zero, Integer)"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "even_int_within_int = ProperSubset(IntegerEven, Integer)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "odd_int_within_int = ProperSubset(IntegerOdd, Integer)"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -450,6 +560,94 @@
     "        domain=Integer)"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "neg_even_is_even = Forall(\n",
+    "        a, InSet(Neg(a), IntegerEven), domain = IntegerEven)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "even_int_is_double_int = Forall(\n",
+    "        a, Exists(z, Equals(a, Mult(two, z)), domain = Integer),\n",
+    "        domain = IntegerEven\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "double_int_is_even = Forall(\n",
+    "        a,\n",
+    "        InSet(a, IntegerEven),\n",
+    "        condition = Exists(z, Equals(a, Mult(two, z)), domain = Integer)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "neg_odd_is_odd = Forall(\n",
+    "        a, InSet(Neg(a), IntegerOdd), domain = IntegerOdd)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "odd_int_is_double_int_plus_one = Forall(\n",
+    "        a, Exists(z, Equals(a, Add(Mult(two, z), one)), domain = Integer),\n",
+    "        domain = IntegerOdd\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "double_int_plus_one_is_odd = Forall(\n",
+    "        a,\n",
+    "        InSet(a, IntegerOdd),\n",
+    "        condition = Exists(z, Equals(a, Add(Mult(two, z), one)), domain = Integer)\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "even_int_has_odd_successor = Forall(a, InSet(a, IntegerOdd), conditions = [InSet(subtract(a, one), IntegerEven)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "odd_int_has_even_successor = Forall(a, InSet(a, IntegerEven), conditions = [InSet(subtract(a, one), IntegerOdd)])"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -494,6 +692,24 @@
     "nonpos_int_membership_is_bool = Forall(x, in_bool(InSet(x, IntegerNonPos)))"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "even_int_membership_is_bool = Forall(x, in_bool(InSet(x, IntegerEven)))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "odd_int_membership_is_bool = Forall(x, in_bool(InSet(x, IntegerOdd)))"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
diff --git a/packages/proveit/numbers/number_sets/integers/integer.py b/packages/proveit/numbers/number_sets/integers/integer.py
index 69d7e443e0fe..cd18ecd261da 100644
--- a/packages/proveit/numbers/number_sets/integers/integer.py
+++ b/packages/proveit/numbers/number_sets/integers/integer.py
@@ -273,6 +273,36 @@ def membership_object(self, element):
         from .integer_membership import IntegerNonPosMembership    
         return IntegerNonPosMembership(element)
 
+class IntegerEvenSet(NumberSet):
+    '''
+    Class to represent the even integers {..., -2, 0, 2, 4, ...}
+    and provide related methods. The even integer set appears as
+    'IntegerEven' in string output and as mathbb 'E' in LaTeX output.
+    '''
+    def __init__(self, *, styles=None):
+        NumberSet.__init__(self, 'IntegerEven', r'\mathbb{E}',
+                           theory=__file__, styles=styles,
+                           fence_when_forced=True)
+
+    def membership_object(self, element):
+        from .integer_membership import IntegerEvenMembership    
+        return IntegerEvenMembership(element)
+
+class IntegerOddSet(NumberSet):
+    '''
+    Class to represent the odd integers {..., -3, -1, 1, 3, ...}
+    and provide related methods. The odd integer set appears as
+    'IntegerOdd' in string output and as mathbb 'O' in LaTeX output.
+    '''
+    def __init__(self, *, styles=None):
+        NumberSet.__init__(self, 'IntegerOdd', r'\mathbb{O}',
+                           theory=__file__, styles=styles,
+                           fence_when_forced=True)
+
+    def membership_object(self, element):
+        from .integer_membership import IntegerOddMembership    
+        return IntegerOddMembership(element)
+
 
 if proveit.defaults.running_theory_notebook is None:
     # Import some fundamental theorems without quantifiers when not 
diff --git a/packages/proveit/numbers/number_sets/integers/integer_membership.py b/packages/proveit/numbers/number_sets/integers/integer_membership.py
index 7c7d76c64276..50447269c7ef 100644
--- a/packages/proveit/numbers/number_sets/integers/integer_membership.py
+++ b/packages/proveit/numbers/number_sets/integers/integer_membership.py
@@ -1,9 +1,10 @@
 from proveit import prover, relation_prover
-from proveit import a, x
-from proveit.logic import NotEquals, InSet
-from proveit.numbers import Less, LessEq
-from proveit.numbers import (zero, Integer, IntegerNeg,
-                             IntegerNonPos, IntegerNonZero)
+from proveit import a, b, n, x, z
+from proveit.logic import Equals, Exists, InSet, NotEquals
+from proveit.numbers import Add, Less, LessEq, Mult, Neg, subtract
+from proveit.numbers import (
+        zero, one, two, Integer, IntegerEven, IntegerNeg,
+        IntegerNonPos, IntegerNonZero, IntegerOdd)
 from proveit.numbers.number_sets.number_set import NumberMembership
 
 
@@ -265,3 +266,240 @@ def derive_element_in_rational_nonpos(self, **defaults_config):
                 nonpos_int_within_rational_nonpos)
         return nonpos_int_within_rational_nonpos.derive_superset_membership(
             self.element, auto_simplify=False)
+
+class IntegerEvenMembership(NumberMembership):
+    '''
+    Defines methods that apply to membership in IntegerEven (the
+    set of even integers, E = {..., -2, 0, 2, 4, ...}).
+    '''
+
+    def __init__(self, element):
+        NumberMembership.__init__(self, element, IntegerEven)
+
+    def _readily_provable(self):
+        return NumberMembership._readily_provable(self)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Conclude element in IntegerEven using the fact that
+        (1) the element can be expressed as 2z for some z in Integer;
+        (2) the element is an instance of Add();
+        (3) the element is a product of integers with at least one of
+        the integers being even;
+        (4) the element is the negation of an even integer; or
+        (5) the element is the successor of an odd integer.
+        '''
+        if (Exists(z, Equals(self.element, Mult(two, z)), domain=Integer)
+                .proven()):
+            return self.conclude_as_last_resort()
+
+        if isinstance(self.element, Add):
+            return self.element.deduce_in_number_set(self.number_set)
+
+        if isinstance(self.element, Mult):
+            operands = self.element.operands
+            if operands.is_double():
+                _a = operands[0]
+                _b = operands[1]
+                if (InSet(_a, IntegerEven).proven()
+                        and InSet(_b, Integer).proven()):
+                    from proveit.numbers.multiplication import (
+                            mult_int_even_from_left_even_bin)
+                    return mult_int_even_from_left_even_bin.instantiate(
+                            {a:_a, b:_b})
+                if (InSet(_b, IntegerEven).proven()
+                        and InSet(_a, Integer).proven()):
+                    from proveit.numbers.multiplication import (
+                            mult_int_even_from_right_even_bin)
+                    return mult_int_even_from_right_even_bin.instantiate(
+                            {a:_a, b:_b})
+            if len(operands) > 0: # empty Mult is 1 (odd) by axiom
+                if (all(InSet(a, Integer).proven() for a in operands)
+                    and any(InSet(a, IntegerEven).proven() for a in operands)):
+                    from proveit.numbers.multiplication import (
+                            mult_int_even_from_ints_with_any_even)
+                    _a = operands
+                    _n = operands.num_elements()
+                    return mult_int_even_from_ints_with_any_even.instantiate(
+                            {n:_n, a:_a})
+
+        if (isinstance(self.element, Neg)
+            and InSet(self.element.operand, IntegerEven).proven()):
+            # Neg(even) is even
+            from . import neg_even_is_even
+            _a = self.element.operand
+            return neg_even_is_even.instantiate({a:_a})
+
+        if InSet(subtract(self.element, one), IntegerOdd).readily_provable():
+            from . import odd_int_has_even_successor
+            _a = self.element
+            return odd_int_has_even_successor.instantiate({a: _a})
+
+        return NumberMembership.conclude(self)
+
+    @prover
+    def conclude_as_last_resort(self, **defaults_config):
+        '''
+        Conclude element in IntegerEven using the fact that the
+        element can be expressed as 2z for some Integer z.
+        This method is called from NumberMembership.conclude()
+        as an almost-last resort.
+        '''
+        from . import double_int_is_even
+        _a_sub = self.element
+        return double_int_is_even.instantiate({a:_a_sub})
+
+    def side_effects(self, judgment):
+        '''
+        Yield side-effects when proving 'n in IntegerEven' for 
+        # a given n:
+        (1) An even integer n is an integer (this will then cascade
+            to rationals, reals, and complex numbers);
+        (2) An even integer n can be expressed as 2z for some
+            integer z.
+        '''
+        yield self.derive_element_in_integer
+        yield self.derive_element_as_double_int
+
+    @relation_prover
+    def deduce_in_bool(self, **defaults_config):
+        from . import even_int_membership_is_bool
+        return even_int_membership_is_bool.instantiate(
+            {x: self.element}, auto_simplify=False)
+
+    @prover
+    def derive_element_in_integer(self, **defaults_config):
+        from . import even_int_within_int
+        return even_int_within_int.derive_superset_membership(
+            self.element, auto_simplify=False)
+
+    @prover
+    def derive_element_in_rational(self, **defaults_config):
+        from proveit.numbers.number_sets.rational_numbers import (
+                even_int_within_rational)
+        return even_int_within_rational.derive_superset_membership(
+                self.element, auto_simplify=False)
+
+    @prover
+    def derive_element_in_real(self, **defaults_config):
+        from proveit.numbers.number_sets.real_numbers import (
+                even_int_within_real)
+        return even_int_within_real.derive_superset_membership(
+                self.element, auto_simplify=False)
+
+    @prover
+    def derive_element_as_double_int(self, **defaults_config):
+        from . import even_int_is_double_int
+        _a_sub = self.element
+        return even_int_is_double_int.instantiate({a:_a_sub})
+
+class IntegerOddMembership(NumberMembership):
+    '''
+    Defines methods that apply to membership in IntegerOdd (the
+    set of odd integers, O = {..., -3, -1, 1, 3, ...}).
+    '''
+
+    def __init__(self, element):
+        NumberMembership.__init__(self, element, IntegerOdd)
+
+    def _readily_provable(self):
+        return NumberMembership._readily_provable(self)
+
+    @prover
+    def conclude(self, **defaults_config):
+        '''
+        Conclude element in IntegerOdd using the fact that the element:
+        (1) can be expressed as (2z + 1) for some z in Integer;
+        (2) is an instance of Add();
+        (3) is a product of all odd integers;
+        (4) the element is the negation of an odd integer; or
+        (5) the element is the successor of an even integer.
+        '''
+        if Exists(z, Equals(self.element, Add(Mult(two, z), one)),
+                  domain=Integer).proven():
+            return self.conclude_as_last_resort()
+
+        if isinstance(self.element, Add):
+            return self.element.deduce_in_number_set(self.number_set)
+
+        if isinstance(self.element, Mult):
+            operands = self.element.operands
+            if all(InSet(a, IntegerOdd).proven() for a in operands):
+                from proveit.numbers.multiplication import (
+                        mult_int_odd_from_all_odd)
+                _a = operands
+                _n = operands.num_elements()
+                return mult_int_odd_from_all_odd.instantiate({n:_n, a:_a})
+
+        if (isinstance(self.element, Neg)
+            and InSet(self.element.operand, IntegerOdd).proven()):
+            # Neg(odd) is odd
+            from . import neg_odd_is_odd
+            _a = self.element.operand
+            return neg_odd_is_odd.instantiate({a:_a})
+
+        if (InSet(subtract(self.element, one), IntegerEven)
+            .readily_provable()):
+            from . import even_int_has_odd_successor
+            _a = self.element
+            return even_int_has_odd_successor.instantiate({a: _a})
+
+        return NumberMembership.conclude(self)
+
+    @prover
+    def conclude_as_last_resort(self, **defaults_config):
+        '''
+        Conclude element in IntegerOdd using the fact that the
+        element can be expressed as (2z + 1) for some Integer z.
+        This method is called via NumberMembership.conclude()
+        if the 'deduce_in_number_set' method of the element raises
+        a NotImplementedError.
+        '''
+        from . import double_int_plus_one_is_odd
+        _a_sub = self.element
+        return double_int_plus_one_is_odd.instantiate({a:_a_sub})
+
+    def side_effects(self, judgment):
+        '''
+        Yield side-effects when proving 'n in IntegerOdd' for 
+        # a given n:
+        (1) An odd integer n is an integer (this will cascade to
+            rationals, reals, and complex numbers);
+        (2) An odd integer n can be expressed as 2z+1 for some
+            integer z.
+        '''
+        yield self.derive_element_in_integer
+        yield self.derive_element_as_double_int_plus_one
+
+    @relation_prover
+    def deduce_in_bool(self, **defaults_config):
+        from . import odd_int_membership_is_bool
+        return odd_int_membership_is_bool.instantiate(
+            {x: self.element}, auto_simplify=False)
+
+    @prover
+    def derive_element_in_integer(self, **defaults_config):
+        from . import odd_int_within_int
+        return odd_int_within_int.derive_superset_membership(
+            self.element, auto_simplify=False)
+
+    @prover
+    def derive_element_in_rational(self, **defaults_config):
+        from proveit.numbers.number_sets.rational_numbers import (
+                odd_int_within_rational)
+        return odd_int_within_rational.derive_superset_membership(
+                self.element, auto_simplify=False)
+
+    @prover
+    def derive_element_in_real(self, **defaults_config):
+        from proveit.numbers.number_sets.real_numbers import (
+                odd_int_within_real)
+        return odd_int_within_real.derive_superset_membership(
+                self.element, auto_simplify=False)
+
+    @prover
+    def derive_element_as_double_int_plus_one(self, **defaults_config):
+        from . import odd_int_is_double_int_plus_one
+        _a_sub = self.element
+        return odd_int_is_double_int_plus_one.instantiate({a:_a_sub})
diff --git a/packages/proveit/numbers/number_sets/number_set.py b/packages/proveit/numbers/number_sets/number_set.py
index 245bdbd7f844..12bef92aadfb 100644
--- a/packages/proveit/numbers/number_sets/number_set.py
+++ b/packages/proveit/numbers/number_sets/number_set.py
@@ -324,6 +324,10 @@ def conclude(self, **defaults_config):
 
         """
         # Try the 'deduce_in_number_set' method.
+        # The 'element.deduce_in_number_set()'' attempt (often?) leads
+        # to infinite loop of some sort, where a membership.conclude()
+        # leads to a NumberSet.conclude(), which then leads to a
+        # membership.conclude(), ad infinitum.
         if hasattr(element, 'deduce_in_number_set'):
             # Try to deduce membership directly in the desired number
             # set via 'deduce_in_number_set'.
@@ -346,6 +350,7 @@ def conclude(self, **defaults_config):
                 return membership.inner_expr().element.substitute(
                         element)
         """
+
         if hasattr(self, 'conclude_as_last_resort'):
             return self.conclude_as_last_resort()
         if hasattr(element, 'readily_provable_number_set'):
diff --git a/packages/proveit/numbers/number_sets/rational_numbers/_theory_nbs_/proofs/even_int_within_rational/thm_proof.ipynb b/packages/proveit/numbers/number_sets/rational_numbers/_theory_nbs_/proofs/even_int_within_rational/thm_proof.ipynb
new file mode 100644
index 000000000000..9ffde96fa254
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/rational_numbers/_theory_nbs_/proofs/even_int_within_rational/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">rational_numbers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#even_int_within_rational\">even_int_within_rational</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving even_int_within_rational"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/rational_numbers/_theory_nbs_/proofs/odd_int_within_rational/thm_proof.ipynb b/packages/proveit/numbers/number_sets/rational_numbers/_theory_nbs_/proofs/odd_int_within_rational/thm_proof.ipynb
new file mode 100644
index 000000000000..4266dabafde3
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/rational_numbers/_theory_nbs_/proofs/odd_int_within_rational/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">rational_numbers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#odd_int_within_rational\">odd_int_within_rational</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving odd_int_within_rational"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/rational_numbers/_theory_nbs_/theorems.ipynb b/packages/proveit/numbers/number_sets/rational_numbers/_theory_nbs_/theorems.ipynb
index 16a57802154b..9eb2bcff3e62 100644
--- a/packages/proveit/numbers/number_sets/rational_numbers/_theory_nbs_/theorems.ipynb
+++ b/packages/proveit/numbers/number_sets/rational_numbers/_theory_nbs_/theorems.ipynb
@@ -22,7 +22,7 @@
     "from proveit.numbers import frac, GCD, Less, LessEq, greater, greater_eq\n",
     "from proveit.numbers import one, zero\n",
     "from proveit.numbers import (ZeroSet, Natural, NaturalPos, Integer, IntegerNonZero, IntegerNeg, IntegerNonPos,\n",
-    "                             Rational, RationalPos, RationalNeg,\n",
+    "                             IntegerEven, IntegerOdd, Rational, RationalPos, RationalNeg,\n",
     "                             RationalNonNeg, RationalNonPos, RationalNonZero, NaturalPos)"
    ]
   },
@@ -161,6 +161,24 @@
     "nonpos_int_within_rational_nonpos = ProperSubset(IntegerNonPos, RationalNonPos)"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "even_int_within_rational = ProperSubset(IntegerEven, Rational)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "odd_int_within_rational = ProperSubset(IntegerOdd, Rational)"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
diff --git a/packages/proveit/numbers/number_sets/real_numbers/_theory_nbs_/proofs/even_int_within_real/thm_proof.ipynb b/packages/proveit/numbers/number_sets/real_numbers/_theory_nbs_/proofs/even_int_within_real/thm_proof.ipynb
new file mode 100644
index 000000000000..00d3cbffbc97
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/real_numbers/_theory_nbs_/proofs/even_int_within_real/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">real_numbers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#even_int_within_real\">even_int_within_real</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving even_int_within_real"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/real_numbers/_theory_nbs_/proofs/odd_int_within_real/thm_proof.ipynb b/packages/proveit/numbers/number_sets/real_numbers/_theory_nbs_/proofs/odd_int_within_real/thm_proof.ipynb
new file mode 100644
index 000000000000..40da1542a0dd
--- /dev/null
+++ b/packages/proveit/numbers/number_sets/real_numbers/_theory_nbs_/proofs/odd_int_within_real/thm_proof.ipynb
@@ -0,0 +1,47 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Proof of <a class=\"ProveItLink\" href=\"../../../../../../_theory_nbs_/theory.ipynb\">proveit</a>.<a class=\"ProveItLink\" href=\"../../../../../_theory_nbs_/theory.ipynb\">numbers</a>.<a class=\"ProveItLink\" href=\"../../../../_theory_nbs_/theory.ipynb\">number_sets</a>.<a class=\"ProveItLink\" href=\"../../theory.ipynb\">real_numbers</a>.<a class=\"ProveItLink\" href=\"../../theorems.ipynb#odd_int_within_real\">odd_int_within_real</a> theorem\n",
+    "========"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import proveit\n",
+    "theory = proveit.Theory() # the theorem's theory"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%proving odd_int_within_real"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/packages/proveit/numbers/number_sets/real_numbers/_theory_nbs_/theorems.ipynb b/packages/proveit/numbers/number_sets/real_numbers/_theory_nbs_/theorems.ipynb
index 2d60c602bdc9..87f90a4ffcfc 100644
--- a/packages/proveit/numbers/number_sets/real_numbers/_theory_nbs_/theorems.ipynb
+++ b/packages/proveit/numbers/number_sets/real_numbers/_theory_nbs_/theorems.ipynb
@@ -25,8 +25,8 @@
     "                             Less, LessEq, Mult, Neg, number_ordering, sqrt)\n",
     "from proveit.numbers import (\n",
     "    zero, one, two, e, pi, ZeroSet, Complex, \n",
-    "    Integer, Natural, NaturalPos, IntegerNeg, IntegerNonPos, IntegerNonZero,\n",
-    "    Rational, RationalNonZero, RationalPos, RationalNeg, RationalNonNeg, RationalNonPos, \n",
+    "    Integer, Natural, NaturalPos, IntegerEven, IntegerNeg, IntegerNonPos, IntegerNonZero,\n",
+    "    IntegerOdd, Rational, RationalNonZero, RationalPos, RationalNeg, RationalNonNeg, RationalNonPos, \n",
     "    Real, RealNonNeg, RealPos, RealNeg, RealNonPos, RealNonZero)\n"
    ]
   },
@@ -174,6 +174,24 @@
     "nonpos_int_within_real_nonpos = ProperSubset(IntegerNonPos, RealNonPos)"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "even_int_within_real = ProperSubset(IntegerEven, Real)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "odd_int_within_real = ProperSubset(IntegerOdd, Real)"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
