Skip to content

Extemely slow creation of ODEProblem from ReactionSystem #1051

@johannesnauta

Description

@johannesnauta

Describe the bug 🐞

Me again. I am simulating Lotka-Volterra systems, and have recently jumped back on Catalyst.jl for the flexibility and comparison with other solvers (SDEs, SSAs, etc.) other than solving ODEs. However, I find that creating (not solving) the ODEProblem from a ReactionSystem that is sufficiently large takes an incredible amount of time. The time it takes reaches the point where trying to create a problem of medium sizes (~50 species) basically does not finish. Additionally, when using an ODESystem using ModelingToolkit.jl directly, and creating the ODEProblem does not seem to have this issue.

Minimal Reproducible Example 👇
To illustrate, consider the following functions, one using Catalyst.jl, and one using ModelingToolkit.jl. They both create a simple Lotka-Volterra system, that is

$$ \frac{dx_i}{dt} = r_i x_i ( 1 - \sum_j A_{ij} x_j ) $$

Using Catalyst.jl

(note: if there are other/better ways to generate the ReactionSystem, let me know!)

function create_rs(S::Int)
    t = default_t()
    @parameters r[1:S] A[1:S,1:S]
    @species (x(t))[1:S]

    #/ Allocate and fill
    growthrxs = Array{Reaction}(undef, S)
    interactionrxs = Array{Reaction}(undef, S, S)
    for i in 1:S
        #~ Growth
        growthrxs[i] = Reaction(r[i], [x[i]], [x[i]], [1], [2])
        #~ Diagonal terms
        interactionrxs[i,i] = Reaction(r[i]*A[i,i], [x[i]], [x[i]], [2], [1])
        for j in (i+1):S
            #~ Off-diagonal terms
            interactionrxs[i,j] = Reaction(r[i]*A[i,j], [x[i], x[j]], [x[j]], [1, 1], [1])
            interactionrxs[j,i] = Reaction(r[i]*A[j,i], [x[j], x[i]], [x[i]], [1, 1], [1])
        end
    end

    #/ Gather reactions
    rxs = [growthrxs..., interactionrxs...]
    @named glvrs = ReactionSystem(rxs, t, combinatoric_ratelaws=false)
    return Catalyst.complete(glvrs)
end

Using ModelingToolkit.jl

function create_ODESystem(S::Int)
	  #/ Define variables and parameters
    @variables (x(t))[1:S]
    @parameters r[1:S] A[1:S,1:S]

    eqns = [D(x[i]) ~ r[i]*x[i]*(1.0 - sum([A[i,j]*x[j] for j in 1:S])) for i in 1:S]
    @named odesys = ODESystem(eqns, t)
    return ModelingToolkit.complete(odesys)
end

Then, running the following:

julia> using BenchmarkTools

julia> S = 16;

julia> rs = create_rs(S);

julia> osys = create_ODESystem(S);

julia> rsprob = create_ODEProblem(rs);

julia> osprob = create_ODEProblem(osys);

julia> @btime rsprob = create_ODEProblem(rs);
  2.235 s (4388516 allocations: 242.32 MiB)

julia> @btime rsprob = create_ODEProblem(rs);
  2.241 s (4388516 allocations: 242.32 MiB)

julia> @btime osprob = create_ODEProblem(osys);
  13.086 ms (163580 allocations: 9.05 MiB)

julia> @btime osprob = create_ODEProblem(osys);
  13.267 ms (163580 allocations: 9.05 MiB)

From this the problem is already apparent; about two orders of magnitude longer time is needed to create the ODEProblem from the ReactionSystem. This issue is even more pressing the larger the no. of species S.
Now, I know I have read somewhere that it is expected to take a bit longer. But even converting the ReactionSystem to an ODESystem, i.e. using convert(ODESystem, rs), and then making the ODEProblem does not change anything for me.

Can someone explain to me what is going on? Am I missing something? Why does creating the ODEProblem takes so much time (and memory)? Why is this different from the ModelingToolkit.jl implementation?
I would like to learn how to do this properly, as I really would like to use the ReactionSystem to create other types of problems, such as SDEProblem, and JumpProblem -- which I think is one of the main selling points for using Catalyst.jl.

Any help or comments are greatly appreciated!

Environment (please complete the following information):

Details
  [c7e460c6] ArgParse v1.2.0
  [ec485272] ArnoldiMethod v0.4.0
  [6e4b80f9] BenchmarkTools v1.5.0
  [336ed68f] CSV v0.10.14
  [13f3f980] CairoMakie v0.12.9
  [479239e8] Catalyst v14.4.1
  [5ae59095] Colors v0.12.11
  [861a8166] Combinatorics v1.0.2
  [a93c6f00] DataFrames v1.6.1
  [0c46a032] DifferentialEquations v7.14.0
  [31c24e10] Distributions v0.25.111
  [ffbed154] DocStringExtensions v0.9.3
  [e30172f5] Documenter v1.7.0
  [d872a56f] ElectronDisplay v1.0.1
  [d4d017d3] ExponentialUtilities v1.26.1
  [68837c9b] FHist v0.11.6
  [86223c79] Graphs v1.11.2
  [033835bb] JLD2 v0.5.1
  [ccbc3e58] JumpProcesses v9.13.7
  [b964fa9f] LaTeXStrings v1.3.1
  [23fbe1c1] Latexify v0.16.5
  [2fda8390] LsqFit v0.15.0
  [961ee093] ModelingToolkit v9.39.1
  [2774e3e8] NLsolve v4.5.1
  [77ba4419] NaNMath v1.0.2
  [67456a42] OhMyThreads v0.6.0
  [1dea7af3] OrdinaryDiffEq v6.89.0
  [91a5bcdd] Plots v1.40.8
  [1fd47b50] QuadGK v2.11.0
  [0bca4576] SciMLBase v2.53.0
  [a9a3c162] SparseArrayKit v0.3.1
  [276daf66] SpecialFunctions v2.4.0
  [0c5d862f] Symbolics v6.11.0
  [bd369af6] Tables v1.12.0
  [3a884ed6] UnPack v1.0.2
  [8ba89e20] Distributed
  [37e2e46d] LinearAlgebra
  [9a3f8284] Random
  [2f01184e] SparseArrays v1.10.0
  [10745b16] Statistics v1.10.0
julia> versioninfo()
Julia Version 1.10.4
Commit 48d4fd48430 (2024-06-04 10:41 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 12 × 12th Gen Intel(R) Core(TM) i5-1235U
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-15.0.7 (ORCJIT, alderlake)
Threads: 8 default, 0 interactive, 4 GC (on 12 virtual cores)
Environment:
  JULIA_NUM_THREADS = 4

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions