functionelastic_collision!(a, b, f =nothing) v1, v2, x1, x2 = a.vel, b.vel, a.pos, b.poslength(v1) !=2&&error("This function works only for two dimensions.") r1 = x1 .- x2; r2 = x2 .- x1 m1, m2 = f ==nothing ? (1.0, 1.0) : (getfield(a, f), getfield(b, f)) !(dot(r2, v1) >0&&dot(r2, v1) >0) &&returnfalse f1,f2 = (2m2/(m1+m2)), (2m1/(m1+m2)) ken =norm(v1)^2+norm(v2)^2 dx = a.pos .- b.pos dv = a.vel .- b.vel n =norm(dx)^2 n ==0&&returnfalse# do nothing if they are at the same position a.vel = v1 .- f1 .* ( dot(v1 .- v2, r1) / n ) .* (r1) b.vel = v2 .- f2 .* ( dot(v2 .- v1, r2) / n ) .* (r2)returntrueend
create-sheep initial-number-sheep ; create the sheep, then init vars[set shape "sheep"set color whiteset size 1.5; easier to seeset label-color blue -2set energy random (2* sheep-gain-from-food) setxy random-xcor random-ycor]
to eat-sheep ; wolf procedure let prey one-of sheep-here ; grab a random sheepif prey != nobody [ ; did we get one? if so, ask prey [ die ] ; kill it, and...set energy energy + wolf-gain-from-food ; get energy from eating ]endto death ; turtle procedure (i.e. both wolf and sheep procedure); when energy dips below zero, dieif energy <0 [ die ]end
; stop the model if there are no wolves and no sheep; stop the model if there are no wolves and the number of sheep gets very largeifnotany? wolves and count sheep >max-sheep [ user-message "The sheep have inherited the earth" stop ]ask sheep [ move;in this version, sheep eat grass, grass grows, and it costs sheep energy to moveif model-version ="sheep-wolves-grass" [set energy energy -1; deduct energy for sheep only if running sheep-wolves-grass model version eat-grass ; sheep eat grass only if running the sheep-wolves-grass model version death ; sheep die from starvation only if running the sheep-wolves-grass model version ] reproduce-sheep ; sheep reproduce at a random rate governed by a slider]ask wolves [ moveset energy energy -1; wolves lose energy as they move eat-sheep ; wolves eat a sheep on their patch death ; wolves die if they run out of energy reproduce-wolves ; wolves reproduce at a random rate governed by a slider]
Introduction - ACT philosophy
General-purpose code not a great syntax for scientific modeling
Some desired properties for a model representation:
Compositional, e.g.
Straightforward to make the Game of Life into a 3D game
Straightforward to share behaviors between wolves and sheep
Straightforward to combine COVID dynamics with a vaccination model
Transparent / verifiable / visualizable
Serializable + Implementation-agnostic
Flexible, e.g.
Straightforward to change semantics (deterministic, nondeterministic, probabilistic)
But these properties do tend to follow from giving a nice syntax category (e.g. presheaves, Poly) a computational semantics.
Introduction - problem statement
Rewrite rules (e.g. DPO, SPO) are a good syntax for small / local changes
How can we design a model with rewrite rules as primitive building blocks?
Need to specify control flow (if…then…else, while…)
Need to specify rewrite rules as well as control their matches
E.g. for each vertex \(v\):
If \(v\) has no neighbors, delete it
Else, look at its neighbors \(v\rightarrow v'\)
Flip a coin
If heads, add a loop to \(v'\)
functionsimulate(G::Graph)for v invertices(G) vsᵢ, vsₒ =inneighbors(G, v), outneighbors(G, v)ifisempty(vsᵢ ∪ vsₒ)apply_rule(DELETE_VERTEX, G; initial=(V=[v],))elsefor v′ in vsₒifrand([false, true])apply_rule(ADD_LOOP, G; initial(V=[v′],))endendendendend
Punchline
functionsimulate(G::Graph)for v invertices(G) vsᵢ, vsₒ =inneighbors(G, v), outneighbors(G, v)ifisempty(vsᵢ ∪ vsₒ)apply_rule(DELETE_VERTEX, G; initial=(V=[v],))elsefor v′ in vsₒifrand([false, true])apply_rule(ADD_LOOP, G; initial(V=[v′],))endendendendend
AlgebraicJulia is a computational category-theoretic software ecosystem.
Catlab.jl defines the core data structures and algorithms with a focus on computing with presheaf categories. For example, consider the category Grph of presheaves on the schema (i.e. small category) \(E \rightrightarrows V\)
usingCatlabg =path_graph(Graph, 3)
# Visualize with Graphvizto_graphviz(g)
AlgebraicRewriting.jl extends Catlab.jl to include many constructions developed by the graph transformation community.
res =apply_schedule(Loop, path_graph(Graph,3))res[1] |> extract |> to_graphviz
Agent-based modeling
A wire labeled by \(A \in Ob(\mathsf{C})\) represents the set \(Ob(A/\mathsf{\mathsf{C}}\text{-}\mathsf{Set})\).
\(A\) is the shape of an agent living in a world \(X\) given by the morphism \(A \rightarrow X\). Being able to keep track of a distinguished subobject enables us to have control over matches.
Previously, the morphism add_loop had the semantics of ‘add a loop to some vertex’
With the ability to have distinguished agents, this morphism has the semantics of ‘add a loop to this vertex’.
Primitive generators
Previously a Grph rewrite was \(Grph \rightarrow Grph + Grph\); now it is \(A/Grph \rightarrow B/Grph + A/Grph\) (in shorthand: \(A \rightarrow B + A\)).
two output ports, depending on whether the rule is successfully applied.
AlgebraicRewriting.jl rules can have application conditions and alternate semantics (e.g. SPO, SqPO).
G0, G1 =Graph(), Graph(1)del_v =Rule(create(G1), id(G0))aL =RuleApp(:addLoop, add_loop, G1) # agent in = agent out = • dV =RuleApp(:delV, del_v, id(G1), id(G0)) # explicit agentsrecipe = aL ⋅merge_wires(G1) ⋅ dVview_sched(recipe)
# Example: second vertex is current focusG =homomorphisms(G1, path_graph(Graph, 3))[2] res1, res2 =apply_schedule(recipe, G)@assertisempty(res1)res2 |> extract |> to_graphviz
Towards dynamic Kleisli categories - Mealy Machines
Control flow: consider a class of Mealy machines \(A \rightarrow A + A\) with \(S=\mathbb{N},s_0=3\) that acts as a for loop, by returning the first element iff \(s > 0\).
res =apply_schedule(Loop2, path_graph(Graph,3))res[1] |> extract |> to_graphviz
The Condition (or ControlFlow) primitive morphism is allowed to have nontrivial state but does not alter its inputs. Thus it must be a morphism \(A \rightarrow n \times A\).
Towards dynamic Kleisli categories - Mealy Machines
Let’s make a morphism which behaves like add_loop for the first three times the function is run and subsequently like delete_vertex:
Towards dynamic Kleisli categories - Quotienting by behavioral equivalence
Our monoidal product \(\otimes\) comes from the coproduct in Set, but it is not a coproduct in Meal.
Towards dynamic Kleisli categories - Quotienting by behavioral equivalence
The behavior of a morphism \(A \rightarrow B\) is an infinite tree whose nodes are functions \(A \rightarrow B\) and which has a branching factor of \(A\).
Impractical to draw the behavior tree explicitly with infinite sets.
However, for a Mealy machine with domain \(\Sigma_i A_i\):
It can be the case that state update \(\phi;\pi_1: S \times \Sigma_i
A_i \rightarrow S\) is purely determined by \(S \times I\).
When this condition is met, we can draw the tree as branching on the number of input wires into a box.
Towards dynamic Kleisli categories - Quotienting by behavioral equivalence
\(\mathfrak{c}\) is the cofree comonad functor \(\mathbf{Poly} \rightarrow \mathbf{Cat^\#}\)
\(\mathbf{Cat^\#}\) is the category of categories and cofunctors.
The set of behavior trees is in bijection with the underlying set of the final coalgebra on \(X \rightarrow B^AX^A\). But we prefer to think about the category of morphisms \(A \rightarrow B\):
this category makes explicit the dynamic nature of these systems, rather than just being a static object with a (fixed) initial state.
Towards dynamic Kleisli categories - Monadic output
We can readily generalize our notion of a morphism \(A \rightarrow B\).
\[Maybe = y+1\]
\[Writer = My\]
\[List = \sum_{N : \mathbb{N}} y^N\]
\[Lott = \sum_{N : \mathbb{N}} \Delta_N y^N\]
where \(M\) is a monoid and \(\Delta_N:=\{P:N\rightarrow [0,1]\ |\ 1=\sum P(i)\}\)
Conjectured trace structure
A trace structure licenses a graphical syntax with feedback loops.
Even without proving the purported trace structure, the theory has influenced an implementation.
Conjectured trace structure
A trace structure licenses a graphical syntax with feedback loops.
Even without proving the purported trace structure, the theory has influenced an implementation.
Towards dynamic Kleisli categories - Enriching in \(\mathbf{Cat}^\#\)
Poly morphisms are forwards on positions (trees) and backwards on directions (ways of updating): composition builds larger trees from smaller ones and decomposes updates of the larger system into corresponding updates of the smaller systems.
Towards dynamic Kleisli categories - Enriching in \(\mathbf{Cat}^\#\)
More primitive generators
Although RuleApp and ControlFlow are the workhorse generators, we need others to facilitate the changing of control from agent to agent. Consider the Query morphism \(A + B' \rightarrow A + B + 0\), whose basic behavior is determined by the types \(A\) and \(B\) on its ports: