Representing sets and functions as data structures
= set([False, True]) # builtin Python type
B
= set(["red","green","blue"])
Colors
class Fun:
def __init__(self, dom:set, codom:set, f:dict):
self.dom = dom
self.codom = codom
self.f = f
def __call__(self, v):
return self.f[v]
# builtin function
assert issubset(set(["a","b"]), set("c","b","a"))
def isomorphic(a:set, b:set): len(a) == len(b)
def image(f):
return set([f.f[a] for a in f.dom])
= Fun(Colors, B, {"red":True,"green":True,"blue":False})
rg = Fun(Colors, B, {"red":False,"green":True,"blue":True})
gb assert image(rg) == image(gb)
We’ve introduced some other data structures too:
class Graph:
def __init__(self, src: Fun, tgt: Fun):
assert src.dom == tgt.dom
assert self.codom == tgt.codom
self.E = src.dom
self.V = src.codom
self.src = src
self.tgt = tgt
class GraphHom:
def __init__(self, vfun: Fun, efun: Fun):
self.vfun = vfun
self.efun = efun
class Petri:
def __init__(self, i_s: Fun, i_t: Fun, o_s: Fun, o_t: Fun):
assert i_s.dom == i_t.dom and i_s.codom == o_s.codom
assert o_s.dom == o_t.dom and i_t.codom == o_t.codom
self.S, self.T = i_s.codom, i_t.codom
self.I, self.O = i_s.dom, o_s.dom
self.i_s, self.i_t = i_s, i_t
self.o_s, self.o_t = o_s, o_t
class PetriHom:
def __init__(self, sfun, tfun, ifun, ofun):
self.sfun = sfun
...
set
automatically handles deduplication.
Set theory
The cardinality of a set \(|A|\) measures the number of elements in the set.
\(|\{red, green, blue\}|=3\)
\(|\B|=2\)
\(|\{\}| = 0\)
\(|\{\bullet \}| = 1\)
\(|\N| = \infty\)
. . .
Set theory: definitions
The identity function for a set \(X\) sends each \(x \in X\) to itself.
The composition of functions \(f: A\rightarrow B\) and \(g: B \rightarrow C\) is a function \(f \then g: A \rightarrow C\) which sends each \(a \in A\) to \(g(f(a))\).
Set theory: on the computer
id(X::Set) = Fun(X, X, Dict(x=>x for x in X))
compose(f::Fun, g::Fun) =
Fun(f.dom, g.codom, Dict([a=>g.fun[f.fun[a]] for a in f.dom]))
def id(X): Fun(X, X, {x:x for x in X})
def compose(f, g): return Fun(f.dom, g.codom,
for a in f.dom}) {a : g.fun[f.fun[a]]
Set theory: definitions
An injective function does not send distinct elements in \(A\) to the same element of \(B\).
\(\forall a,a' \in A, a\ne a' \implies f(a) \ne f(a')\)
A surjective function has for each element in \(B\) some element of \(A\) mapping to it.
\(\forall b \in B, \exists a \in A, f(a)=b\)
\(im(f) = B\)
A bijective function is one which is both injective and surjective.
…
Set theory: theorems
For any \(f: A\rightarrow B\), if \(|A| > |B|\) then \(f\) is not injective.
Any lossless compression algorithm which makes some inputs smaller will also make some other inputs larger.
…
Set theoryII: examples
injective(f::Fun) = length(image(f)) == length(f.dom)
surjective(f::Fun) = length(image(f)) == length(f.codom)
bijective(f::Fun) = injective(f) && bijective(f)
def injective(f):
return len(image(f)) == len(f.dom)
def surjective(f):
return len(image(f)) == len(f.codom)
def bijective(f):
return injective(f) and bijective(f)
Julia isn’t that different from Python. Should be straightforward to pick up.
Set theoryV: definitions
The intersection of sets.
\(X \cap Y := \{elem\ |\ elem \in X \land elem \in Y\}\)
The union of sets.
\(X \cup Y := \{elem\ |\ elem \in X \lor elem \in Y\}\)
…
Set theoryV: definitions
The product (cartesian product) of sets
\(X \times Y := \{(x,y)\ |\ x \in X \lor y \in Y\}\)
The coproduct (disjoint union) of sets
\(X + Y := \{(x,y)\ |\ x \in X \lor y \in Y\}\)
The exponential of sets
\(Y^X := \{f\ |\ f:X \rightarrow Y\) is a function \(\}\)
…
Set theoryV: theorems
These operations are symmetric and associative:
\(A \cap B = B \cap A\)
\(A \cup B = B \cup A\)
\(A \times B \cong B \times A\)
\(A + B \cong B + A\)
\((A \cap B) \cap C = A \cap (B\cap C)\)
\((A \cup B) \cup C = A \cup (B\cup C)\)
\((A \times B) \times C \cong A \times (B \times C)\)
\((A + B) + C \cong A + (B + C)\)
These operations are natural with respect to cardinality:
\(|A + B| = |A| + |B|\)
\(|A \times B| = |A| \times |B|\)
\(|B^A| = |B|^{|A|}\)
This means we can interpret arithmatic equalities as isomorphisms of sets!
\(Z^{X+Y} \cong Z^X \times X^Y\)
Category theory: definitions
A small category \(C\) consists of
- a set \(C_0\) of objects
- for every \(x,y \in C_0\), a set \(\mathrm{Hom}_C(x,y)\) of morphisms from \(x\) to \(y\)
- for every \(x,y,z \in C_0\), a composition function \(\then \colon \mathrm{Hom}_C(x,y) \times \mathrm{Hom}_(y,z) \to \mathrm{Hom}(x,z)\)
- for every \(x \in C_0\), an identity morphism \(1_x \in \mathrm{Hom}_C(x,x)\)
such that
- for all \(x,y,z,w \in C_0\), \(f \in \mathrm{Hom}_C(x,y)\), \(g \in \mathrm{Hom}_C(y,z)\), \(h \in \mathrm{Hom}_C(z,w)\), \[ f \then (g \then h) = (f \then g) \then h \]
- for all \(x,y \in C_0\), \(f \in \mathrm{Hom}_C(x,y)\), \[ 1_x \then f = f = f \then 1_y \]
These two laws are called the associativity law and unitality law respectively.
Category theory: examples
With a little practice, it’s easy to look at something and understand it as a category (as it is to look at something and see it as a set of things, connected via functions).
Category theorys useful because common, useful abstractions in different settings end up being revealed to have the same structure. We can use that when programming to have a single implementation.
Some examples of categories we have seen so far:
Category | Ob | Hom | Id | Comp |
---|---|---|---|---|
Set | sets | functions \(f: A\rightarrow B\) | identity function | function composition |
Grph | graphs | graph maps | identity map | map composition |
Petri | Petri nets | Petri maps | identity map | map composition |
Sub(X) | subsets of \(X\) | inclusions | identity function | function composition |
Mat | \(\N\) | \(n \times m\) matrices | e.g. \(\begin{pmatrix}1 & 0 \\ 0 & 1 \end{pmatrix}\) | matrix multiplication |
Nat | \(\N\) | \(n \leq m\) relation | \(\leq\) reflexivity | \(\leq\) transitivity |
Thinking categorically
In Set, the empty set is distinctive (initial) because it has exactly one function into every other set. The singleton set is terminal because it has exactly one function into it from every other set.
We can understand these notions of initial and terminal in other categories:
Category | Ob | Hom | Initial | Terminal |
---|---|---|---|---|
Set | sets | functions \(f: A\rightarrow B\) | \(\{\}\) | \(\{\bullet\}\) |
Grph | graphs | graph maps | ? | ? |
Petri | Petri nets | Petri maps | ? | ? |
Sub(X) | subsets of \(X\) | inclusions | ? | ? |
Mat | \(\N\) | \(n \times m\) matrices | ? | ? |
Nat | \(\N\) | \(n \leq m\) relation | ? | ? |
Category theory shows how operations like \(\subseteq\), \(\cap\), \(\cup\), \(+\), \(\times\), \(\cong\), exponential, pushout, pullback … all have characterizations like initial and terminal which make sense in any category.
Petri nets and chemical reaction networks: multiplication
Previously, we had a model sys
representing a metal solution oxidizing in a beaker in an atmosphere of \(O_2\) and \(N_2\). We’ve now realized there are two beakers with different solutions, and we want to duplicate that part of the reaction network (without duplicating the atmosphere and its reactions).
# Representation of duplication possibilities
= LabelledPetriNet([:Dup,:NoDup],
base :dup => (:Dup,:NoDup)=>(:Dup,:NoDup),
:nodup => (:Dup,:NoDup)=>(:Dup,:NoDup))
to_graphviz(base)
Petri nets and chemical reaction networks: pullback
# Representation of duplication
= LabelledPetriNet([:Dup1,:Dup2,:NoDup],
dup :dup1 => (:Dup1,:NoDup)=>(:Dup1,:NoDup),
:dup2 => (:Dup2,:NoDup)=>(:Dup2,:NoDup),
:nodup => (:Dup1,:Dup2,:NoDup)=>(:Dup1,:Dup2,:NoDup))
to_graphviz(dup)
= abstract_attributes.([base,sys,dup])
AB,AS,AD= dom.([AB,AS,AD])
abs_base,abs_sys,abs_dup = homomorphism(PetriNet(abs_sys),PetriNet(abs_base); initial=(S=[2,2,2,1,1], T=[1,2]))
sys_base = homomorphism(PetriNet(abs_dup),PetriNet(abs_base); initial=(S=[1,1,2], T=[1,1,2]))
dup_base pullback(sys_base,dup_base) |> apex |> to_graphviz
ODEs
Up until this point we manipulated Petri Nets as implicit presentations of ordinary differential equations. Now we consider ODEs in general and how we wish to compose them.
Logic Notation
Propositional logic:
- \(p \land q\) - “The proposition \(p\) and \(q\)”
- \(p \lor q\) - “The proposition \(p\) or \(q\)”
- \(\neg p\) - “The proposition not-\(p\)”
- \(p \implies q\) - “The proposition \(p\) implies \(q\)”
- \(E\) = “My favorite # is even”
- \(O\) = “My favorite # is odd”
- \(D\) = “My favorite # is divisible by 4.”
\(D \implies E\)
\(E \lor O\)
\(E \iff \neg O\)
. . .
Predicate logic:
- \(\forall x \in X: \phi(x)\) — “For all \(x\)’s in \(X\), it is the case that \(\phi\) of \(x\) is true”
- \(\exists x \in X: \phi(x)\) — “There exists an \(x\) in \(X\), such that \(\phi\) of \(x\) is true”
- \(E(x)\) = “x is even”
- \(O(x)\) = “x is odd”
- \(D(x)\) = “x is divisible by 4.”
\(\forall x \in \N: O(x) \lor E(x)\)
\(\neg \exists x \in \N, D(x) \land O(x)\)
\(\forall x,y \in \N: E(x)\land O(y)\implies O(x+y)\)
Note that or is inclusive.
Implicitly we are quantifying over natural numbers in the bottom.
Comprehension check
Let \(\phi(x) := x^2-2 = 0\) and \(\psi(x) := x \in \Q\)
Which statement is true? (tip: translate \(\phi\) and \(\psi\) to plain English first!)
A.) \(\exists n \in \R: \phi(n) \land \psi(n)\)
B.) \(\forall n \in \R: \phi(n) \lor \psi(n)\)
C.) \(\forall n \in \R: \phi(n) \implies \neg \psi(n)\)
D.) \(\exists n,m \in \R: \phi(n) \land \psi(m) \land \psi(n)\)
. . .
Two kinds of scientific modeling problems
- How do we represent the math on the computer?
- How do we efficiently compute solutions?
Category theory is a branch of math which is suited for addressing the first problem. It is the mathematics of structure, or of composition.
It guides us in telling us exactly what we have to implement in order to write code to do the sorts of compositions in the motivating examples (much like the theory of ODEs taught in this course instructs us how to analytically and numerically solve ODEs).
Another advantage of thinking category theoretically: it helps us write general / generalizable code. For example, it tells us an abstract structure of ‘product’, which we can instantiate in many settings.
- \(A \& B\) for boolean logic
- Intersection of sets, \(A \cap B\)
- Multiplication of numbers, \(A \times B\)
- Multiply spaces, e.g. circle \(\times\) line = cylinder
- Greatest common divisor \(gcd(A,B)\)
- Pair type \((A,B)\) in a programming language
- Greatest lower bound in a hierarchy
- Construct block matrix \(\begin{pmatrix}A & 0 \\ 0 & B \end{pmatrix}\)
Here are examples of ways to structure various mathematical models. On the left we have “directed wiring diagram”, on the right we have an “undirected wiring diagram”.
These are examples of “graphical syntax”. Understanding these as formal mathematical objects (which category theory allows us to do), we can very cleanly write the code once required to make them work in a variety of settings.
We also have a notion of “the structure of a product” which can be instantiated in many different settings to recover familiar operations.
Two kinds of scientific modeling problems
- How do we represent the math on the computer?
- How do we efficiently compute solutions?
Scientific programming traditionally prioritizes being able to answer the second question. This course focuses on mathematics of analysis, calculus, and linear algebra, which are obviously useful for this. Yet abstract algebra (e.g. group theory, ring theory) can be useful for this:
. . .
Gröbner basis, solving kinetics
Point groups, raman spectroscopy
When solving problems on a computer, we have to worry about these two questions.
Gröbner basis / Buchberger algorithm computes analytic solutions to chemical reaction networks.
Point groups describe molecular symmetries, providing qualitative predictions about the possible vibrations (and therefore spectroscopy results)
Motivating example: another system composition
def more_reactions(time):
= 20.0, 30.0, 20.0, 30.0 # L
V1, V2, V3, V4 = [996, 0.4, 0.05, 0.05] # g/L
in_concs
def yprime(t, concs):
= concs
H2O2_1, H2O_1, H_1, ..., H2O2_2, ... # NEW MATH TO BE WORKED OUT
0, time], [.1,.4,.3,.5]) solve_ivp(yprime, [
@relation
def glue_three_crns():
crn1(overlap1, overlap2)
crn2(overlap1, overlap2)
crn3(overlap2)
= ...
olap1_1 = ...
olap1_2
def more_reactions(): # much nicer
return compose(glue_three_crns,
[[olap1_1, olap1_2], [olap2_1, olap2_2, olap2_3]])
Another way Aditi could be cruel is to at the last second change the chemical reaction network. The network might be expressed as three different networks which are glued along some common boundaries, represented as black dots. Think of three different partial descriptions of the whole which need to be assembled together.
For example, you might have the gas phase reactions together, the the liquid phase reactions, the reactions/mass transfer at the interface, each designed by different teams. But the teams then need to come together, combining their work (which might have lots of conflicting naming and redundancy).
Comprehension check
How many functions are there from \(\{red, green, blue\}\) to \(\B\)?
A.) 3
B.) 6
C.) 8
D.) 9
How many functions are there from \(\R\) to \(1\)?
A.) 0
B.) 1
C.) \(|\N|\)
D.) \(|\R|\)
Bonus question, what are some sets \(A,B\) such that there are || functions \(A \rightarrow B\)?
Set theory: definitions
A subset of a set.
\(X \subseteq Y := \forall x \in X, x \in Y\)
- \(Evens \subseteq \N\), \(Odds \subseteq \N\)
- \(\{\} \subseteq \{red,green,blue\}\)
- \(\{red, blue\} \subseteq \{red,green,blue\}\)
- \(\{green, blue\} \subseteq \{red,green,blue\}\)
The image of a function \(f: A \rightarrow B\) is the subset of \(B\) mapped to by \(f\).
\(im(f) := \{f(a)\ |\ a \in A\}\)
- \(im(double: \N\rightarrow\N) = Evens\)
- \(im(\#ofLettersIsOdd: \{red,green,blue\}\rightarrow \B) = \B\)
- \(im(x\mapsto x^2: \R\rightarrow\R) = \R^{\geq 0}\)