Skip to content

Commit 8652f3d

Browse files
Add structured agents and related examples
1 parent 033f202 commit 8652f3d

File tree

13 files changed

+697
-84
lines changed

13 files changed

+697
-84
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "ReactiveDynamics"
22
uuid = "c7456e7d-545a-4b79-91ea-6e93d96dd4d4"
3-
version = "0.2.7"
3+
version = "0.2.8"
44

55
[deps]
66
ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8"

src/ReactiveDynamics.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ TheoryReactionNetwork = BasicSchema(
3737
:ModalityAttributeT,
3838
:PcsOptT,
3939
:PrmAttributeT,
40+
:BoolAttributeT,
4041
], # AttrTypes
4142
[
4243
# species
@@ -47,6 +48,7 @@ TheoryReactionNetwork = BasicSchema(
4748
(:specCost, :S, :SampleableAttributeT),
4849
(:specReward, :S, :SampleableAttributeT),
4950
(:specValuation, :S, :SampleableAttributeT),
51+
(:specStructured, :S, :BoolAttributeT),
5052
# transitions
5153
(:trans, :T, :SampleableAttributeT),
5254
(:transPriority, :T, :SampleableAttributeT),
@@ -55,6 +57,7 @@ TheoryReactionNetwork = BasicSchema(
5557
(:transProbOfSuccess, :T, :SampleableAttributeT),
5658
(:transCapacity, :T, :SampleableAttributeT),
5759
(:transMaxLifeTime, :T, :SampleableAttributeT),
60+
(:transPreAction, :T, :SampleableAttributeT),
5861
(:transPostAction, :T, :SampleableAttributeT),
5962
(:transMultiplier, :T, :SampleableAttributeT),
6063
(:transName, :T, :DescriptiveAttributeT),
@@ -81,6 +84,7 @@ const ReactionNetworkSchema = FoldedReactionNetworkType{
8184
Set{Symbol},
8285
FoldedObservable,
8386
Any,
87+
Bool
8488
}
8589

8690
Base.convert(::Type{Symbol}, ex::String) = Symbol(ex)
@@ -100,6 +104,7 @@ Base.convert(::Type{FoldedObservable}, ex::String) = eval(Meta.parse(ex))
100104
prettynames = Dict(
101105
:transRate => [:rate],
102106
:specInitUncertainty => [:uncertainty, :stoch, :stochasticity],
107+
:transPreAction => [:preAction, :pre],
103108
:transPostAction => [:postAction, :post],
104109
:transName => [:name, :interpretation],
105110
:transPriority => [:priority],
@@ -117,6 +122,7 @@ defargs = Dict(
117122
:transCycleTime => 0.0,
118123
:transMaxLifeTime => Inf,
119124
:transMultiplier => 1,
125+
:transPreAction => :(),
120126
:transPostAction => :(),
121127
:transName => missing,
122128
),
@@ -126,6 +132,7 @@ defargs = Dict(
126132
:specCost => 0.0,
127133
:specReward => 0.0,
128134
:specValuation => 0.0,
135+
:specStructured => false,
129136
),
130137
:P => Dict{Symbol,Any}(:prmVal => missing),
131138
:M => Dict{Symbol,Any}(:metaVal => missing),

src/compilers.jl

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,17 @@ end
2626
Recursively substitute model variables. Subsitution pairs are specified in `varmap`.
2727
"""
2828
function recursively_substitute_vars!(varmap, ex)
29-
ex isa Symbol && return (haskey(varmap, ex) ? varmap[ex] : ex)
30-
ex isa Expr && for i = 1:length(ex.args)
31-
if ex.args[i] isa Expr
32-
recursively_substitute_vars!(varmap, ex.args[i])
33-
else
34-
(
35-
ex.args[i] isa Symbol &&
36-
haskey(varmap, ex.args[i]) &&
37-
(ex.args[i] = varmap[ex.args[i]])
38-
)
29+
if ex isa Symbol
30+
return haskey(varmap, ex) ? varmap[ex] : ex
31+
elseif ex isa Expr
32+
for i = 1:length(ex.args)
33+
if ex.args[i] isa Expr
34+
ex.args[i] = recursively_substitute_vars!(varmap, ex.args[i])
35+
else
36+
if ex.args[i] isa Symbol && haskey(varmap, ex.args[i])
37+
ex.args[i] = varmap[ex.args[i]]
38+
end
39+
end
3940
end
4041
end
4142

@@ -114,7 +115,12 @@ function wrap_expr(fex, species_names, prm_names, varmap)
114115

115116
# the function shall be a function of the dynamic ReactionNetworkSchema structure: letex -> :(state -> $letex)
116117
# eval the expression to a Julia function, save that function into the "compiled" acset
117-
return eval(:(state -> $letex))
118+
119+
return eval(quote
120+
function (state, transition)
121+
$letex
122+
end
123+
end)
118124
end
119125

120126
function get_wrap_fun(acs::ReactionNetworkSchema)
@@ -133,8 +139,9 @@ function skip_compile(attr)
133139
(string(attr) == "trans")
134140
end
135141

136-
function compile_attrs(acs::ReactionNetworkSchema)
137-
species_names = collect(acs[:, :specName])
142+
function compile_attrs(acs::ReactionNetworkSchema, structured_species)
143+
species_names = setdiff(collect(acs[:, :specName]), structured_species)
144+
138145
prm_names = collect(acs[:, :prmName])
139146
varmap = Dict([name => :(state.u[$i]) for (i, name) in enumerate(species_names)])
140147
for name in prm_names

src/interface/agents.jl

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
export AbstractStructuredSpecies, BaseStructuredSpecies
2+
export @structured
3+
export add_structured_species!
4+
5+
# Abstract supertype of all structured species.
6+
abstract type AbstractStructuredSpecies <: AbstractAlgebraicAgent end
7+
8+
# It comes handy to keep track of the transition the entity is assigned to (if).
9+
# In general, we will probably assume that each "structured agent" type implements this field.
10+
# Otherwise, it would be possible to implement getter and setter interface and use it from within ReaDyn.
11+
@aagent FreeAgent struct BaseStructuredSpecies
12+
bound_transition::Union{Nothing, ReactiveDynamics.Transition}
13+
end
14+
15+
# We use this to let the network know that the type is structured.
16+
function register_structured_species!(reaction_network, type)
17+
if !(type reaction_network[:, :specName])
18+
add_part!(reaction_network, :S; specName = type)
19+
end
20+
21+
i = first(incident(reaction_network, type, :specName))
22+
reaction_network[i, :specStructured] = true
23+
24+
return nothing
25+
end
26+
27+
# Convenience macro to define structured species.
28+
macro structured(network, type)
29+
name = Docs.namify(type.args[2])
30+
31+
quote
32+
$(AlgebraicAgents.aagent(BaseStructuredSpecies, AbstractStructuredSpecies, type, ReactiveDynamics))
33+
register_structured_species!($(esc(network)), $(QuoteNode(name)))
34+
end
35+
end
36+
37+
# Add a structured agent instance to an instance of a reaction network.
38+
function add_structured_species!(problem::ReactionNetworkProblem, agent)
39+
entangle!(getagent(problem, "structured/$(nameof(typeof(agent)))"), agent)
40+
end
41+
42+
import AlgebraicAgents
43+
44+
# By default, structured agents have no evolutionary rule.
45+
AlgebraicAgents._projected_to(::AbstractStructuredSpecies) = nothing
46+
AlgebraicAgents._step!(::AbstractStructuredSpecies) = nothing
47+
48+
# Tell if an agent is assigned to a transition, as a resource.
49+
isblocked(a) = !isnothing(a.bound_transition)
50+
51+
# Priority with which an unbound agent will be assigned to a transition.
52+
priority(a, transition) = 0.0

src/interface/create.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,8 @@ function recursively_find_reactants!(reactants, pcs, ex::SampleableValues)
295295
end
296296
elseif isexpr(ex, :macrocall)
297297
recursively_find_reactants!(reactants, pcs, ex.args[3])
298+
elseif isexpr(ex, :call)
299+
push!(reactants, ex.args[1])
298300
else
299301
push!(reactants, underscorize(ex))
300302
end

src/interface/reaction_parser.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using MacroTools: postwalk
44

55
struct FoldedReactant
6-
species::Symbol
6+
species::Union{Expr, Symbol}
77
stoich::SampleableValues
88
modality::Set{Symbol}
99
end
@@ -71,6 +71,8 @@ function recursive_find_reactants!(
7171
4:length(ex.args),
7272
)
7373
recursive_find_reactants!(ex.args[3], mult, mods, reactants)
74+
elseif isexpr(ex, :call)
75+
push!(reactants, FoldedReactant(ex, mult, mods))
7476
else
7577
@error("malformed reaction")
7678
end

src/operators/equalize.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export @equalize
1+
export equalize!, @equalize
22

33
expand_name_ff(ex) =
44
if ex isa Expr && isexpr(ex, :macrocall)
@@ -53,9 +53,10 @@ function equalize!(acs::ReactionNetworkSchema, eqs = [])
5353
for attr in propertynames(acs.subparts)
5454
attr == :specName && continue
5555
attr_ = acs[:, attr]
56-
for i = 1:length(attr_)
56+
for i in eachindex(attr_)
5757
attr_[i] = escape_ref(attr_[i], collect(keys(specmap)))
5858
attr_[i] = recursively_substitute_vars!(specmap, attr_[i])
59+
acs[i, attr] = attr_[i]
5960
end
6061
end
6162

src/operators/joins.jl

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
# model joins
2-
export @join
2+
export union_acs!, @join
33

44
using MacroTools
55
using MacroTools: prewalk
66

77
"""
8-
Merge `acs2` onto `acs1`, the attributes in `acs2` taking precedence. Identify respective species given `eqs`, renaming species in `acs2`.
8+
Merge `acs2` into `acs1`, the attributes in `acs2` taking precedence. Identify respective species given `eqs`, renaming species in `acs2`.
99
"""
10-
function union_acs!(acs1, acs2, name = gensym("acs_"), eqs = [])
10+
function union_acs!(acs1, acs2, name = gensym("acs"), eqs = [])
1111
acs2 = deepcopy(acs2)
1212
prepend!(acs2, name, eqs)
13+
1314
for i in parts(acs2, :S)
1415
inc = incident(acs1, acs2[i, :specName], :specName)
15-
isempty(inc) && (inc = add_part!(acs1, :S; specName = acs2[i, :specName]);
16-
assign_defaults!(acs1))
17-
return (acs1, acs2)
18-
println(first(inc))
19-
println(acs1[first(inc), :specModality])
20-
println()
21-
println(acs2[:, :specModality])
16+
17+
if isempty(inc)
18+
inc = add_part!(acs1, :S; specName = acs2[i, :specName])
19+
assign_defaults!(acs1)
20+
end
21+
2222
union!(acs1[first(inc), :specModality], acs2[i, :specModality])
2323

2424
for attr in propertynames(acs1.subparts)
@@ -30,7 +30,9 @@ function union_acs!(acs1, acs2, name = gensym("acs_"), eqs = [])
3030
new_trans_ix = add_parts!(acs1, :T, nparts(acs2, :T))
3131
for attr in propertynames(acs2.subparts)
3232
!occursin("trans", string(attr)) && continue
33-
acs1[new_trans_ix, attr] .= acs2[:, attr]
33+
for (ix1, ix2) in enumerate(new_trans_ix)
34+
acs1[ix2, attr] = acs2[ix1, attr]
35+
end
3436
end
3537

3638
foreach(
@@ -69,10 +71,10 @@ function prepend!(acs::ReactionNetworkSchema, name = gensym("acs"), eqs = [])
6971
for attr in propertynames(acs.subparts)
7072
attr == :specName && continue
7173
attr_ = acs[:, attr]
72-
for i = 1:length(attr_)
74+
for i in eachindex(attr_)
7375
attr_[i] = escape_ref(attr_[i], collect(keys(specmap)))
7476
attr_[i] = recursively_substitute_vars!(specmap, attr_[i])
75-
attr_[i] isa Expr && (attr_[i] = prepend_obs(attr_[i], name))
77+
acs[i, attr] = attr_[i]
7678
end
7779
end
7880

0 commit comments

Comments
 (0)