Skip to content

Commit d2989ad

Browse files
authored
Merge branch 'master' into fix_doc_project_versions
2 parents b7047cf + 46ea95d commit d2989ad

File tree

6 files changed

+461
-10
lines changed

6 files changed

+461
-10
lines changed

docs/pages.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pages = Any[
1818
#"model_creation/network_analysis.md",
1919
"model_creation/chemistry_related_functionality.md",
2020
"Model creation examples" => Any[
21-
"model_creation/examples/basic_CRN_library.md",
21+
#"model_creation/examples/basic_CRN_library.md",
2222
"model_creation/examples/programmatic_generative_linear_pathway.md",
2323
#"model_creation/examples/hodgkin_huxley_equation.md",
2424
#"model_creation/examples/smoluchowski_coagulation_equation.md"
@@ -28,7 +28,7 @@ pages = Any[
2828
"model_simulation/simulation_introduction.md",
2929
"model_simulation/simulation_plotting.md",
3030
"model_simulation/simulation_structure_interfacing.md",
31-
"model_simulation/ensemble_simulations.md",
31+
#"model_simulation/ensemble_simulations.md",
3232
# Stochastic simulation statistical analysis.
3333
"model_simulation/ode_simulation_performance.md",
3434
# SDE Performance considerations/advice.
@@ -37,7 +37,7 @@ pages = Any[
3737
],
3838
"Steady state analysis" => Any[
3939
"steady_state_functionality/homotopy_continuation.md",
40-
"steady_state_functionality/nonlinear_solve.md",
40+
#"steady_state_functionality/nonlinear_solve.md",
4141
"steady_state_functionality/steady_state_stability_computation.md",
4242
"steady_state_functionality/bifurcation_diagrams.md",
4343
"steady_state_functionality/dynamical_systems.md"

src/Catalyst.jl

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ module Catalyst
66
using DocStringExtensions
77
using SparseArrays, DiffEqBase, Reexport, Setfield
88
using LaTeXStrings, Latexify, Requires
9-
using JumpProcesses: JumpProcesses,
10-
JumpProblem, MassActionJump, ConstantRateJump,
11-
VariableRateJump
9+
using JumpProcesses: JumpProcesses, JumpProblem,
10+
MassActionJump, ConstantRateJump, VariableRateJump,
11+
SpatialMassActionJump
1212

1313
# ModelingToolkit imports and convenience functions we use
1414
using ModelingToolkit
@@ -165,20 +165,21 @@ export make_si_ode
165165

166166
### Spatial Reaction Networks ###
167167

168-
# spatial reactions
168+
# Spatial reactions.
169169
include("spatial_reaction_systems/spatial_reactions.jl")
170170
export TransportReaction, TransportReactions, @transport_reaction
171171
export isedgeparameter
172172

173-
# lattice reaction systems
173+
# Lattice reaction systems
174174
include("spatial_reaction_systems/lattice_reaction_systems.jl")
175175
export LatticeReactionSystem
176176
export spatial_species, vertex_parameters, edge_parameters
177177

178-
# variosu utility functions
178+
# Various utility functions
179179
include("spatial_reaction_systems/utility.jl")
180180

181-
# spatial lattice ode systems.
181+
# Specific spatial problem types.
182182
include("spatial_reaction_systems/spatial_ODE_systems.jl")
183+
include("spatial_reaction_systems/lattice_jump_systems.jl")
183184

184185
end # module
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
### JumpProblem ###
2+
3+
# Builds a spatial DiscreteProblem from a Lattice Reaction System.
4+
function DiffEqBase.DiscreteProblem(lrs::LatticeReactionSystem, u0_in, tspan, p_in = DiffEqBase.NullParameters(), args...; kwargs...)
5+
if !is_transport_system(lrs)
6+
error("Currently lattice Jump simulations only supported when all spatial reactions are transport reactions.")
7+
end
8+
9+
# Converts potential symmaps to varmaps
10+
# Vertex and edge parameters may be given in a tuple, or in a common vector, making parameter case complicated.
11+
u0_in = symmap_to_varmap(lrs, u0_in)
12+
p_in = (p_in isa Tuple{<:Any,<:Any}) ?
13+
(symmap_to_varmap(lrs, p_in[1]),symmap_to_varmap(lrs, p_in[2])) :
14+
symmap_to_varmap(lrs, p_in)
15+
16+
# Converts u0 and p to their internal forms.
17+
# u0 is [spec 1 at vert 1, spec 2 at vert 1, ..., spec 1 at vert 2, ...].
18+
u0 = lattice_process_u0(u0_in, species(lrs), lrs.num_verts)
19+
# Both vert_ps and edge_ps becomes vectors of vectors. Each have 1 element for each parameter.
20+
# These elements are length 1 vectors (if the parameter is uniform),
21+
# or length num_verts/nE, with unique values for each vertex/edge (for vert_ps/edge_ps, respectively).
22+
vert_ps, edge_ps = lattice_process_p(p_in, vertex_parameters(lrs), edge_parameters(lrs), lrs)
23+
24+
# Returns a DiscreteProblem.
25+
# Previously, a Tuple was used for (vert_ps, edge_ps), but this was converted to a Vector internally.
26+
return DiscreteProblem(u0, tspan, [vert_ps, edge_ps], args...; kwargs...)
27+
end
28+
29+
# Builds a spatial JumpProblem from a DiscreteProblem containg a Lattice Reaction System.
30+
function JumpProcesses.JumpProblem(lrs::LatticeReactionSystem, dprob, aggregator, args...; name = nameof(lrs.rs),
31+
combinatoric_ratelaws = get_combinatoric_ratelaws(lrs.rs), kwargs...)
32+
# Error checks.
33+
if !isnothing(dprob.f.sys)
34+
error("Unexpected `DiscreteProblem` passed into `JumpProblem`. Was a `LatticeReactionSystem` used as input to the initial `DiscreteProblem`?")
35+
end
36+
37+
# Computes hopping constants and mass action jumps (requires some internal juggling).
38+
# Currently, JumpProcesses requires uniform vertex parameters (hence `p=first.(dprob.p[1])`).
39+
# Currently, the resulting JumpProblem does not depend on parameters (no way to incorporate these).
40+
# Hence the parameters of this one does nto actually matter. If at some point JumpProcess can
41+
# handle parameters this can be updated and improved.
42+
# The non-spatial DiscreteProblem have a u0 matrix with entries for all combinations of species and vertexes.
43+
hopping_constants = make_hopping_constants(dprob, lrs)
44+
sma_jumps = make_spatial_majumps(dprob, lrs)
45+
non_spat_dprob = DiscreteProblem(reshape(dprob.u0, lrs.num_species, lrs.num_verts), dprob.tspan, first.(dprob.p[1]))
46+
47+
return JumpProblem(non_spat_dprob, aggregator, sma_jumps;
48+
hopping_constants, spatial_system = lrs.lattice, name, kwargs...)
49+
end
50+
51+
# Creates the hopping constants from a discrete problem and a lattice reaction system.
52+
function make_hopping_constants(dprob::DiscreteProblem, lrs::LatticeReactionSystem)
53+
# Creates the all_diff_rates vector, containing for each species, its transport rate across all edges.
54+
# If transport rate is uniform for one species, the vector have a single element, else one for each edge.
55+
spatial_rates_dict = Dict(compute_all_transport_rates(dprob.p[1], dprob.p[2], lrs))
56+
all_diff_rates = [haskey(spatial_rates_dict, s) ? spatial_rates_dict[s] : [0.0] for s in species(lrs)]
57+
58+
# Creates the hopping constant Matrix. It contains one element for each combination of species and vertex.
59+
# Each element is a Vector, containing the outgoing hopping rates for that species, from that vertex, on that edge.
60+
hopping_constants = [Vector{Float64}(undef, length(lrs.lattice.fadjlist[j]))
61+
for i in 1:(lrs.num_species), j in 1:(lrs.num_verts)]
62+
63+
# For each edge, finds each position in `hopping_constants`.
64+
for (e_idx, e) in enumerate(edges(lrs.lattice))
65+
dst_idx = findfirst(isequal(e.dst), lrs.lattice.fadjlist[e.src])
66+
# For each species, sets that hopping rate.
67+
for s_idx in 1:(lrs.num_species)
68+
hopping_constants[s_idx, e.src][dst_idx] = get_component_value(all_diff_rates[s_idx], e_idx)
69+
end
70+
end
71+
72+
return hopping_constants
73+
end
74+
75+
# Creates a SpatialMassActionJump struct from a (spatial) DiscreteProblem and a LatticeReactionSystem.
76+
# Could implementation a version which, if all reaction's rates are uniform, returns a MassActionJump.
77+
# Not sure if there is any form of performance improvement from that though. Possibly is not the case.
78+
function make_spatial_majumps(dprob, lrs::LatticeReactionSystem)
79+
# Creates a vector, storing which reactions have spatial components.
80+
is_spatials = [Catalyst.has_spatial_vertex_component(rx.rate, lrs; vert_ps = dprob.p[1]) for rx in reactions(lrs.rs)]
81+
82+
# Creates templates for the rates (uniform and spatial) and the stoichiometries.
83+
# We cannot fetch reactant_stoich and net_stoich from a (non-spatial) MassActionJump.
84+
# The reason is that we need to re-order the reactions so that uniform appears first, and spatial next.
85+
u_rates = Vector{Float64}(undef, length(reactions(lrs.rs)) - count(is_spatials))
86+
s_rates = Matrix{Float64}(undef, count(is_spatials), lrs.num_verts)
87+
reactant_stoich = Vector{Vector{Pair{Int64, Int64}}}(undef, length(reactions(lrs.rs)))
88+
net_stoich = Vector{Vector{Pair{Int64, Int64}}}(undef, length(reactions(lrs.rs)))
89+
90+
# Loops through reactions with non-spatial rates, computes their rates and stoichiometries.
91+
cur_rx = 1;
92+
for (is_spat, rx) in zip(is_spatials, reactions(lrs.rs))
93+
is_spat && continue
94+
u_rates[cur_rx] = compute_vertex_value(rx.rate, lrs; vert_ps = dprob.p[1])[1]
95+
substoich_map = Pair.(rx.substrates, rx.substoich)
96+
reactant_stoich[cur_rx] = int_map(substoich_map, lrs.rs)
97+
net_stoich[cur_rx] = int_map(rx.netstoich, lrs.rs)
98+
cur_rx += 1
99+
end
100+
# Loops through reactions with spatial rates, computes their rates and stoichiometries.
101+
for (is_spat, rx) in zip(is_spatials, reactions(lrs.rs))
102+
is_spat || continue
103+
s_rates[cur_rx-length(u_rates),:] = compute_vertex_value(rx.rate, lrs; vert_ps = dprob.p[1])
104+
substoich_map = Pair.(rx.substrates, rx.substoich)
105+
reactant_stoich[cur_rx] = int_map(substoich_map, lrs.rs)
106+
net_stoich[cur_rx] = int_map(rx.netstoich, lrs.rs)
107+
cur_rx += 1
108+
end
109+
# SpatialMassActionJump expects empty rate containers to be nothing.
110+
isempty(u_rates) && (u_rates = nothing)
111+
(count(is_spatials)==0) && (s_rates = nothing)
112+
113+
return SpatialMassActionJump(u_rates, s_rates, reactant_stoich, net_stoich)
114+
end
115+
116+
### Extra ###
117+
118+
# Temporary. Awaiting implementation in SII, or proper implementation withinCatalyst (with more general functionality).
119+
function int_map(map_in, sys) where {T,S}
120+
return [ModelingToolkit.variable_index(sys, pair[1]) => pair[2] for pair in map_in]
121+
end
122+
123+
# Currently unused. If we want to create certain types of MassActionJumps (instead of SpatialMassActionJumps) we can take this one back.
124+
# Creates the (non-spatial) mass action jumps from a (non-spatial) DiscreteProblem (and its Reaction System of origin).
125+
# function make_majumps(non_spat_dprob, rs::ReactionSystem)
126+
# # Computes various required inputs for assembling the mass action jumps.
127+
# js = convert(JumpSystem, rs)
128+
# statetoid = Dict(ModelingToolkit.value(state) => i for (i, state) in enumerate(states(rs)))
129+
# eqs = equations(js)
130+
# invttype = non_spat_dprob.tspan[1] === nothing ? Float64 : typeof(1 / non_spat_dprob.tspan[2])
131+
#
132+
# # Assembles the non-spatial mass action jumps.
133+
# p = (non_spat_dprob.p isa DiffEqBase.NullParameters || non_spat_dprob.p === nothing) ? Num[] : non_spat_dprob.p
134+
# majpmapper = ModelingToolkit.JumpSysMajParamMapper(js, p; jseqs = eqs, rateconsttype = invttype)
135+
# return ModelingToolkit.assemble_maj(eqs.x[1], statetoid, majpmapper)
136+
# end

src/spatial_reaction_systems/utility.jl

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,99 @@ end
295295
function matrix_expand_component_values(values::Vector{<:Vector}, n)
296296
reshape(expand_component_values(values, n), length(values), n)
297297
end
298+
299+
# For an expression, computes its values using the provided state and parameter vectors.
300+
# The expression is assumed to be valid in edges (and can have edges parameter components).
301+
# If some component is non-uniform, output is a vector of length equal to the number of vertexes.
302+
# If all components are uniform, the output is a length one vector.
303+
function compute_edge_value(exp, lrs::LatticeReactionSystem, edge_ps)
304+
# Finds the symbols in the expression. Checks that all correspond to edge parameters.
305+
relevant_syms = Symbolics.get_variables(exp)
306+
if !all(any(isequal(sym, p) for p in edge_parameters(lrs)) for sym in relevant_syms)
307+
error("An non-edge parameter was encountered in expressions: $exp. Here, only edge parameters are expected.")
308+
end
309+
310+
# Creates a Function tha computes the expressions value for a parameter set.
311+
exp_func = drop_expr(@RuntimeGeneratedFunction(build_function(exp, relevant_syms...)))
312+
# Creates a dictionary with the value(s) for all edge parameters.
313+
sym_val_dict = vals_to_dict(edge_parameters(lrs), edge_ps)
314+
315+
# If all values are uniform, compute value once. Else, do it at all edges.
316+
if !has_spatial_edge_component(exp, lrs, edge_ps)
317+
return [exp_func([sym_val_dict[sym][1] for sym in relevant_syms]...)]
318+
end
319+
return [exp_func([get_component_value(sym_val_dict[sym], idxE) for sym in relevant_syms]...)
320+
for idxE in 1:lrs.num_edges]
321+
end
322+
323+
# For an expression, computes its values using the provided state and parameter vectors.
324+
# The expression is assumed to be valid in vertexes (and can have vertex parameter and state components).
325+
# If at least one component is non-uniform, output is a vector of length equal to the number of vertexes.
326+
# If all components are uniform, the output is a length one vector.
327+
function compute_vertex_value(exp, lrs::LatticeReactionSystem; u=nothing, vert_ps=nothing)
328+
# Finds the symbols in the expression. Checks that all correspond to states or vertex parameters.
329+
relevant_syms = Symbolics.get_variables(exp)
330+
if any(any(isequal(sym) in edge_parameters(lrs)) for sym in relevant_syms)
331+
error("An edge parameter was encountered in expressions: $exp. Here, on vertex-based components are expected.")
332+
end
333+
# Creates a Function tha computes the expressions value for a parameter set.
334+
exp_func = drop_expr(@RuntimeGeneratedFunction(build_function(exp, relevant_syms...)))
335+
# Creates a dictionary with the value(s) for all edge parameters.
336+
if !isnothing(u) && !isnothing(vert_ps)
337+
all_syms = [species(lrs); vertex_parameters(lrs)]
338+
all_vals = [u; vert_ps]
339+
elseif !isnothing(u) && isnothing(vert_ps)
340+
all_syms = species(lrs)
341+
all_vals = u
342+
343+
elseif isnothing(u) && !isnothing(vert_ps)
344+
all_syms = vertex_parameters(lrs)
345+
all_vals = vert_ps
346+
else
347+
error("Either u or vertex_ps have to be provided to has_spatial_vertex_component.")
348+
end
349+
sym_val_dict = vals_to_dict(all_syms, all_vals)
350+
351+
# If all values are uniform, compute value once. Else, do it at all edges.
352+
if !has_spatial_vertex_component(exp, lrs; u, vert_ps)
353+
return [exp_func([sym_val_dict[sym][1] for sym in relevant_syms]...)]
354+
end
355+
return [exp_func([get_component_value(sym_val_dict[sym], idxV) for sym in relevant_syms]...)
356+
for idxV in 1:lrs.num_verts]
357+
end
358+
359+
### System Property Checks ###
360+
361+
# For a Symbolic expression, a LatticeReactionSystem, and a parameter list of the internal format:
362+
# Checks if any edge parameter in the expression have a spatial component (that is, is not uniform).
363+
function has_spatial_edge_component(exp, lrs::LatticeReactionSystem, edge_ps)
364+
# Finds the edge parameters in the expression. Computes their indexes.
365+
exp_syms = Symbolics.get_variables(exp)
366+
exp_edge_ps = filter(sym -> any(isequal(sym), edge_parameters(lrs)), exp_syms)
367+
p_idxs = [findfirst(isequal(sym, edge_p) for edge_p in edge_parameters(lrs)) for sym in exp_syms]
368+
# Checks if any of the corresponding value vectors have length != 1 (that is, is not uniform).
369+
return any(length(edge_ps[p_idx]) != 1 for p_idx in p_idxs)
370+
end
371+
372+
# For a Symbolic expression, a LatticeReactionSystem, and a parameter list of the internal format (vector of vectors):
373+
# Checks if any vertex parameter in the expression have a spatial component (that is, is not uniform).
374+
function has_spatial_vertex_component(exp, lrs::LatticeReactionSystem; u=nothing, vert_ps=nothing)
375+
# Finds all the symbols in the expression.
376+
exp_syms = Symbolics.get_variables(exp)
377+
378+
# If vertex parameter values where given, checks if any of these have non-uniform values.
379+
if !isnothing(vert_ps)
380+
exp_vert_ps = filter(sym -> any(isequal(sym), vertex_parameters(lrs)), exp_syms)
381+
p_idxs = [ModelingToolkit.parameter_index(lrs.rs, sym) for sym in exp_vert_ps]
382+
any(length(vert_ps[p_idx]) != 1 for p_idx in p_idxs) && return true
383+
end
384+
385+
# If states values where given, checks if any of these have non-uniform values.
386+
if !isnothing(u)
387+
exp_u = filter(sym -> any(isequal(sym), species(lrs)), exp_syms)
388+
u_idxs = [ModelingToolkit.variable_index(lrs.rs, sym) for sym in exp_u]
389+
any(length(u[u_idx]) != 1 for u_idx in u_idxs) && return true
390+
end
391+
392+
return false
393+
end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ using SafeTestsets, Test
6161
@time @safetestset "PDE Systems Simulations" begin include("spatial_modelling/simulate_PDEs.jl") end
6262
@time @safetestset "Lattice Reaction Systems" begin include("spatial_modelling/lattice_reaction_systems.jl") end
6363
@time @safetestset "ODE Lattice Systems Simulations" begin include("spatial_modelling/lattice_reaction_systems_ODEs.jl") end
64+
@time @safetestset "Jump Lattice Systems Simulations" begin include("spatial_reaction_systems/lattice_reaction_systems_jumps.jl") end
6465
#end
6566

6667
#if GROUP == "All" || GROUP == "Visualisation-Extensions"

0 commit comments

Comments
 (0)