Skip to content

Commit

Permalink
Structured species
Browse files Browse the repository at this point in the history
- An agent now implements a property pointing at
the species.
- Using `@structured` macro, it is possible to set
call a custom agent constructor and assign the species.
- Using `@move` macro, it is possible to
reuse LHS agents on the RHS and modify their species.
  • Loading branch information
thevolatilebit committed Mar 12, 2024
1 parent 2814b14 commit 4791a81
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 74 deletions.
14 changes: 11 additions & 3 deletions src/compilers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ end

reserved_names =
[:t, :state, :obs, :resample, :solverarg, :take, :log, :periodic, :set_params]
push!(reserved_names, :state)

function escape_ref(ex, species)
return if ex isa Symbol
Expand Down Expand Up @@ -103,6 +102,15 @@ function wrap_expr(fex, species_names, prm_names, varmap)
end
end

fex = prewalk(fex) do x
# here we convert the query metalanguage: @t() -> time(state) etc.
if isexpr(x, :macrocall) && (macroname(x) == :transition)
:transition
else
x
end
end

# substitute the species names with "pointers" into the state space: S -> state.u[1]
fex = recursively_substitute_vars!(varmap, fex)
# substitute the params names with "pointers" into the parameter space: β -> state.p[:β]
Expand Down Expand Up @@ -139,8 +147,8 @@ function skip_compile(attr)
(string(attr) == "trans")
end

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

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

# Abstract supertype of all structured species.
abstract type AbstractStructuredSpecies <: AbstractAlgebraicAgent end
abstract type AbstractStructuredToken <: 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
@aagent FreeAgent struct BaseStructuredToken
species::Union{Nothing,Symbol}
bound_transition::Union{Nothing,ReactiveDynamics.Transition}
past_bonds::Vector{Tuple{Symbol,Float64,Transition}}
end

# We use this to let the network know that the type is structured.
Expand All @@ -25,33 +27,45 @@ function register_structured_species!(reaction_network, type)
end

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

macro structured_token(network, type)
quote
$(AlgebraicAgents.aagent(
BaseStructuredSpecies,
AbstractStructuredSpecies,
BaseStructuredToken,
AbstractStructuredToken,
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)
return entangle!(getagent(problem, "structured/$(nameof(typeof(agent)))"), agent)
function add_structured_token!(problem::ReactionNetworkProblem, agent)
return entangle!(getagent(problem, "structured"), agent)
end

import AlgebraicAgents

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

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

# Add a record that an agent was used as "species" in a "transition".
function add_to_log!(a::AbstractStructuredToken, species::Symbol, t, transition::Transition)
return push!(a.past_bonds, (species, Float64(t), transition))
end

# Set the transition a token is bound to.
get_bound_transition(a::AbstractStructuredToken) = a.bound_transition
function set_bound_transition!(a::AbstractStructuredToken, t::Union{Nothing,Transition})
return a.bound_transition = t
end

# Priority with which an unbound agent will be assigned to a transition.
priority(a, transition) = 0.0
priority(a::AbstractStructuredToken, transition) = 0.0

# What species (place) is an agent currently assigned to.
get_species(a::AbstractStructuredToken) = a.species
set_species!(a::AbstractStructuredToken, species::Symbol) = a.species = species
5 changes: 3 additions & 2 deletions src/interface/reaction_parser.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ function recursive_find_reactants!(
for i = 2:length(ex.args)
recursive_find_reactants!(ex.args[i], mult, mods, reactants)
end
elseif isexpr(ex, :call) ||
(ex.head == :macrocall && macroname(ex) [:structured, :move])
push!(reactants, FoldedReactant(ex, mult, mods))
elseif ex.head == :macrocall
mods = copy(mods)
macroname(ex) in species_modalities && push!(mods, macroname(ex))
Expand All @@ -71,8 +74,6 @@ 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
Loading

0 comments on commit 4791a81

Please sign in to comment.