Skip to content

Commit f0f4a09

Browse files
committed
update JumpProblem for latest version
1 parent 2e4956a commit f0f4a09

13 files changed

+312
-698
lines changed

src/Catalyst.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ export make_edge_p_values, make_directed_edge_values
183183
include("spatial_reaction_systems/spatial_ODE_systems.jl")
184184
include("spatial_reaction_systems/lattice_jump_systems.jl")
185185

186+
# General spatial modelling utility functions.
187+
include("spatial_reaction_systems/utility.jl")
188+
186189
### ReactionSystem Serialisation ###
187190
# Has to be at the end (because it uses records of all metadata declared by Catalyst).
188191
include("reactionsystem_serialisation/serialisation_support.jl")

src/spatial_reaction_systems/lattice_jump_systems.jl

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,25 @@ function DiffEqBase.DiscreteProblem(lrs::LatticeReactionSystem, u0_in, tspan,
77
error("Currently lattice Jump simulations only supported when all spatial reactions are transport reactions.")
88
end
99

10-
# Converts potential symmaps to varmaps
11-
# Vertex and edge parameters may be given in a tuple, or in a common vector, making parameter case complicated.
10+
# Converts potential symmaps to varmaps.
1211
u0_in = symmap_to_varmap(lrs, u0_in)
13-
p_in = (p_in isa Tuple{<:Any, <:Any}) ?
14-
(symmap_to_varmap(lrs, p_in[1]), symmap_to_varmap(lrs, p_in[2])) :
15-
symmap_to_varmap(lrs, p_in)
12+
p_in = symmap_to_varmap(lrs, p_in)
1613

1714
# Converts u0 and p to their internal forms.
15+
# u0 is simply a vector with all the species' initial condition values across all vertices.
1816
# u0 is [spec 1 at vert 1, spec 2 at vert 1, ..., spec 1 at vert 2, ...].
19-
u0 = lattice_process_u0(u0_in, species(lrs), num_verts(lrs))
20-
# Both vert_ps and edge_ps becomes vectors of vectors. Each have 1 element for each parameter.
21-
# These elements are length 1 vectors (if the parameter is uniform),
22-
# or length num_verts/nE, with unique values for each vertex/edge (for vert_ps/edge_ps, respectively).
23-
vert_ps, edge_ps = lattice_process_p(p_in, vertex_parameters(lrs),
24-
edge_parameters(lrs), lrs)
25-
26-
# Returns a DiscreteProblem.
27-
# Previously, a Tuple was used for (vert_ps, edge_ps), but this was converted to a Vector internally.
28-
return DiscreteProblem(u0, tspan, [vert_ps, edge_ps], args...; kwargs...)
17+
u0 = lattice_process_u0(u0_in, species(lrs), lrs)
18+
# vert_ps and `edge_ps` are vector maps, taking each parameter's Symbolics representation to its value(s).
19+
# vert_ps values are vectors. Here, index (i) is a parameter's value in vertex i.
20+
# edge_ps values are sparse matrices. Here, index (i,j) is a parameter's value in the edge from vertex i to vertex j.
21+
# Uniform vertex/edge parameters store only a single value (a length 1 vector, or size 1x1 sparse matrix).
22+
vert_ps, edge_ps = lattice_process_p(p_in, vertex_parameters(lrs), edge_parameters(lrs), lrs)
23+
24+
# Returns a DiscreteProblem (which basically just stores the processed input).
25+
return DiscreteProblem(u0, tspan, [vert_ps; edge_ps], args...; kwargs...)
2926
end
3027

31-
# Builds a spatial JumpProblem from a DiscreteProblem containg a Lattice Reaction System.
28+
# Builds a spatial JumpProblem from a DiscreteProblem containing a `LatticeReactionSystem`.
3229
function JumpProcesses.JumpProblem(lrs::LatticeReactionSystem, dprob, aggregator, args...; name = nameof(reactionsystem(lrs)),
3330
combinatoric_ratelaws = get_combinatoric_ratelaws(reactionsystem(lrs)), kwargs...)
3431
# Error checks.
@@ -37,50 +34,53 @@ function JumpProcesses.JumpProblem(lrs::LatticeReactionSystem, dprob, aggregator
3734
end
3835

3936
# Computes hopping constants and mass action jumps (requires some internal juggling).
40-
# Currently, JumpProcesses requires uniform vertex parameters (hence `p=first.(dprob.p[1])`).
4137
# Currently, the resulting JumpProblem does not depend on parameters (no way to incorporate these).
42-
# Hence the parameters of this one does nto actually matter. If at some point JumpProcess can
38+
# Hence the parameters of this one does not actually matter. If at some point JumpProcess can
4339
# handle parameters this can be updated and improved.
4440
# The non-spatial DiscreteProblem have a u0 matrix with entries for all combinations of species and vertexes.
4541
hopping_constants = make_hopping_constants(dprob, lrs)
4642
sma_jumps = make_spatial_majumps(dprob, lrs)
4743
non_spat_dprob = DiscreteProblem(reshape(dprob.u0, num_species(lrs), num_verts(lrs)), dprob.tspan, first.(dprob.p[1]))
4844

45+
# Creates and returns a spatial JumpProblem (masked lattices are not supported by these).
46+
spatial_system = has_masked_lattice(lrs) ? get_lattice_graph(lrs) : lattice(lrs)
4947
return JumpProblem(non_spat_dprob, aggregator, sma_jumps;
50-
hopping_constants, spatial_system = lattice(lrs), name, kwargs...)
48+
hopping_constants, spatial_system , name, kwargs...)
5149
end
5250

5351
# Creates the hopping constants from a discrete problem and a lattice reaction system.
5452
function make_hopping_constants(dprob::DiscreteProblem, lrs::LatticeReactionSystem)
5553
# Creates the all_diff_rates vector, containing for each species, its transport rate across all edges.
5654
# If transport rate is uniform for one species, the vector have a single element, else one for each edge.
57-
spatial_rates_dict = Dict(compute_all_transport_rates(dprob.p[1], dprob.p[2], lrs))
55+
spatial_rates_dict = Dict(compute_all_transport_rates(Dict(dprob.p), lrs))
5856
all_diff_rates = [haskey(spatial_rates_dict, s) ? spatial_rates_dict[s] : [0.0]
5957
for s in species(lrs)]
6058

61-
# Creates the hopping constant Matrix. It contains one element for each combination of species and vertex.
62-
# Each element is a Vector, containing the outgoing hopping rates for that species, from that vertex, on that edge.
63-
hopping_constants = [Vector{Float64}(undef, length(lattice(lrs).fadjlist[j]))
64-
for i in 1:(num_species(lrs)), j in 1:(num_verts(lrs))]
65-
66-
# For each edge, finds each position in `hopping_constants`.
67-
for (e_idx, e) in enumerate(edges(lattice(lrs)))
68-
dst_idx = findfirst(isequal(e.dst), lattice(lrs).fadjlist[e.src])
69-
# For each species, sets that hopping rate.
70-
for s_idx in 1:(num_species(lrs))
71-
hopping_constants[s_idx, e.src][dst_idx] = get_component_value(all_diff_rates[s_idx], e_idx)
72-
end
59+
# Creates an array (of the same size as the hopping constant array) containing all edges.
60+
# First the array is a NxM matrix (number of species x number of vertices). Each element is a
61+
# vector containing all edges leading out from that vertex (sorted by destination index).
62+
edge_array = [Pair{Int64,Int64}[] for _1 in 1:num_species(lrs), _2 in 1:num_verts(lrs)]
63+
for e in edge_iterator(lrs), s_idx in 1:num_species(lrs)
64+
push!(edge_array[s_idx, e[1]], e)
7365
end
74-
66+
foreach(e_vec -> sort!(e_vec; by = e -> e[2]), edge_array)
67+
68+
# Creates the hopping constants array. It has the same shape as the edge array, but each
69+
# element is that species transportation rate along that edge
70+
hopping_constants = [
71+
[Catalyst.get_edge_value(all_diff_rates[s_idx], e) for e in edge_array[s_idx, src_idx]]
72+
for s_idx in 1:num_species(lrs), src_idx in 1:num_verts(lrs)
73+
]
7574
return hopping_constants
7675
end
7776

7877
# Creates a SpatialMassActionJump struct from a (spatial) DiscreteProblem and a LatticeReactionSystem.
7978
# Could implementation a version which, if all reaction's rates are uniform, returns a MassActionJump.
80-
# Not sure if there is any form of performance improvement from that though. Possibly is not the case.
79+
# Not sure if there is any form of performance improvement from that though. Likely not the case.
8180
function make_spatial_majumps(dprob, lrs::LatticeReactionSystem)
8281
# Creates a vector, storing which reactions have spatial components.
83-
is_spatials = [Catalyst.has_spatial_vertex_component(rx.rate, lrs; vert_ps = dprob.p[1]) for rx in reactions(reactionsystem(lrs))]
82+
is_spatials = [has_spatial_vertex_component(rx.rate, dprob.p)
83+
for rx in reactions(reactionsystem(lrs))]
8484

8585
# Creates templates for the rates (uniform and spatial) and the stoichiometries.
8686
# We cannot fetch reactant_stoich and net_stoich from a (non-spatial) MassActionJump.
@@ -91,10 +91,10 @@ function make_spatial_majumps(dprob, lrs::LatticeReactionSystem)
9191
net_stoich = Vector{Vector{Pair{Int64, Int64}}}(undef, length(reactions(reactionsystem(lrs))))
9292

9393
# Loops through reactions with non-spatial rates, computes their rates and stoichiometries.
94-
cur_rx = 1;
94+
cur_rx = 1
9595
for (is_spat, rx) in zip(is_spatials, reactions(reactionsystem(lrs)))
9696
is_spat && continue
97-
u_rates[cur_rx] = compute_vertex_value(rx.rate, lrs; vert_ps = dprob.p[1])[1]
97+
u_rates[cur_rx] = compute_vertex_value(rx.rate, lrs; ps = dprob.p)[1]
9898
substoich_map = Pair.(rx.substrates, rx.substoich)
9999
reactant_stoich[cur_rx] = int_map(substoich_map, reactionsystem(lrs))
100100
net_stoich[cur_rx] = int_map(rx.netstoich, reactionsystem(lrs))
@@ -103,8 +103,7 @@ function make_spatial_majumps(dprob, lrs::LatticeReactionSystem)
103103
# Loops through reactions with spatial rates, computes their rates and stoichiometries.
104104
for (is_spat, rx) in zip(is_spatials, reactions(reactionsystem(lrs)))
105105
is_spat || continue
106-
s_rates[cur_rx - length(u_rates), :] = compute_vertex_value(rx.rate, lrs;
107-
vert_ps = dprob.p[1])
106+
s_rates[cur_rx - length(u_rates), :] .= compute_vertex_value(rx.rate, lrs; ps = dprob.p)
108107
substoich_map = Pair.(rx.substrates, rx.substoich)
109108
reactant_stoich[cur_rx] = int_map(substoich_map, reactionsystem(lrs))
110109
net_stoich[cur_rx] = int_map(rx.netstoich, reactionsystem(lrs))
@@ -114,7 +113,7 @@ function make_spatial_majumps(dprob, lrs::LatticeReactionSystem)
114113
isempty(u_rates) && (u_rates = nothing)
115114
(count(is_spatials) == 0) && (s_rates = nothing)
116115

117-
return SpatialMassActionJump(u_rates, s_rates, reactant_stoich, net_stoich)
116+
return SpatialMassActionJump(u_rates, s_rates, reactant_stoich, net_stoich, nothing)
118117
end
119118

120119
### Extra ###

src/spatial_reaction_systems/lattice_reaction_systems.jl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,16 @@ grid_dims(lrs::LatticeReactionSystem) = grid_dims(lattice(lrs))
307307
grid_dims(lattice::GridLattice{N,T}) where {N,T} = return N
308308
grid_dims(lattice::Graph) = error("Grid dimensions is only defined for LatticeReactionSystems with grid-based lattices (not graph-based).")
309309

310+
"""
311+
get_lattice_graph(lrs::LatticeReactionSystem)
312+
313+
Returns lrs's lattice, but in as a graph. Currently does not work for Cartesian lattices.
314+
"""
315+
function get_lattice_graph(lrs::LatticeReactionSystem)
316+
has_graph_lattice(lrs) && return lattice(lrs)
317+
return Graphs.SimpleGraphFromIterator(Graphs.SimpleEdge(e[1], e[2])
318+
for e in edge_iterator(lrs))
319+
end
310320

311321
### Catalyst-based Getters ###
312322

@@ -393,7 +403,7 @@ end
393403
D_vals = make_edge_p_values(lrs, make_edge_p_value)
394404
```
395405
"""
396-
function make_edge_p_values(lrs::LatticeReactionSystem, make_edge_p_value::Function, )
406+
function make_edge_p_values(lrs::LatticeReactionSystem, make_edge_p_value::Function)
397407
if has_graph_lattice(lrs)
398408
error("The `make_edge_p_values` function is only meant for lattices with (Cartesian or masked) grid structures. It cannot be applied to graph lattices.")
399409
end

src/spatial_reaction_systems/spatial_ODE_systems.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ function DiffEqBase.ODEProblem(lrs::LatticeReactionSystem, u0_in, tspan,
186186
u0 = lattice_process_u0(u0_in, species(lrs), lrs)
187187
# vert_ps and `edge_ps` are vector maps, taking each parameter's Symbolics representation to its value(s).
188188
# vert_ps values are vectors. Here, index (i) is a parameter's value in vertex i.
189-
# edge_ps becomes a sparse matrix. Here, index (i,j) is a parameter's value in the edge from vertex i to vertex j.
189+
# edge_ps values are sparse matrices. Here, index (i,j) is a parameter's value in the edge from vertex i to vertex j.
190190
# Uniform vertex/edge parameters store only a single value (a length 1 vector, or size 1x1 sparse matrix).
191191
# In the `ODEProblem` vert_ps and edge_ps are merged (but for building the ODEFunction, they are separate).
192192
vert_ps, edge_ps = lattice_process_p(p_in, vertex_parameters(lrs), edge_parameters(lrs), lrs)
@@ -321,6 +321,9 @@ end
321321

322322
# Defines the forcing functor's effect on the (spatial) ODE system.
323323
function (f_func::LatticeTransportODEf)(du, u, p, t)
324+
println(du)
325+
println(u)
326+
println(p)
324327
# Updates for non-spatial reactions.
325328
for vert_i in 1:(f_func.num_verts)
326329
# Gets the indices of all the species at vertex i.

src/spatial_reaction_systems/utility.jl

Lines changed: 16 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -327,36 +327,25 @@ end
327327
# The expression is assumed to be valid in vertexes (and can have vertex parameter and state components).
328328
# If at least one component is non-uniform, output is a vector of length equal to the number of vertexes.
329329
# If all components are uniform, the output is a length one vector.
330-
function compute_vertex_value(exp, lrs::LatticeReactionSystem; u=nothing, vert_ps=nothing)
330+
function compute_vertex_value(exp, lrs::LatticeReactionSystem; u = [], ps = [])
331331
# Finds the symbols in the expression. Checks that all correspond to unknowns or vertex parameters.
332332
relevant_syms = Symbolics.get_variables(exp)
333333
if any(any(isequal(sym) in edge_parameters(lrs)) for sym in relevant_syms)
334-
error("An edge parameter was encountered in expressions: $exp. Here, on vertex-based components are expected.")
334+
error("An edge parameter was encountered in expressions: $exp. Here, only vertex-based components are expected.")
335335
end
336-
# Creates a Function tha computes the expressions value for a parameter set.
336+
337+
# Creates a Function that computes the expressions value for a parameter set.
337338
exp_func = drop_expr(@RuntimeGeneratedFunction(build_function(exp, relevant_syms...)))
339+
338340
# Creates a dictionary with the value(s) for all edge parameters.
339-
if !isnothing(u) && !isnothing(vert_ps)
340-
all_syms = [species(lrs); vertex_parameters(lrs)]
341-
all_vals = [u; vert_ps]
342-
elseif !isnothing(u) && isnothing(vert_ps)
343-
all_syms = species(lrs)
344-
all_vals = u
345-
346-
elseif isnothing(u) && !isnothing(vert_ps)
347-
all_syms = vertex_parameters(lrs)
348-
all_vals = vert_ps
349-
else
350-
error("Either u or vertex_ps have to be provided to has_spatial_vertex_component.")
351-
end
352-
sym_val_dict = vals_to_dict(all_syms, all_vals)
341+
value_dict = Dict(vcat(u, ps))
353342

354343
# If all values are uniform, compute value once. Else, do it at all edges.
355-
if !has_spatial_vertex_component(exp, lrs; u, vert_ps)
356-
return [exp_func([sym_val_dict[sym][1] for sym in relevant_syms]...)]
344+
if all(length(value_dict[sym]) == 1 for sym in relevant_syms)
345+
return [exp_func([value_dict[sym][1] for sym in relevant_syms]...)]
357346
end
358-
return [exp_func([get_component_value(sym_val_dict[sym], idxV) for sym in relevant_syms]...)
359-
for idxV in 1:num_verts(lrs)]
347+
return [exp_func([get_vertex_value(value_dict[sym], vert_idx) for sym in relevant_syms]...)
348+
for vert_idx in 1:num_verts(lrs)]
360349
end
361350

362351
### System Property Checks ###
@@ -373,26 +362,10 @@ function has_spatial_edge_component(exp, lrs::LatticeReactionSystem, edge_ps)
373362
return any(length(edge_ps[p_idx]) != 1 for p_idx in p_idxs)
374363
end
375364

376-
# For a Symbolic expression, a LatticeReactionSystem, and a parameter list of the internal format (vector of vectors):
377-
# Checks if any vertex parameter in the expression have a spatial component (that is, is not uniform).
378-
function has_spatial_vertex_component(exp, lrs::LatticeReactionSystem;
379-
u = nothing, vert_ps = nothing)
380-
# Finds all the symbols in the expression.
381-
exp_syms = Symbolics.get_variables(exp)
382-
383-
# If vertex parameter values where given, checks if any of these have non-uniform values.
384-
if !isnothing(vert_ps)
385-
exp_vert_ps = filter(sym -> any(isequal(sym), vertex_parameters(lrs)), exp_syms)
386-
p_idxs = [ModelingToolkit.parameter_index(reactionsystem(lrs), sym) for sym in exp_vert_ps]
387-
any(length(vert_ps[p_idx]) != 1 for p_idx in p_idxs) && return true
388-
end
389-
390-
# If states values where given, checks if any of these have non-uniform values.
391-
if !isnothing(u)
392-
exp_u = filter(sym -> any(isequal(sym), species(lrs)), exp_syms)
393-
u_idxs = [ModelingToolkit.variable_index(reactionsystem(lrs), sym) for sym in exp_u]
394-
any(length(u[u_idx]) != 1 for u_idx in u_idxs) && return true
395-
end
396-
397-
return false
365+
# For a Symbolic expression, and a parameter set, checks if any relevant parameters have a
366+
# spatial component. Filters out any parameters that are edge parameters.
367+
function has_spatial_vertex_component(exp, ps)
368+
relevant_syms = Symbolics.get_variables(exp)
369+
value_dict = Dict(filter(p -> p[2] isa Vector, ps))
370+
return any(length(value_dict[sym]) > 1 for sym in relevant_syms)
398371
end

test/runtests.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ using SafeTestsets, Test
99

1010
### Run Tests ###
1111
@time begin
12+
@time @safetestset "Jump Lattice Systems Simulations" begin include("spatial_modelling/lattice_reaction_systems_jumps.jl") end
1213

1314
# Tests the `ReactionSystem` structure and its properties.
1415
@time @safetestset "Reaction Structure" begin include("reactionsystem_core/reaction.jl") end
@@ -53,8 +54,9 @@ using SafeTestsets, Test
5354
# Tests spatial modelling and simulations.
5455
@time @safetestset "PDE Systems Simulations" begin include("spatial_modelling/simulate_PDEs.jl") end
5556
@time @safetestset "Lattice Reaction Systems" begin include("spatial_modelling/lattice_reaction_systems.jl") end
57+
@time @safetestset "Spatial Lattice Variants" begin include("spatial_modelling/lattice_reaction_systems_lattice_types.jl") end
5658
@time @safetestset "ODE Lattice Systems Simulations" begin include("spatial_modelling/lattice_reaction_systems_ODEs.jl") end
57-
@time @safetestset "Jump Lattice Systems Simulations" begin include("spatial_reaction_systems/lattice_reaction_systems_jumps.jl") end
59+
@time @safetestset "Jump Lattice Systems Simulations" begin include("spatial_modelling/lattice_reaction_systems_jumps.jl") end
5860

5961
# Tests network visualisation.
6062
@time @safetestset "Latexify" begin include("visualisation/latexify.jl") end

0 commit comments

Comments
 (0)