Skip to content

Commit

Permalink
Add structured agents and related examples
Browse files Browse the repository at this point in the history
  • Loading branch information
thevolatilebit committed Feb 22, 2024
1 parent 033f202 commit 8652f3d
Show file tree
Hide file tree
Showing 13 changed files with 697 additions and 84 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "ReactiveDynamics"
uuid = "c7456e7d-545a-4b79-91ea-6e93d96dd4d4"
version = "0.2.7"
version = "0.2.8"

[deps]
ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8"
Expand Down
7 changes: 7 additions & 0 deletions src/ReactiveDynamics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ TheoryReactionNetwork = BasicSchema(
:ModalityAttributeT,
:PcsOptT,
:PrmAttributeT,
:BoolAttributeT,
], # AttrTypes
[
# species
Expand All @@ -47,6 +48,7 @@ TheoryReactionNetwork = BasicSchema(
(:specCost, :S, :SampleableAttributeT),
(:specReward, :S, :SampleableAttributeT),
(:specValuation, :S, :SampleableAttributeT),
(:specStructured, :S, :BoolAttributeT),
# transitions
(:trans, :T, :SampleableAttributeT),
(:transPriority, :T, :SampleableAttributeT),
Expand All @@ -55,6 +57,7 @@ TheoryReactionNetwork = BasicSchema(
(:transProbOfSuccess, :T, :SampleableAttributeT),
(:transCapacity, :T, :SampleableAttributeT),
(:transMaxLifeTime, :T, :SampleableAttributeT),
(:transPreAction, :T, :SampleableAttributeT),
(:transPostAction, :T, :SampleableAttributeT),
(:transMultiplier, :T, :SampleableAttributeT),
(:transName, :T, :DescriptiveAttributeT),
Expand All @@ -81,6 +84,7 @@ const ReactionNetworkSchema = FoldedReactionNetworkType{
Set{Symbol},
FoldedObservable,
Any,
Bool
}

Base.convert(::Type{Symbol}, ex::String) = Symbol(ex)
Expand All @@ -100,6 +104,7 @@ Base.convert(::Type{FoldedObservable}, ex::String) = eval(Meta.parse(ex))
prettynames = Dict(
:transRate => [:rate],
:specInitUncertainty => [:uncertainty, :stoch, :stochasticity],
:transPreAction => [:preAction, :pre],
:transPostAction => [:postAction, :post],
:transName => [:name, :interpretation],
:transPriority => [:priority],
Expand All @@ -117,6 +122,7 @@ defargs = Dict(
:transCycleTime => 0.0,
:transMaxLifeTime => Inf,
:transMultiplier => 1,
:transPreAction => :(),
:transPostAction => :(),
:transName => missing,
),
Expand All @@ -126,6 +132,7 @@ defargs = Dict(
:specCost => 0.0,
:specReward => 0.0,
:specValuation => 0.0,
:specStructured => false,
),
:P => Dict{Symbol,Any}(:prmVal => missing),
:M => Dict{Symbol,Any}(:metaVal => missing),
Expand Down
33 changes: 20 additions & 13 deletions src/compilers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,17 @@ end
Recursively substitute model variables. Subsitution pairs are specified in `varmap`.
"""
function recursively_substitute_vars!(varmap, ex)
ex isa Symbol && return (haskey(varmap, ex) ? varmap[ex] : ex)
ex isa Expr && for i = 1:length(ex.args)
if ex.args[i] isa Expr
recursively_substitute_vars!(varmap, ex.args[i])
else
(
ex.args[i] isa Symbol &&
haskey(varmap, ex.args[i]) &&
(ex.args[i] = varmap[ex.args[i]])
)
if ex isa Symbol
return haskey(varmap, ex) ? varmap[ex] : ex
elseif ex isa Expr
for i = 1:length(ex.args)
if ex.args[i] isa Expr
ex.args[i] = recursively_substitute_vars!(varmap, ex.args[i])
else
if ex.args[i] isa Symbol && haskey(varmap, ex.args[i])
ex.args[i] = varmap[ex.args[i]]
end
end
end
end

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

# the function shall be a function of the dynamic ReactionNetworkSchema structure: letex -> :(state -> $letex)
# eval the expression to a Julia function, save that function into the "compiled" acset
return eval(:(state -> $letex))

return eval(quote
function (state, transition)
$letex
end
end)
end

function get_wrap_fun(acs::ReactionNetworkSchema)
Expand All @@ -133,8 +139,9 @@ function skip_compile(attr)
(string(attr) == "trans")
end

function compile_attrs(acs::ReactionNetworkSchema)
species_names = collect(acs[:, :specName])
function compile_attrs(acs::ReactionNetworkSchema, structured_species)
species_names = setdiff(collect(acs[:, :specName]), structured_species)

prm_names = collect(acs[:, :prmName])
varmap = Dict([name => :(state.u[$i]) for (i, name) in enumerate(species_names)])
for name in prm_names
Expand Down
52 changes: 52 additions & 0 deletions src/interface/agents.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
export AbstractStructuredSpecies, BaseStructuredSpecies
export @structured
export add_structured_species!

# Abstract supertype of all structured species.
abstract type AbstractStructuredSpecies <: AbstractAlgebraicAgent end

# It comes handy to keep track of the transition the entity is assigned to (if).
# In general, we will probably assume that each "structured agent" type implements this field.
# Otherwise, it would be possible to implement getter and setter interface and use it from within ReaDyn.
@aagent FreeAgent struct BaseStructuredSpecies
bound_transition::Union{Nothing, ReactiveDynamics.Transition}
end

# We use this to let the network know that the type is structured.
function register_structured_species!(reaction_network, type)
if !(type reaction_network[:, :specName])
add_part!(reaction_network, :S; specName = type)
end

i = first(incident(reaction_network, type, :specName))
reaction_network[i, :specStructured] = true

return nothing
end

# Convenience macro to define structured species.
macro structured(network, type)
name = Docs.namify(type.args[2])

quote
$(AlgebraicAgents.aagent(BaseStructuredSpecies, AbstractStructuredSpecies, type, ReactiveDynamics))
register_structured_species!($(esc(network)), $(QuoteNode(name)))
end
end

# Add a structured agent instance to an instance of a reaction network.
function add_structured_species!(problem::ReactionNetworkProblem, agent)
entangle!(getagent(problem, "structured/$(nameof(typeof(agent)))"), agent)
end

import AlgebraicAgents

# By default, structured agents have no evolutionary rule.
AlgebraicAgents._projected_to(::AbstractStructuredSpecies) = nothing
AlgebraicAgents._step!(::AbstractStructuredSpecies) = nothing

# Tell if an agent is assigned to a transition, as a resource.
isblocked(a) = !isnothing(a.bound_transition)

# Priority with which an unbound agent will be assigned to a transition.
priority(a, transition) = 0.0
2 changes: 2 additions & 0 deletions src/interface/create.jl
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ function recursively_find_reactants!(reactants, pcs, ex::SampleableValues)
end
elseif isexpr(ex, :macrocall)
recursively_find_reactants!(reactants, pcs, ex.args[3])
elseif isexpr(ex, :call)
push!(reactants, ex.args[1])
else
push!(reactants, underscorize(ex))
end
Expand Down
4 changes: 3 additions & 1 deletion src/interface/reaction_parser.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using MacroTools: postwalk

struct FoldedReactant
species::Symbol
species::Union{Expr, Symbol}
stoich::SampleableValues
modality::Set{Symbol}
end
Expand Down Expand Up @@ -71,6 +71,8 @@ function recursive_find_reactants!(
4:length(ex.args),
)
recursive_find_reactants!(ex.args[3], mult, mods, reactants)
elseif isexpr(ex, :call)
push!(reactants, FoldedReactant(ex, mult, mods))
else
@error("malformed reaction")
end
Expand Down
5 changes: 3 additions & 2 deletions src/operators/equalize.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export @equalize
export equalize!, @equalize

expand_name_ff(ex) =
if ex isa Expr && isexpr(ex, :macrocall)
Expand Down Expand Up @@ -53,9 +53,10 @@ function equalize!(acs::ReactionNetworkSchema, eqs = [])
for attr in propertynames(acs.subparts)
attr == :specName && continue
attr_ = acs[:, attr]
for i = 1:length(attr_)
for i in eachindex(attr_)
attr_[i] = escape_ref(attr_[i], collect(keys(specmap)))
attr_[i] = recursively_substitute_vars!(specmap, attr_[i])
acs[i, attr] = attr_[i]
end
end

Expand Down
28 changes: 15 additions & 13 deletions src/operators/joins.jl
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
# model joins
export @join
export union_acs!, @join

using MacroTools
using MacroTools: prewalk

"""
Merge `acs2` onto `acs1`, the attributes in `acs2` taking precedence. Identify respective species given `eqs`, renaming species in `acs2`.
Merge `acs2` into `acs1`, the attributes in `acs2` taking precedence. Identify respective species given `eqs`, renaming species in `acs2`.
"""
function union_acs!(acs1, acs2, name = gensym("acs_"), eqs = [])
function union_acs!(acs1, acs2, name = gensym("acs"), eqs = [])
acs2 = deepcopy(acs2)
prepend!(acs2, name, eqs)

for i in parts(acs2, :S)
inc = incident(acs1, acs2[i, :specName], :specName)
isempty(inc) && (inc = add_part!(acs1, :S; specName = acs2[i, :specName]);
assign_defaults!(acs1))
return (acs1, acs2)
println(first(inc))
println(acs1[first(inc), :specModality])
println()
println(acs2[:, :specModality])

if isempty(inc)
inc = add_part!(acs1, :S; specName = acs2[i, :specName])
assign_defaults!(acs1)
end

union!(acs1[first(inc), :specModality], acs2[i, :specModality])

for attr in propertynames(acs1.subparts)
Expand All @@ -30,7 +30,9 @@ function union_acs!(acs1, acs2, name = gensym("acs_"), eqs = [])
new_trans_ix = add_parts!(acs1, :T, nparts(acs2, :T))
for attr in propertynames(acs2.subparts)
!occursin("trans", string(attr)) && continue
acs1[new_trans_ix, attr] .= acs2[:, attr]
for (ix1, ix2) in enumerate(new_trans_ix)
acs1[ix2, attr] = acs2[ix1, attr]
end
end

foreach(
Expand Down Expand Up @@ -69,10 +71,10 @@ function prepend!(acs::ReactionNetworkSchema, name = gensym("acs"), eqs = [])
for attr in propertynames(acs.subparts)
attr == :specName && continue
attr_ = acs[:, attr]
for i = 1:length(attr_)
for i in eachindex(attr_)
attr_[i] = escape_ref(attr_[i], collect(keys(specmap)))
attr_[i] = recursively_substitute_vars!(specmap, attr_[i])
attr_[i] isa Expr && (attr_[i] = prepend_obs(attr_[i], name))
acs[i, attr] = attr_[i]
end
end

Expand Down
Loading

0 comments on commit 8652f3d

Please sign in to comment.