algebraicjulia / algebraicpetri.jl Goto Github PK
View Code? Open in Web Editor NEWBuild Petri net models compositionally
Home Page: https://algebraicjulia.github.io/AlgebraicPetri.jl/
License: MIT License
Build Petri net models compositionally
Home Page: https://algebraicjulia.github.io/AlgebraicPetri.jl/
License: MIT License
Writing a wrapper for ACSetTransformation
similar to oapply_typed
that takes a LabelledPetriNet
instead of a uwd
Given:
Output: typed petrinet
Example visual if helpful Below
We should overload to_graphviz
instead of Graph
in order to do visualization.
When trying to remove input or output arcs from a Petri net, the function one_removed_subobs
fails to remove the part and returns the original Petri net. This leads to problems with the mca
recursion because some subACSets are not decreasing in size.
m3 = LabelledPetriNet(
[:X,:Y,:W,:Z],
:f=>(:X=>(:W, :Y, :Z))
)
m3 == dom(one_removed_subobs(m3,:I)[1])
The simulation tooling available in Petri.jl should be able to be migrated into AlgebraicPetri.jl, removing the need for dependence on Petri.jl.
The visualization module has lots of type piracies. For example, the Graph
constructor is defined for Multispan
, Subobject
, and ACSetTransformation
, but AlgebraicPetri doesn't own any of these types, nor are the methods defined here general enough for those types.
This is an issue to test the ASKEM integration workflow.
https://github.com/AlgebraicJulia/AlgebraicPetri.jl/blob/master/src/SubACSets.jl
This should be upstreamed to Catlab.jl
Not sure how they got there. Maybe from graphics of generating the plots?
We are working on removing LabelledArrays as a package dependency and all of our internal tooling should work with any sort of indexable type for labelled petri nets. While making a PR to remove this dependency from AlgebraicPetri I noticed that the new vectorfield_expr
outputs a function that strictly relies on LabelledArrays
using AlgebraicPetri
# Define some random model
sir_lpetri = LabelledPetriNet([:S, :I, :R], :inf => ((:S, :I), (:I, :I)), :rec => (:I, :R))
# Define some parameters and stuff as dictionaries
initial_concentrations = Dict(:S => 990, :I => 10, :R => 0)
model_rates = Dict(:inf => 0.001, :rec => 0.25)
# This works
du = Dict(:S=>0.0, :I=>0.0, :R=>0.0)
out = vectorfield(sir_lpetri)(du, initial_concentrations, model_rates, 0.01)
# This does not
du_expr = Dict(:S=>0.0, :I=>0.0, :R=>0.0)
out = vectorfield_expr(sir_lpetri)(du, initial_concentrations, model_rates, 0.01)
# vectorfield_expr only works with LVectors from LabelledArrays, it should work with Dictionaries/Named Tuples/etc.
using LabelledArrays
du_expr = LVector(;Dict(:S=>0.0, :I=>0.0, :R=>0.0)...)
out = vectorfield_expr(sir_lpetri)(du, LVector(;initial_concentrations...), LVector(;model_rates...), 0.01)
@slwu89 pinging since you did this implementation maybe you have a good idea of how to resolve this. It looks like the function relies on the shorthand of like du_expr[[:S,:I]]
to index multiple keys at one time which we shouldn't be reliant on.
I was trying to clean up #104 to get it merged when I found a very odd method error arising from the call to solve
here https://github.com/AlgebraicJulia/AlgebraicPetri.jl/blob/master/examples/enzymes/enzyme_reactions.jl#L251 (also occurs for other calls to solve
in that file). The error is very long, I'll paste it at the end of this post (without the stacktrace, which is even longer).
Here's my environment info:
julia> versioninfo()
Julia Version 1.8.5
Commit 17cfb8e65ea (2023-01-08 06:45 UTC)
Platform Info:
OS: Linux (x86_64-linux-gnu)
CPU: 32 × AMD Ryzen 9 5950X 16-Core Processor
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-13.0.1 (ORCJIT, znver3)
Threads: 1 on 32 virtual cores
Environment:
LD_LIBRARY_PATH = /app/lib
JULIA_EDITOR = code
JULIA_NUM_THREADS =
(@v1.8) pkg> st
Status `~/.julia/environments/v1.8/Project.toml`
[4f99eebe] AlgebraicPetri v0.8.4 `../../../Desktop/git/AlgebraicPetri.jl`
[134e5e36] Catlab v0.14.14
[31a5f54b] Debugger v0.7.8
[0c46a032] DifferentialEquations v7.7.0
[aa1ae85d] JuliaInterpreter v0.9.22
[91a5bcdd] Plots v1.38.5
[295af30f] Revise v3.5.1
[3c863552] Graphviz_jll v2.50.0+1
The error:
ERROR: MethodError: Cannot `convert` an object of type
LinearSolve.LinearCache{LabelledArrays.LArray{Float64{},2,Array{Float64{},2},(:K, :Kinact, :KK, :Kdeg, :KKinact, :S, :Sinact, :SS, :Sdeg, :SSinact, :L, :Linact, :LL, :Ldeg, :LLinact, :KS, :KSinact, :KL, :KLinact, :SK, :SKinact, :SL, :SLinact, :LK, :LKinact, :LS, :LSinact, :E, :KE, :Edeg, :SE, :LE)},LabelledArrays.LArray{Float64{},1,Array{Float64{},1},(:K, :Kinact, :KK, :Kdeg, :KKinact, :S, :Sinact, :SS, :Sdeg, :SSinact, :L, :Linact, :LL, :Ldeg, :LLinact, :KS, :KSinact, :KL, :KLinact, :SK, :SKinact, :SL, :SLinact, :LK, :LKinact, :LS, :LSinact, :E, :KE, :Edeg, :SE, :LE)},LabelledArrays.LArray{Float64{},1,Array{Float64{},1},(:K, :Kinact, :KK, :Kdeg, :KKinact, :S, :Sinact, :SS, :Sdeg, :SSinact, :L, :Linact, :LL, :Ldeg, :LLinact, :KS, :KSinact, :KL, :KLinact, :SK, :SKinact, :SL, :SLinact, :LK, :LKinact, :LS, :LSinact, :E, :KE, :Edeg, :SE, :LE)},SciMLBase.NullParameters{},RFLUFactorization{true,true},Tuple{LinearAlgebra.LU{Float64{},LabelledArrays.LArray{Float64, 2, Matrix{Float64}, (:K, :Kinact, :KK, :Kdeg, :KKinact, :S, :Sinact, :SS, :Sdeg, :SSinact, :L, :Linact, :LL, :Ldeg, :LLinact, :KS, :KSinact, :KL, :KLinact, :SK, :SKinact, :SL, :SLinact, :LK, :LKinact, :LS, :LSinact, :E, :KE, :Edeg, :SE, :LE)},Array{Int64{},1}},Array{Int64{},1}},LinearSolve.InvPreconditioner{LinearAlgebra.Diagonal{Float64{},LabelledArrays.LArray{Float64{},1,Array{Float64{},1},(:K, :Kinact, :KK, :Kdeg, :KKinact, :S, :Sinact, :SS, :Sdeg, :SSinact, :L, :Linact, :LL, :Ldeg, :LLinact, :KS, :KSinact, :KL, :KLinact, :SK, :SKinact, :SL, :SLinact, :LK, :LKinact, :LS, :LSinact, :E, :KE, :Edeg, :SE, :LE)}}},LinearAlgebra.Diagonal{Float64{},LabelledArrays.LArray{Float64{},1,Array{Float64{},1},(:K, :Kinact, :KK, :Kdeg, :KKinact, :S, :Sinact, :SS, :Sdeg, :SSinact, :L, :Linact, :LL, :Ldeg, :LLinact, :KS, :KSinact, :KL, :KLinact, :SK, :SKinact, :SL, :SLinact, :LK, :LKinact, :LS, :LSinact, :E, :KE, :Edeg, :SE, :LE)}},Float64{},true} to an object of type
LinearSolve.LinearCache{LabelledArrays.LArray{Float64{},2,Array{Float64{},2},(:K, :Kinact, :KK, :Kdeg, :KKinact, :S, :Sinact, :SS, :Sdeg, :SSinact, :L, :Linact, :LL, :Ldeg, :LLinact, :KS, :KSinact, :KL, :KLinact, :SK, :SKinact, :SL, :SLinact, :LK, :LKinact, :LS, :LSinact, :E, :KE, :Edeg, :SE, :LE)},LabelledArrays.LArray{Float64{},1,Array{Float64{},1},(:K, :Kinact, :KK, :Kdeg, :KKinact, :S, :Sinact, :SS, :Sdeg, :SSinact, :L, :Linact, :LL, :Ldeg, :LLinact, :KS, :KSinact, :KL, :KLinact, :SK, :SKinact, :SL, :SLinact, :LK, :LKinact, :LS, :LSinact, :E, :KE, :Edeg, :SE, :LE)},LabelledArrays.LArray{Float64{},1,Array{Float64{},1},(:K, :Kinact, :KK, :Kdeg, :KKinact, :S, :Sinact, :SS, :Sdeg, :SSinact, :L, :Linact, :LL, :Ldeg, :LLinact, :KS, :KSinact, :KL, :KLinact, :SK, :SKinact, :SL, :SLinact, :LK, :LKinact, :LS, :LSinact, :E, :KE, :Edeg, :SE, :LE)},SciMLBase.NullParameters{},RFLUFactorization{true,true},Tuple{LinearAlgebra.LU{Float64{},Matrix{Float64},Array{Int64{},1}},Array{Int64{},1}},LinearSolve.InvPreconditioner{LinearAlgebra.Diagonal{Float64{},LabelledArrays.LArray{Float64{},1,Array{Float64{},1},(:K, :Kinact, :KK, :Kdeg, :KKinact, :S, :Sinact, :SS, :Sdeg, :SSinact, :L, :Linact, :LL, :Ldeg, :LLinact, :KS, :KSinact, :KL, :KLinact, :SK, :SKinact, :SL, :SLinact, :LK, :LKinact, :LS, :LSinact, :E, :KE, :Edeg, :SE, :LE)}}},LinearAlgebra.Diagonal{Float64{},LabelledArrays.LArray{Float64{},1,Array{Float64{},1},(:K, :Kinact, :KK, :Kdeg, :KKinact, :S, :Sinact, :SS, :Sdeg, :SSinact, :L, :Linact, :LL, :Ldeg, :LLinact, :KS, :KSinact, :KL, :KLinact, :SK, :SKinact, :SL, :SLinact, :LK, :LKinact, :LS, :LSinact, :E, :KE, :Edeg, :SE, :LE)}},Float64{},true}
Closest candidates are:
convert(::Type{T}, ::T) where T at ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/base/Base.jl:61
LinearSolve.LinearCache{TA, Tb, Tu, Tp, Talg, Tc, Tl, Tr, Ttol, issq}(::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any) where {TA, Tb, Tu, Tp, Talg, Tc, Tl, Tr, Ttol, issq} at ~/.julia/packages/LinearSolve/7ER8k/src/common.jl:9
When precompiling the OrdinaryDiffEq and ModelingToolkit extensions, I get the following warnings:
2 dependencies had warnings during precompilation:
┌ AlgebraicPetriModelingToolkitExt [64666759-9bd1-54ef-b315-8c08f09f6f3d]
│ WARNING: Method definition (::Type{SciMLBase.ODEProblem{uType, tType, isinplace, P, F, K, PT} where PT where K where F where P where isinplace where tType where uType})(AlgebraicPetri.AbstractPetriNet{S, Ts, P} where P where Ts where S, Any) in module AlgebraicPetriOrdinaryDiffEqExt at /home/epatters/Local/algjulia/algpetri/ext/AlgebraicPetriOrdinaryDiffEqExt.jl:7 overwritten in module AlgebraicPetriModelingToolkitExt at /home/epatters/Local/algjulia/algpetri/ext/AlgebraicPetriModelingToolkitExt.jl:47.
│ ** incremental compilation may be fatally broken for this module **
└
┌ AlgebraicPetriCatalystExt [77e4b18c-d989-5097-a0f2-659ba3bbce58]
│ WARNING: Method definition (::Type{SciMLBase.ODEProblem{uType, tType, isinplace, P, F, K, PT} where PT where K where F where P where isinplace where tType where uType})(AlgebraicPetri.AbstractPetriNet{S, Ts, P} where P where Ts where S, Any) in module AlgebraicPetriOrdinaryDiffEqExt at /home/epatters/Local/algjulia/algpetri/ext/AlgebraicPetriOrdinaryDiffEqExt.jl:7 overwritten in module AlgebraicPetriModelingToolkitExt at /home/epatters/Local/algjulia/algpetri/ext/AlgebraicPetriModelingToolkitExt.jl:47.
│ ** incremental compilation may be fatally broken for this module **
│
│ WARNING: Method definition (::Type{SciMLBase.ODEProblem{uType, tType, isinplace, P, F, K, PT} where PT where K where F where P where isinplace where tType where uType})(AlgebraicPetri.AbstractPetriNet{S, Ts, P} where P where Ts where S, Any) in module AlgebraicPetriOrdinaryDiffEqExt at /home/epatters/Local/algjulia/algpetri/ext/AlgebraicPetriOrdinaryDiffEqExt.jl:7 overwritten in module AlgebraicPetriModelingToolkitExt at /home/epatters/Local/algjulia/algpetri/ext/AlgebraicPetriModelingToolkitExt.jl:47.
│ ** incremental compilation may be fatally broken for this module **
This is on Catlab v0.15.5.
With the update of Catalyst.jl from 6.13 to 6.14, it appears that using arrays to index symbol arrays is no longer supported. This breaks the CatalystInterop tooling.
It appears mca_help
, called within mca
, does not recurse. Consequently, searches for maximum common sub-ACSets only go, at most, two levels deep, one at the mca
level and one at the first (and only) call to mca_help
.
The vectorfield
function currently iterates through each element of the transition matrix, making each evaluation be O(ST) for number of states S and number of transitions T. This can cause significant slowdown on large sparse PetriNets, and could be improved by using a sparse representation of the transition matrix.
ModelComparison
: Need to wait until Subobject
regression is fixedProject.toml
[compat]
entries for (#151):
Catlab
AlgebraicRewriting
main
This issue arose from the following example with multiple states in the type system. As seen in the example, all states wind up being typed to the :Pop
state type. I think if lines 22 and 28 in prim_petri
were replaced with s=i
it would address the issue.
const ontology = LabelledPetriNet(
[:Pop, :Vac],
:infect => ((:Pop, :Pop) => (:Pop, :Pop)),
:disease => (:Pop => :Pop),
:strata => (:Pop => :Pop),
:vaccinate => ((:Pop, :Vac) => :Pop),
:produce => (() => :Vac),
)
Graph(ontology)
sirv_uwd = @relation () where (S::Pop, I::Pop, R::Pop, vaccine::Vac, V::Pop) begin
infect(S, I, I, I)
disease(I, R)
vaccinate(S, vaccine, V)
produce(vaccine)
end
to_graphviz(sirv_uwd, box_labels = :name, junction_labels = :variable)
Hi @mehalter!
I'm trying to implement this simple multigroup model using AlgebraicPetri, and (probably due to my lack of understanding), have run into issues.
typed_product
, my recovery rate disappears. How do I keep it in my stratified model?concentrations
and rates
throws an error, as the keys are tuples rather than symbols.using AlgebraicPetri
using AlgebraicPetri.TypedPetri
using Catlab.Programs
using Catlab.Graphics
using Catlab.CategoricalAlgebra
using Catlab.WiringDiagrams
using GraphViz
using LabelledArrays
using OrdinaryDiffEq
using Plots
display_uwd(ex) = to_graphviz(ex, box_labels=:name, junction_labels=:variable, edge_attrs=Dict(:len=>".75"));
infectious_ontology = LabelledPetriNet(
[:Pop],
:infect => ((:Pop, :Pop)=>(:Pop, :Pop)),
:disease => (:Pop => :Pop),
:strata => (:Pop => :Pop)
)
sir_relation = @relation () where {(S::Pop, I::Pop, R::Pop)} begin
infect(S, I, I, I)
disease(I, R)
end
display_uwd(sir_relation)
sir_model = oapply_typed(infectious_ontology, sir_relation, [:infection, :recovery])
sir_model = add_params(sir_model,
Dict(:S => 0.99, :I => 0.01, :R => 0.0),
Dict(:infection => 0.5, :recovery => 0.25))
sir_pn = dom(sir_model)
sir_prob = ODEProblem(vectorfield(sir_pn), concentrations(sir_pn), (0.0, 40.0), rates(sir_pn))
sir_sol = solve(sir_prob, Rosenbrock32())
plot(sir_sol)
risk_relation = @relation () where {(H::Pop, L::Pop)} begin
infect(H, H, H, H)
infect(L, L, L, L)
infect(H, L, H, L)
infect(L, H, L, H)
end
risk_model = oapply_typed(infectious_ontology, risk_relation, [:infect_hh, :infect_ll, :infect_hl, :infect_lh])
risk_model = add_params(risk_model,
Dict(:H => 500.0, :L => 500.0),
Dict(:infect_hh => 1.0, :infect_ll => 1.0, :infect_hl => 1.0, :infect_lh => 1.0))
sir_model = add_reflexives(sir_model, [[:strata], [:strata], [:strata]], infectious_ontology)
risk_model = add_reflexives(risk_model, [[:strata], [:strata]], infectious_ontology)
sir_risk = typed_product(sir_model, risk_model)
sir_risk_pn = dom(sir_risk)
sir_risk_pn
It would be awesome to have an SDE model of PetriNets with the interface
pn = Petri.Model(...)
p, cb = SDEProblem(pn, u0, params, tspan)
solve(p, callback=cb)
m1 = LabelledPetriNet(
[:X,:Y,:W,:Z,:V],
:f=>((:X, :W)=>(:Y, :Z))
)
m2 = LabelledPetriNet(
[:X,:Y,:Z],
:f=>((:X, :Y)=>:Z),
:g=>(:Z=>:X)
)
test = mca(m1,m2)
Fix interface with Catlab to match the new Catlab v0.7.3 changes. Mainly FinOrd
is now FinSet
This simple SEIR example gives a wiring from I to R in the UWD (correct) but from E to R in the labelled Petri net.
using AlgebraicPetri,AlgebraicPetri.TypedPetri
using Catlab, Catlab.CategoricalAlgebra, Catlab.Programs
using Catlab.WiringDiagrams, Catlab.Graphics
using AlgebraicDynamics.UWDDynam
epi_transitions = LabelledPetriNet(
[:Pop],
:infection=>((:Pop, :Pop)=>(:Pop, :Pop)),
:progression=>(:Pop=>:Pop),
:recovery=>(:Pop=>:Pop)
)
seir_uwd = @relation () where (S::Pop, E::Pop, I::Pop, R::Pop) begin
infection(S, I, E, I)
progression(E, I)
recovery(I, R)
end
to_graphviz(seir_uwd, box_labels=:name, junction_labels=:variable)
seir_acst = oapply_typed(epi_transitions, seir_uwd, [:β, :δ, :γ])
seir_lpn = dom(seir_acst)
Graph(seir_lpn)
@jpfairbanks found that changing the order to (S::Pop, I::Pop, E::Pop, R::Pop)
in the @relation
fixed this.
mca
currently searches by taking the list of subACSets formed by removing a single part from each object of the smaller input ACSet. It then looks for a mono from each subACSet in the list into the larger input ACSet. If it finds any monos, it returns the largest ones, otherwise it recurses on the list of subACSets.
Unfortunately, at each round the subACSets in the list can be of varying size, and if it finds a mono for one of the smaller subACSets, it will terminate without further searching the larger subACSets.
For example, in the following mca
returns the four separate nodes, rather than a vector of isomorphic cases of the single transition with a single input and single output.
m3 = LabelledPetriNet(
[:X,:Y,:W,:Z],
:f=>(:X=>(:W, :Y, :Z))
)
m4 = LabelledPetriNet(
[:X,:W,:Y,:Z],
:f=>((:W, :X, :Y)=>:Z)
)
test = mca(m3,m4)
I've noticed that the function returned from vectorfield
can tend to be unstable when working with a combination of ForwardDiff and functions as rates. I feel like this can be improved and optimized by constructing a specialized function expression instead of returning a single function that works with a wide range of parameter types.
the SIR example construction in the docstring for LabelledReactionNet
errors for me.
ReactionNet{Float64, Float64}([:S=>10,:I=>1,:R=>0],
(:inf=>0.5)=>((1,2)=>(2,2)),
(:rec=>0.1)=>(2=>3))
ERROR: MethodError: Cannot `convert` an object of type Pair{Symbol, Int64} to an object of type Float64
All the other code examples in AlgebraicPetri.jl
work for me, though, as well as the SIR example from the tests:
LabelledReactionNet{Float64, Float64}([:S=>1.0, :I=>0.0, :R=>0.0],
(:inf=>0.5)=>((:S,:I)=>(:I,:I)),
(:rec=>0.1)=>(:I=>:R))
This issue is used to trigger TagBot; feel free to unsubscribe.
If you haven't already, you should update your TagBot.yml
to include issue comment triggers.
Please see this post on Discourse for instructions and more details.
Hi AlgPetri team,
I'm interested in gluing PNs along transitions. This is especially useful in modeling manufacturing, logistics, and supply chain systems, see an example below for informal "gluing" of 2 kanban production cells together along the transition
A simply hacky proof of concept is below...I'm really curious to hear if this functionality could be developed into a PR that I can work on to add this feature to the package! Thanks.
using Catlab, Catlab.CategoricalAlgebra, Catlab.WiringDiagrams, Catlab.Programs, Catlab.Graphics
using AlgebraicPetri
draw(uwd::AbstractUWD; kwargs...) = to_graphviz(uwd, junction_labels=:variable, box_labels=:name; kwargs...);
const OpenLabelledPetriNetObUntypedT, OpenLabelledPetriNetUntypedT = OpenACSetTypes(AlgebraicPetri.LabelledPetriNetUntyped,:T)
const OpenLabelledPetriNetObT, OpenLabelledPetriNetT = OpenLabelledPetriNetObUntypedT{Symbol}, OpenLabelledPetriNetUntypedT{Symbol}
OpenT(p::AbstractLabelledPetriNet, legs...) = begin
t_idx = Dict(tname(p, t)=>t for t in 1:nt(p))
OpenLabelledPetriNetT(p, map(l->FinFunction(map(i->t_idx[i], l), nt(p)),legs)...)
end
annihilation = LabelledPetriNet([:X],
:T => (:X => ())
)
creation = LabelledPetriNet([:X],
:T => (() => :X)
)
annihilation_o = OpenT(annihilation, [:T])
creation_o = OpenT(creation, [:T])
join_pattern = @relation (Event,) begin
X(Event)
Y(Event)
end
joined_pn = oapply(join_pattern,
Dict(
:X => annihilation_o,
:Y => creation_o
)
)
Graph(annihilation)
Graph(creation)
draw(join_pattern)
Graph(apex(joined_pn))
Currently the methods don't have any doc strings, this should be fixed
Examples for subobject algebras and parameter estimation with Catalyst should be added to the AlgebraicPetri documentation.
@mehalter, are the docs section for library reference broken? Have they ever worked?
The following tests were removed from @kris-brown's AlgebraicJulia/Catlab.jl#425 to avoid code duplication from this repo. This issue is a reminder to add the tests here once the DPO feature is merged and released in Catlab. Actually, I only see one @test
below, so the test assertions should probably be bulked up a bit too.
sir=EpiRxnNet((:S=>100, :I=>1, :R=>0),
(:inf,.03)=>((:S,:I)=>(:I,:I)),
(:rec,.25)=>(:I=>:R));
seir = EpiRxnNet((:S=>100,:I=>1,:E=>1,:R=>0),
(:inf,.03)=>((:S,:I)=>(:I,:I)),
(:rec,.25)=>(:I=>:R),
(:inc,.1)=>(:E=>:I),
(:exp,.1)=>((:S,:I)=>(:E,:I)));
Rg = EpiRxnNet((:S=>100,:I=>1,:E=>1),
(:exp,.1)=>((:S, :I)=>(:E, :I)),
(:inc,.1)=>(:E => :I))
Lg = EpiRxnNet((:S=>100,:I=>1),)
L=ACSetTransformation(Lg,Lg,S=[1,2]);
R=ACSetTransformation(Lg,Rg,S=[1,2]);
m=ACSetTransformation(Lg, sir,S=[1,2]);
@test is_isomorphic(seir, rewrite_match(L,R,m))
Lg = EpiRxnNet((:S=>100,:I=>1),)
Rg = EpiRxnNet((:S=>100,:I=>1), (:sus,.1)=>(:I=>:S))
L=ACSetTransformation(Lg,Lg,S=[1,2]);
R=ACSetTransformation(Lg,Rg,S=[1,2]);
m=ACSetTransformation(Lg, seir,S=[1,2]);
seirs = rewrite_match(L,R,m)
Lg = EpiRxnNet((:I=>1),)
Rg = EpiRxnNet((:I=>1,:D=>0), (:die,.1)=>(:I=>:D))
L=ACSetTransformation(Lg,Lg,S=[1]);
R=ACSetTransformation(Lg,Rg,S=[1]);
m=ACSetTransformation(Lg, seir,S=[2]);
seird = rewrite_match(L,R,m)
i am in the process of reading through the RxNet paper (i don't really know CT)
right now, im just recreating tons and tons of petri nets with one or two edges different and its starting to get cumbersome.
i wrote two helpers that made it easy to add transitions and get out the (n, ts)
tuple used to construct the LabelledPetriNet
one problem i kept running into was not being able to easily remove edges/states.
maybe it doesn't fit Catlabs philosophy or something, but my thought was, if we are creating sets of models to compare, it would be nice to "only store the diff", instead of lugging around a bunch of copies. in the example below, i wanted to start from the most basic SIR model and progressively work up to SEIRHD. im able to get part of the way there but I didn't see anything related to deleting transitions and species. id love some help on this.
at some point, i want to build some tools to be able to ask questions like "compare the death counts of all the models that take :mutation => (:R=>:S)
into account"
thanks for the amazing package!
function add_things!(p::AbstractLabelledPetriNet, ts::Vararg{Union{Pair,Tuple}})
n = snames(p)
state_idx = AlgebraicPetri.state_dict(n)
for (name, (ins, outs)) in ts
i = add_transition!(p, tname=name)
ins = AlgebraicPetri.vectorify(ins)
outs = AlgebraicPetri.vectorify(outs)
add_inputs!(p, length(ins), repeat([i], length(ins)),
map(x -> state_idx[x], collect(ins)))
add_outputs!(p, length(outs), repeat([i], length(outs)),
map(x -> state_idx[x], collect(outs)))
end
p
end
function get_things(p)
is_ = inputs.((p,), 1:nt(p))
os_ = outputs.((p,), 1:nt(p))
tns = tnames(p)
sns = snames(p)
(sns, [tns[i] => (sns[is_[i]] => sns[os_[i]]) for i in 1:nt(p)])
end
sir_n = [:S, :I, :R]
sir_t = [:exp => ((:S, :I) => (:I, :I)), :rec => (:I => :R)]
sir = LabelledPetriNet(
sir_n, sir_t...
)
sird = deepcopy(sir)
add_species!(sird; sname=:D)
@test has_species(sird, :D)
add_things!(sird, :death => (:I => :D))
n, ts = get_things(sird)
@test LabelledPetriNet(n, ts...) == sird
# how do i split I into I and E?
seird = LabelledPetriNet(
[:S, :E, :I, :R, :D],
:exp => ([:S, :I] => [:E, :I]),
:conv => (:E => :I),
:rec => (:I => :R),
:death => (:I => :D)
)
Adding reflexive transitions to an already stratified model doesn't work because the transition names are tuples. This prevents doing multiple stratifications in the straightforward, iterative way. (There is a work around by adding the extra reflexive transitions to the initial model in the beginning before the first stratification, but this impairs a general model building/exploration workflow.) The code below sets up and attempts multiple stratification, but errors at trying to augment the already stratified model with reflexive transitions.
Note that the available flatten_labels
function does not work in this case because the stratified model is already typed and, hence, an ACSetTransformation
, not a LabelledPetriNet
.
using AlgebraicPetri, AlgebraicPetri.TypedPetri
using Catlab.Programs, Catlab.Graphics
using Catlab.CategoricalAlgebra
const infectious_ontology = LabelledPetriNet(
[:Pop],
:infect => ((:Pop, :Pop) => (:Pop, :Pop)),
:disease => (:Pop => :Pop),
:strata => (:Pop => :Pop)
)
Graph(infectious_ontology)
sir_uwd = @relation () where (S::Pop, I::Pop, R::Pop) begin
infect(S, I, I, I)
disease(I, R)
end
to_graphviz(sir_uwd, box_labels = :name, junction_labels = :variable)
tnames = [:beta, :gamma]
typed_sir = oapply_typed(infectious_ontology, sir_uwd, tnames)
Graph(dom(typed_sir))
N = 2
snames = [Symbol("Age$i") for i in 1:N]
typed_age = pairwise_id_typed_petri(infectious_ontology, :Pop, :infect, snames)
Graph(dom(typed_age))
typed_age_aug = add_reflexives(
typed_age,
repeat([[:disease]], N),
infectious_ontology
)
Graph(dom(typed_age_aug))
typed_sir_aug = add_reflexives(
typed_sir,
[[:strata], [:strata], [:strata]],
infectious_ontology
)
Graph(dom(typed_sir_aug))
typed_sir_age = typed_product(typed_sir_aug, typed_age_aug)
Graph(dom(typed_sir_age))
M = 2
snames = [Symbol("Country$i") for i in 1:M]
# Need a function like `pairwise_id_typed_petri` but without identity
travel_uwd = @relation () where (Country1::Pop, Country2::Pop) begin
strata(Country1, Country2)
strata(Country2, Country1)
end
to_graphviz(travel_uwd, box_labels = :name, junction_labels = :variable)
typed_travel = oapply_typed(infectious_ontology, travel_uwd, [:Country1_Country2, :Country2_Country1])
Graph(dom(typed_travel))
# Augment the travel model with reflexives
typed_travel_aug = add_reflexives(
typed_travel,
repeat([[:infect, :disease]], M),
infectious_ontology
)
typed_sir_age_aug = add_reflexives(
typed_sir_age,
repeat([[:strata]], 3 * N),
# [[], [], [], [], [], []],
infectious_ontology
)
Graph(dom(typed_sir_age_aug))
typed_sir_age_travel = typed_product(typed_sir_age_aug, typed_travel_aug)
Graph(dom(typed_sir_age_travel))
A food web is like a petri net where the species are species of organisms and the transitions of consumption events. We should be able to represent them with Dynamical systems based on a conservation of biomass principle. AlgPetri should be able to support the hierarchical definition of a FoodWeb.
Howard Odom (UNC grad, UF Prof) had a theory of ecological systems theory back in the 1970s, which is very compatible with modern ACT approaches. We could implement these diagrams using Decorated Cospans to describe large ecosystems as interacting subsystems.
A good start would be implementing a foodweb as a petrinet (or general open dynamical system connected by resource sharing). Then we could show that you can do Trophic Species Reduction, which merges species if they have the same set of preditors and prey as a symbolic simplification in the resulting category.
As of AlgebraicJulia/Catlab.jl#768 released in v0.14.17, Catlab now supports cascading deletion of parts in acsets (see cascading_rem_part(s)!
). The function rm_cascade_subobj
in SubACSets.jl
can now be removed.
When looking at morphisms
With Julia v1.9 PrecompileTools.jl allows us to easily do some caching during precompilation of native code. We should use this to eagerly precompile some common functions with different types that are commonly used.
For the design of the PetriFunctors, I think we want to have something like the following:
abstract AbstractFunctor end # this type should be imported from Catlab.
abstract AbstractLaxitor end # this type should be imported from Catlab.
struct PetriDecorator <: AbstractFunctor end
struct PetriLaxitor <: AbstractLaxitor end
struct LaxMonoidalFunctor{Ftr, Lxr} <: AbstractFunctor
F::Ftr
L::Lxr
end
D = LaxMonoidal(PetriDecorator(), PetriLaxitor())
"""we follow FinOrd's lead in taking the skeleton of FinSet so we work with Petri nets
where the state space is a single Int n rather than the range 1:n.
A functor from FinOrd to Set has an objects part, which given an object n in FinOrd
(a natural number) should return a representation of F(n)::Set, sets can be represented as
a predicate that takes an element and returns true if the element is in the set. Here we take
any julia value and test whether it is a Petri net on n states.
"""
function (pd::PetriDecorator)(n::FinOrdOb) begin
return p -> typeof(p) == Petri.Model && (p.S == n)
end
"""mapping a FinOrdFunc over the transitions of a Petri net means renumbering the states
incident to a transition where the transitions of a petri net are stored as a list of tuples of lists.
This could be easily adapted for storing transitions in a list of pairs of multisets.
"""
map(f::Function, ts::Vector{Tuple{Vector{Int}, Vector{Int}}) = [(f.(t[1]), f.(t[2])) for t in ts]
"""
A functor from FinOrd to Set has a hom part, which given a hom f in FinOrd
(a function n::Int->m::Int) should return a representation of F(f)::F(n)->F(m),
here we implement this as a function that takes a Petri net of size n to a Petri net of size m,
such that the transitions are mapped appropriately.
"""
function (pd::PetriDecorator)(f::FinOrdFunc)
return (p::Petri.Model) -> Petri.Model(codom(f), map(f, p.Δ))
end
"""the laxitor takes a pair of decorations and returns the coproduct decoration. For Petri nets, it encodes
the idea that you shift the states of q up by the number of states in p.
"""
(l::PetriLaxitor)(p::Petri.Model, q::Petri.Model) = Petri.Model(p.S+q.S, hcat(p.Δ, map(x->x+p.S, q.Δ)))
Then the compose/otimes operation for petri decorated cospans can be implemented in terms of these functions and the universal contructions in catlab.categorical_algebra
. We use the empty structs because there is no information in the functor here it just tells us how we want to lift FinOrdOb
s and FinOrdFunc
s up to functions that operate on Petri.Model
s.
Once we implement the C-Set approach, the definitions of the function (pd::PetriDecorator)(f::FinOrdFunc)
and (l::PetriLaxitor)(p::Petri.Model, q::Petri.Model)
can be inferred from the definition of C-Sets, but for now we code it up manually as part of the "code that understands math" aspect of partially symbolic computing.
Just collecting some things I found confusing that I plan to make one or more PRs to address in the future
oapply_typed
do not say what the purpose of the 3rd argument (Vector{Symbol}
) is; update docstringr
at start of add_reflexives
docstring prevents it from workingn
species: https://en.wikipedia.org/wiki/Competitive_Lotka%E2%80%93Volterra_equationsI feel like there should be an exp
here, am I missing something?
AlgebraicPetri.jl/src/AlgebraicPetri.jl
Line 137 in 8c66422
Ah, the real problem is that they should no longer be called log_rates.
We need to check that we can run against Catlab#main.
It would be good to have mca
find the morphisms and return the spans, rather than just the subacsets.
It would be good to generalize mca
to find the maximum common sub-ACSet of an arbitrary number of input ACSets.
Comparison tests in test/ModelComparison.jl
pass on Catlab v0.16.9 but fail on later versions.
I am not sure what the intention of these tests is, but note that by changing line 32 in test/ModelComparison.jl
from
so = Subobject.(legs(c_res))
to
so = Subobject.(feet(c_res))
, all following logical tests pass save for:
¬(A ∧ B) != ¬(A) ∨ B
and
implies((A∧B), A) == A∨B
Those tests further pass if ==
and !=
are substituted in respectively.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.