|
| 1 | +### Rudimentary Interfacing Function ### |
| 2 | +# A single function, `get_lrs_vals`, which contain all interfacing functionality. However, |
| 3 | +# long-term it should be replaced with a sleeker interface. Ideally as MTK-wider support for |
| 4 | +# lattice problems and solutions are introduced. |
| 5 | + |
| 6 | +""" |
| 7 | + get_lrs_vals(sol, sp, lrs::LatticeReactionSystem; t = nothing) |
| 8 | +
|
| 9 | +A function for retrieving the solution of a `LatticeReactionSystem`-based simulation on various |
| 10 | +desired forms. Generally, for `LatticeReactionSystem`s, the values in `sol` is ordered in a |
| 11 | +way which is not directly interpretable by the user. Furthermore, the normal Catalyst interface |
| 12 | +for solutions (e.g. `sol[:X]`) does not work for these solutions. Hence this function is used instead. |
| 13 | +
|
| 14 | +The output is a vector, which in each position contain sp's value (either at a time step of time, |
| 15 | +depending on the input `t`). Its shape depends on the lattice (using a similar form as heterogeneous |
| 16 | +initial conditions). I.e. for a NxM cartesian grid, the values are NxM matrices. For a masked grid, |
| 17 | +the values are sparse matrices. For a graph lattice, the values are vectors (where the value in |
| 18 | +the n'th position corresponds to sp's value in the n'th vertex). |
| 19 | +
|
| 20 | +Arguments: |
| 21 | +- `sol`: The solution from which we wish to retrieve some values. |
| 22 | +- `sp`: The species which values we wish to retrieve. Can be either a symbol (e.g. `:X`) or a symbolic |
| 23 | +variable (e.g. `X`). |
| 24 | +- `lrs`: The `LatticeReactionSystem` which was simulated to generate the solution. |
| 25 | +- `t = nothing`: If `nothing`, we simply returns the solution across all saved timesteps. If `t` |
| 26 | +instead is a vector (or range of values), returns the solutions interpolated at these timepoints. |
| 27 | +
|
| 28 | +Notes: |
| 29 | +- The `get_lrs_vals` is not optimised for performance. However, it should still be quite performant, |
| 30 | +but there might be some limitations if called a very large number of times. |
| 31 | +- Long-term it is likely that this function gets replaced with a sleeker interface. |
| 32 | +
|
| 33 | +Example: |
| 34 | +```julia |
| 35 | +using Catalyst, OrdinaryDiffEq |
| 36 | +
|
| 37 | +# Prepare `LatticeReactionSystem`s. |
| 38 | +rs = @reaction_network begin |
| 39 | + (k1,k2), X1 <--> X2 |
| 40 | +end |
| 41 | +tr = @transport_reaction D X1 |
| 42 | +lrs = LatticeReactionSystem(rs, [tr], CartesianGrid((2,2))) |
| 43 | +
|
| 44 | +# Create problems. |
| 45 | +u0 = [:X1 => 1, :X2 => 2] |
| 46 | +tspan = (0.0, 10.0) |
| 47 | +ps = [:k1 => 1, :k2 => 2.0, :D => 0.1] |
| 48 | +
|
| 49 | +oprob = ODEProblem(lrs1, u0, tspan, ps) |
| 50 | +osol = solve(oprob1, Tsit5()) |
| 51 | +get_lrs_vals(osol, :X1, lrs) # Returns the value of X1 at each timestep. |
| 52 | +get_lrs_vals(osol, :X1, lrs; t = 0.0:10.0) # Returns the value of X1 at times 0.0, 1.0, ..., 10.0 |
| 53 | +``` |
| 54 | +""" |
| 55 | +function get_lrs_vals(sol, sp, lrs::LatticeReactionSystem; t = nothing) |
| 56 | + # Figures out which species we wish to fetch information about. |
| 57 | + (sp isa Symbol) && (sp = Catalyst._symbol_to_var(lrs, sp)) |
| 58 | + sp_idx = findfirst(isequal(sp), species(lrs)) |
| 59 | + sp_tot = length(species(lrs)) |
| 60 | + |
| 61 | + # Extracts the lattice and calls the next function. Masked grids (Array of Bools) are converted |
| 62 | + # to sparse array using the same template size as we wish to shape the data to. |
| 63 | + lattice = Catalyst.lattice(lrs) |
| 64 | + if has_masked_lattice(lrs) |
| 65 | + if grid_dims(lrs) == 3 |
| 66 | + error("The `get_lrs_vals` function is not defined for systems based on 3d sparse arrays. Please raise an issue at the Catalyst GitHub site if this is something which would be useful to you.") |
| 67 | + end |
| 68 | + lattice = sparse(lattice) |
| 69 | + end |
| 70 | + get_lrs_vals(sol, lattice, t, sp_idx, sp_tot) |
| 71 | +end |
| 72 | + |
| 73 | +# Function which handles the input in the case where `t` is `nothing` (i.e. return `sp`s value |
| 74 | +# across all sample points). |
| 75 | +function get_lrs_vals(sol, lattice, t::Nothing, sp_idx, sp_tot) |
| 76 | + # ODE simulations contain, in each data point, all values in a single vector. Jump simulations |
| 77 | + # instead in a matrix (NxM, where N is the number of species and M the number of vertices). We |
| 78 | + # must consider each case separately. |
| 79 | + if sol.prob isa ODEProblem |
| 80 | + return [reshape_vals(vals[sp_idx:sp_tot:end], lattice) for vals in sol.u] |
| 81 | + elseif sol.prob isa DiscreteProblem |
| 82 | + return [reshape_vals(vals[sp_idx,:], lattice) for vals in sol.u] |
| 83 | + else |
| 84 | + error("Unknown type of solution provided to `get_lrs_vals`. Only ODE or Jump solutions are supported.") |
| 85 | + end |
| 86 | +end |
| 87 | + |
| 88 | +# Function which handles the input in the case where `t` is a range of values (i.e. return `sp`s |
| 89 | +# value at all designated time points. |
| 90 | +function get_lrs_vals(sol, lattice, t::AbstractVector{T}, sp_idx, sp_tot) where {T <: Number} |
| 91 | + if (minimum(t) < sol.t[1]) || (maximum(t) > sol.t[end]) |
| 92 | + error("The range of the t values provided for sampling, ($(minimum(t)),$(maximum(t))) is not fully within the range of the simulation time span ($(sol.t[1]),$(sol.t[end])).") |
| 93 | + end |
| 94 | + |
| 95 | + # ODE simulations contain, in each data point, all values in a single vector. Jump simulations |
| 96 | + # instead in a matrix (NxM, where N is the number of species and M the number of vertices). We |
| 97 | + # must consider each case separately. |
| 98 | + if sol.prob isa ODEProblem |
| 99 | + return [reshape_vals(sol(ti)[sp_idx:sp_tot:end], lattice) for ti in t] |
| 100 | + elseif sol.prob isa DiscreteProblem |
| 101 | + return [reshape_vals(sol(ti)[sp_idx,:], lattice) for ti in t] |
| 102 | + else |
| 103 | + error("Unknown type of solution provided to `get_lrs_vals`. Only ODE or Jump solutions are supported.") |
| 104 | + end |
| 105 | +end |
| 106 | + |
| 107 | +# Functions which in each sample point reshapes the vector of values to the correct form (depending |
| 108 | +# on the type of lattice used). |
| 109 | +function reshape_vals(vals, lattice::CartesianGridRej{N, T}) where {N,T} |
| 110 | + return reshape(vals, lattice.dims...) |
| 111 | +end |
| 112 | +function reshape_vals(vals, lattice::AbstractSparseArray{Bool, Int64, 1}) |
| 113 | + return SparseVector(lattice.n, lattice.nzind, vals) |
| 114 | +end |
| 115 | +function reshape_vals(vals, lattice::AbstractSparseArray{Bool, Int64, 2}) |
| 116 | + return SparseMatrixCSC(lattice.m, lattice.n, lattice.colptr, lattice.rowval, vals) |
| 117 | +end |
| 118 | +function reshape_vals(vals, lattice::DiGraph) |
| 119 | + return vals |
| 120 | +end |
| 121 | + |
0 commit comments