{
"cells": [
{
"cell_type": "markdown",
"source": [
"# Partitions\n",
"\n",
"\n",
"Partitions are a categorical construction that we derive from sets and functions.\n",
"Given a set A, you can think of all of the ways to partition A into parts.\n",
"These ways of partitioning are isomorphic to equivalence relations R ⊆ A × A."
],
"metadata": {}
},
{
"cell_type": "markdown",
"source": [
"The first step is our Catlab imports"
],
"metadata": {}
},
{
"outputs": [],
"cell_type": "code",
"source": [
"using Core: GeneratedFunctionStub\n",
"using Test\n",
"\n",
"using Catlab, Catlab.Theories, Catlab.CategoricalAlgebra\n",
"import Catlab.Theories: compose\n",
"using DataStructures\n",
"using PrettyTables\n",
"PrettyTables.pretty_table(f::FinFunction, name::Symbol=:f) =\n",
" pretty_table(OrderedDict(:x=>1:length(dom(f)), Symbol(\"$(name)(x)\")=>collect(f)))\n",
"using LaTeXStrings"
],
"metadata": {},
"execution_count": 1
},
{
"cell_type": "markdown",
"source": [
"## FinSet: the category of Finite Sets\n",
"In FinSet the objects are sets n = {1...n} and the morphisms are functions between finite sets.\n",
"You can wrap a plain old Int into a finite set with the FinSet(n::Int) function. These sets will\n",
"serve as the domain and codomains of our functions."
],
"metadata": {}
},
{
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": "FinSet(4)"
},
"metadata": {},
"execution_count": 2
}
],
"cell_type": "code",
"source": [
"n = FinSet(3)\n",
"m = FinSet(4)"
],
"metadata": {},
"execution_count": 2
},
{
"cell_type": "markdown",
"source": [
"once you have some sets, you can define functions between them. A FinFunction from n to m, f:n→m, can be specified as\n",
"an array of length n with elements from m."
],
"metadata": {}
},
{
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"┌───────┬───────┐\n",
"│ x │ f(x) │\n",
"│ Int64 │ Int64 │\n",
"├───────┼───────┤\n",
"│ 1 │ 2 │\n",
"│ 2 │ 4 │\n",
"│ 3 │ 3 │\n",
"└───────┴───────┘\n"
]
}
],
"cell_type": "code",
"source": [
"f = FinFunction([2,4,3], n, m)\n",
"\n",
"pretty_table(f)"
],
"metadata": {},
"execution_count": 3
},
{
"cell_type": "markdown",
"source": [
"## Surjective maps\n",
"In order to use a map to represent a partition, we have to make sure that it is surjective.\n",
"Given a FinFunction, we can compute the preimage of any element in its codomain."
],
"metadata": {}
},
{
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": "Int64[]"
},
"metadata": {},
"execution_count": 4
}
],
"cell_type": "code",
"source": [
"preimage(f, 2)\n",
"\n",
"preimage(f, 1)"
],
"metadata": {},
"execution_count": 4
},
{
"cell_type": "markdown",
"source": [
"If the preimage is empty, then there is no element in the domain that maps to that element of the codomain.\n",
"This gives us a definition of surjective functions by asserting that all the preimages are nonempty.\n",
"Julia note: !p is the predicate x ↦ ¬p(x), f.(A) applies f to all of the elements in A."
],
"metadata": {}
},
{
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": "false"
},
"metadata": {},
"execution_count": 5
}
],
"cell_type": "code",
"source": [
"is_surjective(f::FinFunction) = all((!isempty).(preimage(f,i) for i in codom(f)))\n",
"is_surjective(f)"
],
"metadata": {},
"execution_count": 5
},
{
"cell_type": "markdown",
"source": [
"Our function f, wasn't surjective so it can't be used to induce a partition via its preimages.\n",
"Let's try again,"
],
"metadata": {}
},
{
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"┌───────┬───────┐\n",
"│ x │ g(x) │\n",
"│ Int64 │ Int64 │\n",
"├───────┼───────┤\n",
"│ 1 │ 1 │\n",
"│ 2 │ 2 │\n",
"│ 3 │ 3 │\n",
"│ 4 │ 3 │\n",
"└───────┴───────┘\n"
]
},
{
"output_type": "execute_result",
"data": {
"text/plain": "true"
},
"metadata": {},
"execution_count": 6
}
],
"cell_type": "code",
"source": [
"g = FinFunction([1,2,3,3], m, n)\n",
"pretty_table(g, :g)\n",
"is_surjective(g)"
],
"metadata": {},
"execution_count": 6
},
{
"cell_type": "markdown",
"source": [
"# Refinements of a Partition\n",
"When defining partitions classically as A = ∪ₚ Aₚ with p ≠ r ⟹ Aₚ ≠ Aᵣ,\n",
"it is not immediately obvious how to define comparisons between partitions.\n",
"With the \"a partition of A is a surjective map out of A\" definition, the comparisons\n",
"are obvious. The composition of surjective maps is surjective, so we can define\n",
"the refinement order in terms of a diagram in Set.\n",
"\n",
"You can see a graphical definition in [quiver](https://q.uiver.app/?q=WzAsMyxbMCwwLCJBIl0sWzMsMCwiUSJdLFszLDIsIlAiXSxbMSwyLCJoIiwwLHsic3R5bGUiOnsiaGVhZCI6eyJuYW1lIjoiZXBpIn19fV0sWzAsMSwiZiIsMCx7InN0eWxlIjp7ImhlYWQiOnsibmFtZSI6ImVwaSJ9fX1dLFswLDIsImciLDIseyJzdHlsZSI6eyJoZWFkIjp7Im5hbWUiOiJlcGkifX19XV0=)"
],
"metadata": {}
},
{
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": "TikzPictures.TikzPicture(\"A &&& Q \\\\\\\\\\n\\\\\\\\\\n&&& P\\n\\\\arrow[\\\"h\\\", two heads, from=1-4, to=3-4]\\n\\\\arrow[\\\"f\\\", two heads, from=1-1, to=1-4]\\n\\\\arrow[\\\"g\\\"', two heads, from=1-1, to=3-4]\", \"\", \"\\\\usepackage{tikz-cd}\\n% contents of quiver.sty\\n% `tikz-cd` is necessary to draw commutative diagrams.\\n\\\\RequirePackage{tikz-cd}\\n% `amssymb` is necessary for `\\\\lrcorner` and `\\\\ulcorner`.\\n\\\\RequirePackage{amssymb}\\n% `calc` is necessary to draw curved arrows.\\n\\\\usetikzlibrary{calc}\\n% `pathmorphing` is necessary to draw squiggly arrows.\\n\\\\usetikzlibrary{decorations.pathmorphing}\\n\\n% A TikZ style for curved arrows of a fixed height, due to AndréC.\\n\\\\tikzset{curve/.style={settings={#1},to path={(\\\\tikztostart)\\n .. controls (\\$(\\\\tikztostart)!\\\\pv{pos}!(\\\\tikztotarget)!\\\\pv{height}!270:(\\\\tikztotarget)\\$)\\n and (\\$(\\\\tikztostart)!1-\\\\pv{pos}!(\\\\tikztotarget)!\\\\pv{height}!270:(\\\\tikztotarget)\\$)\\n .. (\\\\tikztotarget)\\\\tikztonodes}},\\n settings/.code={\\\\tikzset{quiver/.cd,#1}\\n \\\\def\\\\pv##1{\\\\pgfkeysvalueof{/tikz/quiver/##1}}},\\n quiver/.cd,pos/.initial=0.35,height/.initial=0}\\n\\n% TikZ arrowhead/tail styles.\\n\\\\tikzset{tail reversed/.code={\\\\pgfsetarrowsstart{tikzcd to}}}\\n\\\\tikzset{2tail/.code={\\\\pgfsetarrowsstart{Implies[reversed]}}}\\n\\\\tikzset{2tail reversed/.code={\\\\pgfsetarrowsstart{Implies}}}\\n% TikZ arrow styles.\\n\\\\tikzset{no body/.style={/tikz/dash pattern=on 0 off 1mm}}\", \"tikzcd\", \"\", \"\", true, true)",
"image/svg+xml": [
"\n",
"\n",
"\n"
]
},
"metadata": {},
"execution_count": 7
}
],
"cell_type": "code",
"source": [
"using TikzCDs\n",
"\n",
"triangle = L\"\"\"\n",
"A &&& Q \\\\\n",
"\\\\\n",
"&&& P\n",
"\\arrow[\"h\", two heads, from=1-4, to=3-4]\n",
"\\arrow[\"f\", two heads, from=1-1, to=1-4]\n",
"\\arrow[\"g\"', two heads, from=1-1, to=3-4]\n",
"\"\"\";\n",
"\n",
"TikzCD(triangle, preamble=TikzCDs.Styles.Quiver)"
],
"metadata": {},
"execution_count": 7
},
{
"cell_type": "markdown",
"source": [
"Let's take a look at an example:"
],
"metadata": {}
},
{
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"┌───────┬──────────┐\n",
"│ x │ (f⋅h)(x) │\n",
"│ Int64 │ Int64 │\n",
"├───────┼──────────┤\n",
"│ 1 │ 1 │\n",
"│ 2 │ 1 │\n",
"│ 3 │ 2 │\n",
"│ 4 │ 2 │\n",
"└───────┴──────────┘\n"
]
},
{
"output_type": "execute_result",
"data": {
"text/plain": "true"
},
"metadata": {},
"execution_count": 8
}
],
"cell_type": "code",
"source": [
"A = FinSet(4)\n",
"Q = FinSet(3)\n",
"P = FinSet(2)\n",
"\n",
"f = FinFunction([1,2,3,3], A, Q)\n",
"g = FinFunction([1,1,2,2], A, P)\n",
"h = FinFunction([1,1,2], Q, P)\n",
"\n",
"@test_throws ErrorException compose(g,h) #Catlab checks the domains match\n",
"\n",
"pretty_table(compose(f,h), Symbol(\"(f⋅h)\"))\n",
"\n",
"compose(f,h) == g"
],
"metadata": {},
"execution_count": 8
},
{
"cell_type": "markdown",
"source": [
"This triangle commutes, so f is a refinement of g equivalently g is coarser than f."
],
"metadata": {}
},
{
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"┌───────┬───────────┐\n",
"│ x │ f⋅h⋅h′(x) │\n",
"│ Int64 │ Int64 │\n",
"├───────┼───────────┤\n",
"│ 1 │ 1 │\n",
"│ 2 │ 1 │\n",
"│ 3 │ 1 │\n",
"│ 4 │ 1 │\n",
"└───────┴───────────┘\n"
]
}
],
"cell_type": "code",
"source": [
"h′ = FinFunction([1,1], P, FinSet(1))\n",
"\n",
"pretty_table(f⋅h⋅h′, Symbol(\"f⋅h⋅h′\"))"
],
"metadata": {},
"execution_count": 9
},
{
"cell_type": "markdown",
"source": [
"### Properties of refinements\n",
"We can show that refinement gives us a preorder on partitions directly from the\n",
"nice properties of surjective maps.\n",
"1. Reflexive: Any partition is a refinement of itself.\n",
"2. Transitive: If f ≤ g ≤ h as partitions, then f ≤ h\n",
"You can read these directly off the definition of refinements as a commutative\n",
"triangle in the category of (Set, Surjections).\n",
"You can edit this diagram in [quiver](https://q.uiver.app/?q=WzAsNCxbMCwwLCJBIl0sWzMsMCwiUSJdLFszLDIsIlAiXSxbMyw0LCJRXlxccHJpbWUiXSxbMSwyLCJoIl0sWzAsMSwiZiIsMCx7InN0eWxlIjp7ImhlYWQiOnsibmFtZSI6ImVwaSJ9fX1dLFswLDIsImciLDIseyJzdHlsZSI6eyJoZWFkIjp7Im5hbWUiOiJlcGkifX19XSxbMiwzLCJoXlxccHJpbWUiXSxbMCwzLCJmXFxjZG90IGhcXGNkb3QgaF5cXHByaW1lID0gZ1xcY2RvdCBoXlxccHJpbWUiLDIseyJzdHlsZSI6eyJoZWFkIjp7Im5hbWUiOiJlcGkifX19XV0=)"
],
"metadata": {}
},
{
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": "TikzPictures.TikzPicture(\"A &&& Q \\\\\\\\\\n\\\\\\\\\\n&&& P \\\\\\\\\\n\\\\\\\\\\n&&& {Q^\\\\prime}\\n\\\\arrow[\\\"h\\\", from=1-4, to=3-4]\\n\\\\arrow[\\\"f\\\", two heads, from=1-1, to=1-4]\\n\\\\arrow[\\\"g\\\"', two heads, from=1-1, to=3-4]\\n\\\\arrow[\\\"{h^\\\\prime}\\\", from=3-4, to=5-4]\\n\\\\arrow[\\\"{f\\\\cdot h\\\\cdot h^\\\\prime = g\\\\cdot h^\\\\prime}\\\"', two heads, from=1-1, to=5-4]\", \"\", \"\\\\usepackage{tikz-cd}\\n% contents of quiver.sty\\n% `tikz-cd` is necessary to draw commutative diagrams.\\n\\\\RequirePackage{tikz-cd}\\n% `amssymb` is necessary for `\\\\lrcorner` and `\\\\ulcorner`.\\n\\\\RequirePackage{amssymb}\\n% `calc` is necessary to draw curved arrows.\\n\\\\usetikzlibrary{calc}\\n% `pathmorphing` is necessary to draw squiggly arrows.\\n\\\\usetikzlibrary{decorations.pathmorphing}\\n\\n% A TikZ style for curved arrows of a fixed height, due to AndréC.\\n\\\\tikzset{curve/.style={settings={#1},to path={(\\\\tikztostart)\\n .. controls (\\$(\\\\tikztostart)!\\\\pv{pos}!(\\\\tikztotarget)!\\\\pv{height}!270:(\\\\tikztotarget)\\$)\\n and (\\$(\\\\tikztostart)!1-\\\\pv{pos}!(\\\\tikztotarget)!\\\\pv{height}!270:(\\\\tikztotarget)\\$)\\n .. (\\\\tikztotarget)\\\\tikztonodes}},\\n settings/.code={\\\\tikzset{quiver/.cd,#1}\\n \\\\def\\\\pv##1{\\\\pgfkeysvalueof{/tikz/quiver/##1}}},\\n quiver/.cd,pos/.initial=0.35,height/.initial=0}\\n\\n% TikZ arrowhead/tail styles.\\n\\\\tikzset{tail reversed/.code={\\\\pgfsetarrowsstart{tikzcd to}}}\\n\\\\tikzset{2tail/.code={\\\\pgfsetarrowsstart{Implies[reversed]}}}\\n\\\\tikzset{2tail reversed/.code={\\\\pgfsetarrowsstart{Implies}}}\\n% TikZ arrow styles.\\n\\\\tikzset{no body/.style={/tikz/dash pattern=on 0 off 1mm}}\", \"tikzcd\", \"\", \"\", true, true)",
"image/svg+xml": [
"\n",
"\n",
"\n"
]
},
"metadata": {},
"execution_count": 10
}
],
"cell_type": "code",
"source": [
"refinement = L\"\"\"\n",
"A &&& Q \\\\\n",
"\\\\\n",
"&&& P \\\\\n",
"\\\\\n",
"&&& {Q^\\prime}\n",
"\\arrow[\"h\", from=1-4, to=3-4]\n",
"\\arrow[\"f\", two heads, from=1-1, to=1-4]\n",
"\\arrow[\"g\"', two heads, from=1-1, to=3-4]\n",
"\\arrow[\"{h^\\prime}\", from=3-4, to=5-4]\n",
"\\arrow[\"{f\\cdot h\\cdot h^\\prime = g\\cdot h^\\prime}\"', two heads, from=1-1, to=5-4]\n",
"\"\"\";\n",
"\n",
"TikzCD(refinement, preamble=TikzCDs.Styles.Quiver)"
],
"metadata": {},
"execution_count": 10
}
],
"nbformat_minor": 3,
"metadata": {
"language_info": {
"file_extension": ".jl",
"mimetype": "application/julia",
"name": "julia",
"version": "1.7.0"
},
"kernelspec": {
"name": "julia-1.7",
"display_name": "Julia 1.7.0",
"language": "julia"
}
},
"nbformat": 4
}