From 267c35122002148b045fca96b18080e6aaf1cea7 Mon Sep 17 00:00:00 2001 From: Torkel Loman Date: Mon, 12 Feb 2024 19:21:39 -0500 Subject: [PATCH 01/51] init --- .../compositional_modeling.md | 44 +++++++++++++++++-- src/reaction_network.jl | 2 +- src/reactionsystem.jl | 11 +++-- test/dsl/dsl_options.jl | 22 +++++++++- test/extensions/structural_identifiability.jl | 1 - test/miscellaneous_tests/compound_macro.jl | 4 +- test/miscellaneous_tests/nonlinear_solve.jl | 4 +- .../miscellaneous_tests/reaction_balancing.jl | 4 +- .../component_based_model_creation.jl | 34 +++++++------- .../programmatic_model_expansion.jl | 4 +- .../reactionsystem.jl | 18 ++++++++ 11 files changed, 114 insertions(+), 34 deletions(-) diff --git a/docs/src/catalyst_functionality/compositional_modeling.md b/docs/src/catalyst_functionality/compositional_modeling.md index 8b9b3b2db6..862c1d9d19 100644 --- a/docs/src/catalyst_functionality/compositional_modeling.md +++ b/docs/src/catalyst_functionality/compositional_modeling.md @@ -5,6 +5,35 @@ can construct the earlier repressilator model by composing together three identically repressed genes, and how to use compositional modeling to create compartments. +## A note on *completeness* +Catalyst `ReactionSystem` can either be *complete* or *incomplete*. *By default they are created as complete*. Here, only complete `ReactionSystem`s can be used to create the various problem types (e.g. `ODEProblem`). However, only incomplete `ReactionSystem`s can be composed using the features described below. Hence, for compositional modeling, `ReactionSystem` must be created as incomplete, and later set to complete before simulation. + +To set a `ReactionSystem` created via the DSL as complete, use the `@incomplete` option: +```@example ex0 +using Catalyst +degradation_component = @reaction_network begin + @incomplete + d, X --> 0 +end +``` +Or when created directly, use the `complete = false` argument: +```@example ex0 +@parameters d +@variable t +@species X(t) +rx = Reaction(d, [X], nothing) +@named degradation_component = ReactionSystem([rs], t; complete = false) +``` +We can test whether a system is complete using the `ModelingToolkit.iscomplete` function: +```@example ex0 +ModelingToolkit.iscomplete(degradation_component) +``` +To make a incomplete system complete, we can use the `complete` function: +```@example ex0 +degradation_component_complete = complete(degradation_component) +ModelingToolkit.iscomplete(degradation_component_complete) +``` + ## Compositional modeling tooling Catalyst supports two ModelingToolkit interfaces for composing multiple [`ReactionSystem`](@ref)s together into a full model. The first mechanism for @@ -12,9 +41,11 @@ extending a system is the `extend` command ```@example ex1 using Catalyst basern = @reaction_network rn1 begin + @incomplete k, A + B --> C end newrn = @reaction_network rn2 begin + @incomplete r, C --> A + B end @named rn = extend(newrn, basern) @@ -28,8 +59,9 @@ now add to `basern` two subsystems, `newrn` and `newestrn`, we get a different result: ```@example ex1 newestrn = @reaction_network rn3 begin - v, A + D --> 2D - end + @incomplete + v, A + D --> 2D +end @named rn = compose(basern, [newrn, newestrn]) ``` Here we have created a new `ReactionSystem` that adds `newrn` and `newestrn` as @@ -55,7 +87,7 @@ t = default_t() @parameters k @species A(t), B(t), C(t) rxs = [Reaction(k, [A,B], [C])] -@named rn = ReactionSystem(rxs, t; systems = [newrn, newestrn]) +@named rn = ReactionSystem(rxs, t; systems = [newrn, newestrn], complete = false) ``` Catalyst provides several different accessors for getting information from a @@ -100,6 +132,7 @@ repressed gene, taking the repressor as input ```@example ex1 function repressed_gene(; R, name) @reaction_network $name begin + @incomplete hillr($R,α,K,n), ∅ --> m (δ,γ), m <--> ∅ β, m --> m + P @@ -119,7 +152,7 @@ t = default_t() @named G1 = repressed_gene(; R=ParentScope(G3₊P)) @named G2 = repressed_gene(; R=ParentScope(G1.P)) @named G3 = repressed_gene(; R=ParentScope(G2.P)) -@named repressilator = ReactionSystem(t; systems=[G1,G2,G3]) +@named repressilator = ReactionSystem(t; systems=[G1,G2,G3], complete = false) ``` Notice, in this system each gene is a child node in the system graph of the repressilator ```julia @@ -157,12 +190,14 @@ account for compartment volumes: ```@example ex1 # transcription and regulation nuc = @reaction_network nuc begin + @incomplete α, G --> G + M (κ₊/V,κ₋), D + G <--> DG end # translation and dimerization cyto = @reaction_network cyto begin + @incomplete β, M --> M + P (k₊/V,k₋), 2P <--> D σ, P --> 0 @@ -172,6 +207,7 @@ end # export reactions, # γ,δ=probability per time to be exported/imported model = @reaction_network model begin + @incomplete γ, $(nuc.M) --> $(cyto.M) δ, $(cyto.D) --> $(nuc.D) end diff --git a/src/reaction_network.jl b/src/reaction_network.jl index d2d5b29594..e786e47daa 100644 --- a/src/reaction_network.jl +++ b/src/reaction_network.jl @@ -123,7 +123,7 @@ const forbidden_variables_error = let end # Declares the keys used for various options. -const option_keys = (:species, :parameters, :variables, :ivs, :compounds, :observables, :default_noise_scaling) +const option_keys = (:species, :parameters, :variables, :ivs, :compounds, :observables, :default_noise_scaling, :incomplete) ### The @species macro, basically a copy of the @variables macro. ### macro species(ex...) diff --git a/src/reactionsystem.jl b/src/reactionsystem.jl index b84a7938cb..185057605c 100644 --- a/src/reactionsystem.jl +++ b/src/reactionsystem.jl @@ -609,7 +609,8 @@ function ReactionSystem(eqs, iv, unknowns, ps; balanced_bc_check = true, spatial_ivs = nothing, continuous_events = nothing, - discrete_events = nothing) + discrete_events = nothing, + complete = true) name === nothing && throw(ArgumentError("The `name` keyword must be provided. Please consider using the `@named` macro")) sysnames = nameof.(systems) @@ -678,7 +679,7 @@ function ReactionSystem(eqs, iv, unknowns, ps; ReactionSystem(eqs′, rxs, iv′, sivs′, unknowns′, spcs, ps′, var_to_name, observed, name, systems, defaults, connection_type, nps, combinatoric_ratelaws, - ccallbacks, dcallbacks; checks = checks) + ccallbacks, dcallbacks, complete; checks = checks) end function ReactionSystem(rxs::Vector, iv = Catalyst.DEFAULT_IV; kwargs...) @@ -1803,7 +1804,8 @@ function MT.flatten(rs::ReactionSystem; name = nameof(rs)) balanced_bc_check = false, spatial_ivs = get_sivs(rs), continuous_events = MT.continuous_events(rs), - discrete_events = MT.discrete_events(rs)) + discrete_events = MT.discrete_events(rs), + complete = false) end """ @@ -1860,5 +1862,6 @@ function ModelingToolkit.extend(sys::MT.AbstractSystem, rs::ReactionSystem; balanced_bc_check = false, spatial_ivs = sivs, continuous_events, - discrete_events) + discrete_events, + complete = false) end diff --git a/test/dsl/dsl_options.jl b/test/dsl/dsl_options.jl index 419863cb7d..3666c44eaf 100644 --- a/test/dsl/dsl_options.jl +++ b/test/dsl/dsl_options.jl @@ -629,4 +629,24 @@ let @observables $X ~ X1 + X2 (k1,k2), X1 <--> X2 end -end \ No newline at end of file +end + +### Tests Completeness Designations ### +let + # Creates models with/without the `@incomplete` option. + rn1 = @reaction_network begin + (p,d), 0 <--> X + end + rn2 = @reaction_network begin + @incomplete + (p,d), 0 <--> X + end + + # Check that the correct compeltness is achived. + @test ModelingToolkit.iscomplete(rn1) + @test !ModelingToolkit.iscomplete(rn2) + rn2 = complete(rn2) + @test ModelingToolkit.iscomplete(rn2) +end + + \ No newline at end of file diff --git a/test/extensions/structural_identifiability.jl b/test/extensions/structural_identifiability.jl index 74ce98b6d5..ee78c17482 100644 --- a/test/extensions/structural_identifiability.jl +++ b/test/extensions/structural_identifiability.jl @@ -193,7 +193,6 @@ let si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities=[pₑ, pₚ], known_p=[pₑ]) # Tests using model.component style (have to make system complete first). - gw_osc_complt = complete(goodwind_oscillator_catalyst) @test make_si_ode(gw_osc_complt; measured_quantities=[gw_osc_complt.M]) isa ODE @test make_si_ode(gw_osc_complt; known_p=[gw_osc_complt.pₑ]) isa ODE @test make_si_ode(gw_osc_complt; measured_quantities=[gw_osc_complt.M], known_p=[gw_osc_complt.pₑ]) isa ODE diff --git a/test/miscellaneous_tests/compound_macro.jl b/test/miscellaneous_tests/compound_macro.jl index cbf0076ca1..1eb7e43623 100644 --- a/test/miscellaneous_tests/compound_macro.jl +++ b/test/miscellaneous_tests/compound_macro.jl @@ -297,7 +297,7 @@ end # Test using multiple compounds. # Test using rn. notation to fetch species. let - rn = complete(@reaction_network begin + rn = @reaction_network begin @species C(t) O(t) H(t) @compounds begin CH4 ~ C + 4H @@ -306,7 +306,7 @@ let H2O ~ 2H + O end k, CH4 + O2 --> CO2 + H2O - end) + end species(rn) @test length(species(rn)) == 7 diff --git a/test/miscellaneous_tests/nonlinear_solve.jl b/test/miscellaneous_tests/nonlinear_solve.jl index fe4afb08f3..fa84dbb86b 100644 --- a/test/miscellaneous_tests/nonlinear_solve.jl +++ b/test/miscellaneous_tests/nonlinear_solve.jl @@ -76,11 +76,11 @@ end # Checks using interfacing with output solution. let # Creates steady state network, unpack the parameter values. - steady_state_network_3 = complete(@reaction_network begin + steady_state_network_3 = @reaction_network begin (p,d), 0 <--> X (k1, k2), 2Y <--> Y2 (k3, k4), X + Y2 <--> XY2 - end) + end @unpack X, Y, Y2, XY2 = steady_state_network_3 # Creates NonlinearProblem. diff --git a/test/miscellaneous_tests/reaction_balancing.jl b/test/miscellaneous_tests/reaction_balancing.jl index b5af82e075..1bd0e656db 100644 --- a/test/miscellaneous_tests/reaction_balancing.jl +++ b/test/miscellaneous_tests/reaction_balancing.jl @@ -395,7 +395,7 @@ end # Checks that balancing work for a reaction from a reaction_network. let - rn = complete(@reaction_network begin + rn = @reaction_network begin @species C(t) H(t) O(t) @compounds begin O2 ~ 2O @@ -404,7 +404,7 @@ let C6H12O6 ~ 6C + 12H + 6O end k, CO2 + H2O --> C6H12O6 + O2 - end) + end brxs = balance_reaction(reactions(rn)[1])[1] diff --git a/test/programmatic_model_creation/component_based_model_creation.jl b/test/programmatic_model_creation/component_based_model_creation.jl index 259d9318bd..cf50636a8a 100644 --- a/test/programmatic_model_creation/component_based_model_creation.jl +++ b/test/programmatic_model_creation/component_based_model_creation.jl @@ -20,7 +20,7 @@ let specs = [m, P, R] pars = [α₀, α, K, n, δ, β, μ] - @named rs = ReactionSystem(rxs, t, specs, pars) + @named rs = ReactionSystem(rxs, t, specs, pars; complete = false) # Using ODESystem components. @named sys₁ = convert(ODESystem, rs; include_zero_odes = false) @@ -71,14 +71,14 @@ let @test all(isapprox.(sol(tvs, idxs = sys₁.P), sol2(tvs, idxs = 4), atol = 1e-4)) # Using ReactionSystem components. - @named sys₁ = ReactionSystem(rxs, t, specs, pars) - @named sys₂ = ReactionSystem(rxs, t, specs, pars) - @named sys₃ = ReactionSystem(rxs, t, specs, pars) + @named sys₁ = ReactionSystem(rxs, t, specs, pars; complete = false) + @named sys₂ = ReactionSystem(rxs, t, specs, pars; complete = false) + @named sys₃ = ReactionSystem(rxs, t, specs, pars; complete = false) connections = [ParentScope(sys₁.R) ~ ParentScope(sys₃.P), ParentScope(sys₂.R) ~ ParentScope(sys₁.P), ParentScope(sys₃.R) ~ ParentScope(sys₂.P)] @named csys = ODESystem(connections, t, [], []) - @named repressilator = ReactionSystem(t; systems = [csys, sys₁, sys₂, sys₃]) + @named repressilator = ReactionSystem(t; systems = [csys, sys₁, sys₂, sys₃], complete = false) @named oderepressilator2 = convert(ODESystem, repressilator, include_zero_odes = false) sys2 = structural_simplify(oderepressilator2) # FAILS currently oprob = ODEProblem(sys2, u₀, tspan, pvals) @@ -87,7 +87,7 @@ let # Test conversion to nonlinear system. @named nsys = NonlinearSystem(connections, [], []) - @named ssrepressilator = ReactionSystem(t; systems = [nsys, sys₁, sys₂, sys₃]) + @named ssrepressilator = ReactionSystem(t; systems = [nsys, sys₁, sys₂, sys₃], complete = false) @named nlrepressilator = convert(NonlinearSystem, ssrepressilator, include_zero_odes = false) sys2 = structural_simplify(nlrepressilator) @@ -117,7 +117,7 @@ let sys₃.R ~ sys₂.P] @named csys = NonlinearSystem(connections, [sys₁.R, sys₃.P, sys₂.R, sys₁.P, sys₃.R, sys₂.P], []) - @named repressilator2 = ReactionSystem(connections, t; systems = [sys₁, sys₂, sys₃]) + @named repressilator2 = ReactionSystem(connections, t; systems = [sys₁, sys₂, sys₃], complete = false) @named nlrepressilator = convert(NonlinearSystem, repressilator2, include_zero_odes = false) sys2 = structural_simplify(nlrepressilator) @test length(equations(sys2)) <= 6 @@ -231,7 +231,7 @@ let @test all(isapprox.(sol(tvs, idxs = sys₁.P), sol2(tvs, idxs = 4), atol = 1e-4)) # Test extending with NonlinearSystem. - @named repressilator2 = ReactionSystem(t; systems = [sys₁, sys₂, sys₃]) + @named repressilator2 = ReactionSystem(t; systems = [sys₁, sys₂, sys₃], complete = false) repressilator2 = Catalyst.flatten(repressilator2) repressilator2 = extend(csys, repressilator2) @named nlrepressilator = convert(NonlinearSystem, repressilator2, include_zero_odes = false) @@ -253,8 +253,8 @@ let @species A(t), B(t), C(t), D(t) rxs1 = [Reaction(r₊, [A, B], [C])] rxs2 = [Reaction(r₋, [C], [A, B])] - @named rs1 = ReactionSystem(rxs1, t, [A, B, C], [r₊]) - @named rs2 = ReactionSystem(rxs2, t, [A, B, C], [r₋]) + @named rs1 = ReactionSystem(rxs1, t, [A, B, C], [r₊]; complete = false) + @named rs2 = ReactionSystem(rxs2, t, [A, B, C], [r₋]; complete = false) @named rs = extend(rs1, rs2) @test issetequal(unknowns(rs), [A, B, C]) @test issetequal(parameters(rs), [r₊, r₋]) @@ -301,13 +301,13 @@ let eqs3 = [ParentScope(A2a) ~ p3b * A3b] @named rs3 = ReactionSystem(rxs3, t, [A3a, ParentScope(A2a)], [p3a, ParentScope(p2a)]; - combinatoric_ratelaws = false) + combinatoric_ratelaws = false, complete = false) @named ns3 = NonlinearSystem(eqs3, [ParentScope(A2a), A3b], [p3b]) @named rs2 = ReactionSystem(rxs2, t, [A2a, ParentScope(A1)], [p2a, p2b], - systems = [rs3, ns3]; combinatoric_ratelaws = true) + systems = [rs3, ns3]; combinatoric_ratelaws = true, complete = false) @named ns2 = NonlinearSystem(eqs2, [ParentScope(A1), A2b], [ParentScope(p1)]) @named rs1 = ReactionSystem(rxs1, t, [A1], [p1], systems = [rs2, ns2]; - combinatoric_ratelaws = false) + combinatoric_ratelaws = false, complete = false) # Namespaced reactions. nrxs1 = [Reaction(p1, [A1], nothing)] @@ -427,13 +427,13 @@ let D = default_time_deriv() rx1 = Reaction(k1*V1, [A1], [B1]) eq1 = D(V1) ~ -V1 - @named rs1 = ReactionSystem([rx1, eq1], t) + @named rs1 = ReactionSystem([rx1, eq1], t; complete = false) rx2 = Reaction(k2*V2, [A2], [B2]) eq2 = D(V2) ~ -V2 - @named rs2 = ReactionSystem([rx2, eq2], t) + @named rs2 = ReactionSystem([rx2, eq2], t; complete = false) rx3 = Reaction(k3*V3, [A3], [B3]) eq3 = D(V3) ~ -V3 - @named rs3 = ReactionSystem([rx3, eq3], t) + @named rs3 = ReactionSystem([rx3, eq3], t; complete = false) @named rs23 = compose(rs2, [rs3]) @test length(unknowns(rs23)) == 6 @test all(p -> isequal(p[1], p[2]), zip(unknowns(rs23)[1:4], species(rs23))) @@ -453,12 +453,14 @@ end # Tests that conversion with defaults works for a composed model. let rn1 = @reaction_network rn1 begin + @incomplete @parameters p=1.0 r=2.0 @species X(t) = 3.0 Y(t) = 4.0 (p1, d), 0 <--> X (p2, r), 0 <--> Z end rn2 = @reaction_network rn1 begin + @incomplete @parameters p=10. q=20.0 @species X(t) = 30.0 Z(t) = 40.0 (p1, d), 0 <--> X diff --git a/test/programmatic_model_creation/programmatic_model_expansion.jl b/test/programmatic_model_creation/programmatic_model_expansion.jl index c4bfa059e0..7d63e174c8 100644 --- a/test/programmatic_model_creation/programmatic_model_expansion.jl +++ b/test/programmatic_model_creation/programmatic_model_expansion.jl @@ -48,7 +48,9 @@ end # Tests accessing parameters and species added with network API. let - empty_network_3 = @reaction_network + empty_network_3 = @reaction_network begin + @incomplete + end @parameters p @species x(t) addspecies!(empty_network_3, x) diff --git a/test/reactionsystem_structure/reactionsystem.jl b/test/reactionsystem_structure/reactionsystem.jl index a977143ce7..bafb1f05bc 100644 --- a/test/reactionsystem_structure/reactionsystem.jl +++ b/test/reactionsystem_structure/reactionsystem.jl @@ -710,3 +710,21 @@ let @test isnothing(ModelingToolkit.get_metadata(rs)) end + +### Tests Completeness Designations ### +let + # Creates models with/without the `complete` input. + @parameters p d + @variables t + @species X(t) + r1 = Reaction(p, [X], nothing) + r2 = Reaction(d, nothing, [X]) + @named rs1 = ReactionSystem([r1, r2], t) + @named rs2 = ReactionSystem([r1, r2], t; complete = false) + + # Check that the correct compeltness is achived. + @test ModelingToolkit.iscomplete(rs1) + @test !ModelingToolkit.iscomplete(rs2) + rs2 = complete(rs2) + @test ModelingToolkit.iscomplete(rs2) +end From e67d20d2a732f3fc9dcc97acd132360b1b616517 Mon Sep 17 00:00:00 2001 From: Torkel Loman Date: Mon, 12 Feb 2024 19:21:46 -0500 Subject: [PATCH 02/51] up --- test/extensions/bifurcation_kit.jl | 8 +++++++- test/extensions/structural_identifiability.jl | 3 +++ test/miscellaneous_tests/api.jl | 7 ++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/test/extensions/bifurcation_kit.jl b/test/extensions/bifurcation_kit.jl index d388eb3f69..c67c75a8a1 100644 --- a/test/extensions/bifurcation_kit.jl +++ b/test/extensions/bifurcation_kit.jl @@ -86,22 +86,26 @@ end # Tests with defaults within nested networks. let rn1 = @reaction_network rn1 begin + @incomplete @parameters p=1.0 (p, d), 0 <--> X end rn2 = @reaction_network rn2 begin + @incomplete @parameters p=2.0 (p, d), 0 <--> X end rn3 = @reaction_network rn3 begin + @incomplete @parameters p=3.0 (p, d), 0 <--> X end rn4 = @reaction_network rn4 begin + @incomplete @parameters p=4.0 (p, d), 0 <--> X end - @named rn3 =compose(rn3, [rn4]) + @named rn3 = compose(rn3, [rn4]) @named rn = compose(rn1, [rn2, rn3]) # Declares parameter values and initial u guess. @@ -144,9 +148,11 @@ end let # Creates model. rn1 = @reaction_network rn1 begin + @incomplete (k1, k2), X1 <--> X2 end rn2 = @reaction_network rn2 begin + @incomplete (l1, l2), Y1 <--> Y2 end @named rn = compose(rn1, [rn2]) diff --git a/test/extensions/structural_identifiability.jl b/test/extensions/structural_identifiability.jl index ee78c17482..9c9cfb1217 100644 --- a/test/extensions/structural_identifiability.jl +++ b/test/extensions/structural_identifiability.jl @@ -193,6 +193,7 @@ let si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities=[pₑ, pₚ], known_p=[pₑ]) # Tests using model.component style (have to make system complete first). + gw_osc_complt = complete(goodwind_oscillator_catalyst) @test make_si_ode(gw_osc_complt; measured_quantities=[gw_osc_complt.M]) isa ODE @test make_si_ode(gw_osc_complt; known_p=[gw_osc_complt.pₑ]) isa ODE @test make_si_ode(gw_osc_complt; measured_quantities=[gw_osc_complt.M], known_p=[gw_osc_complt.pₑ]) isa ODE @@ -217,9 +218,11 @@ end let # Identifiability analysis for Catalyst model. rs1 = @reaction_network rs1 begin + @incomplete (k1, k2), X1 <--> X2 end rs2 = @reaction_network rs2 begin + @incomplete (k3, k4), X3 <--> X4 end @named rs_catalyst = compose(rs1, [rs2]) diff --git a/test/miscellaneous_tests/api.jl b/test/miscellaneous_tests/api.jl index 2776e25bf4..b262cf3e90 100644 --- a/test/miscellaneous_tests/api.jl +++ b/test/miscellaneous_tests/api.jl @@ -486,12 +486,17 @@ let sol3 = unpacktest(rn) @test norm(sol.u - sol3.u) ≈ 0 + # Test symmap_to_varmap. # Test symmap_to_varmap. sir = @reaction_network sir begin + @incomplete β, S + I --> 2I ν, I --> R end - subsys = @reaction_network subsys begin k, A --> B end + subsys = @reaction_network subsys begin + @incomplete + k, A --> B + end @named sys = compose(sir, [subsys]) symmap = [:S => 1.0, :I => 1.0, :R => 1.0, :subsys₊A => 1.0, :subsys₊B => 1.0] u0map = symmap_to_varmap(sys, symmap) From e23c72fb291fd66a71be40772ef685f951798735 Mon Sep 17 00:00:00 2001 From: Torkel Date: Mon, 18 Mar 2024 19:06:43 -0400 Subject: [PATCH 03/51] up --- src/Catalyst.jl | 2 +- src/reactionsystem.jl | 30 +++++++++++++++++++----------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/Catalyst.jl b/src/Catalyst.jl index 91b403da36..f0aaac63f9 100644 --- a/src/Catalyst.jl +++ b/src/Catalyst.jl @@ -33,7 +33,7 @@ import ModelingToolkit: get_variables, namespace_expr, namespace_equation, get_v # internal but needed ModelingToolkit functions import ModelingToolkit: check_variables, check_parameters, _iszero, _merge, check_units, - get_unit, check_equations + get_unit, check_equations, iscomplete import Base: (==), hash, size, getindex, setindex, isless, Sort.defalg, length, show import MacroTools, Graphs diff --git a/src/reactionsystem.jl b/src/reactionsystem.jl index 185057605c..dd0c376297 100644 --- a/src/reactionsystem.jl +++ b/src/reactionsystem.jl @@ -545,7 +545,7 @@ struct ReactionSystem{V <: NetworkProperties} <: function ReactionSystem(eqs, rxs, iv, sivs, unknowns, spcs, ps, var_to_name, observed, name, systems, defaults, connection_type, nps, cls, cevs, devs, metadata = nothing, complete::Bool = false; checks::Bool = true) - + println("Complete 1: ", complete) # unit checks are for ODEs and Reactions only currently nonrx_eqs = Equation[eq for eq in eqs if eq isa Equation] if checks && isempty(sivs) @@ -611,6 +611,7 @@ function ReactionSystem(eqs, iv, unknowns, ps; continuous_events = nothing, discrete_events = nothing, complete = true) + println("Complete 2: ", complete) name === nothing && throw(ArgumentError("The `name` keyword must be provided. Please consider using the `@named` macro")) sysnames = nameof.(systems) @@ -683,6 +684,7 @@ function ReactionSystem(eqs, iv, unknowns, ps; end function ReactionSystem(rxs::Vector, iv = Catalyst.DEFAULT_IV; kwargs...) + println("Complete 3: ", complete) make_ReactionSystem_internal(rxs, iv, Vector{Num}(), Vector{Num}(); kwargs...) end @@ -749,11 +751,13 @@ function make_ReactionSystem_internal(rxs_and_eqs::Vector, iv, sts_in, ps_in; else fulleqs = rxs end + println("Complete 4: ", kwargs...) ReactionSystem(fulleqs, t, stsv, psv; spatial_ivs, kwargs...) end function ReactionSystem(iv; kwargs...) + println("Complete 5: ", complete) ReactionSystem(Reaction[], iv, [], []; kwargs...) end @@ -1492,14 +1496,14 @@ function Base.convert(::Type{<:ODESystem}, rs::ReactionSystem; name = nameof(rs) default_u0 = Dict(), default_p = Dict(), defaults = _merge(Dict(default_u0), Dict(default_p)), kwargs...) spatial_convert_err(rs::ReactionSystem, ODESystem) - fullrs = Catalyst.flatten(rs) + fullrs = Catalyst.flatten(rs; complete = true) remove_conserved && conservationlaws(fullrs) ists, ispcs = get_indep_sts(fullrs, remove_conserved) eqs = assemble_drift(fullrs, ispcs; combinatoric_ratelaws, remove_conserved, include_zero_odes) eqs, sts, ps, obs, defs = addconstraints!(eqs, fullrs, ists, ispcs; remove_conserved) - ODESystem(eqs, get_iv(fullrs), sts, ps; + osys = ODESystem(eqs, get_iv(fullrs), sts, ps; observed = obs, name, defaults = _merge(defaults,defs), @@ -1507,6 +1511,7 @@ function Base.convert(::Type{<:ODESystem}, rs::ReactionSystem; name = nameof(rs) continuous_events = MT.get_continuous_events(fullrs), discrete_events = MT.get_discrete_events(fullrs), kwargs...) + return iscomplete(rs) ? complete(osys) : osys end """ @@ -1532,7 +1537,7 @@ function Base.convert(::Type{<:NonlinearSystem}, rs::ReactionSystem; name = name default_u0 = Dict(), default_p = Dict(), defaults = _merge(Dict(default_u0), Dict(default_p)), kwargs...) spatial_convert_err(rs::ReactionSystem, NonlinearSystem) - fullrs = Catalyst.flatten(rs) + fullrs = Catalyst.flatten(rs; complete = true) remove_conserved && conservationlaws(fullrs) ists, ispcs = get_indep_sts(fullrs, remove_conserved) eqs = assemble_drift(fullrs, ispcs; combinatoric_ratelaws, remove_conserved, @@ -1540,12 +1545,13 @@ function Base.convert(::Type{<:NonlinearSystem}, rs::ReactionSystem; name = name error_if_constraint_odes(NonlinearSystem, fullrs) eqs, sts, ps, obs, defs = addconstraints!(eqs, fullrs, ists, ispcs; remove_conserved) - NonlinearSystem(eqs, sts, ps; + nlsys = NonlinearSystem(eqs, sts, ps; name, observed = obs, defaults = _merge(defaults,defs), checks, kwargs...) + return iscomplete(rs) ? complete(nlsys) : nlsys end """ @@ -1574,7 +1580,7 @@ function Base.convert(::Type{<:SDESystem}, rs::ReactionSystem; kwargs...) spatial_convert_err(rs::ReactionSystem, SDESystem) - flatrs = Catalyst.flatten(rs) + flatrs = Catalyst.flatten(rs; complete = true) error_if_constraints(SDESystem, flatrs) remove_conserved && conservationlaws(flatrs) @@ -1588,7 +1594,7 @@ function Base.convert(::Type{<:SDESystem}, rs::ReactionSystem; @info "Boundary condition species detected. As constraint equations are not currently supported when converting to SDESystems, the resulting system will be undetermined. Consider using constant species instead." end - SDESystem(eqs, noiseeqs, get_iv(flatrs), sts, ps; + ssys = SDESystem(eqs, noiseeqs, get_iv(flatrs), sts, ps; observed = obs, name, defaults = defs, @@ -1596,6 +1602,7 @@ function Base.convert(::Type{<:SDESystem}, rs::ReactionSystem; continuous_events = MT.get_continuous_events(flatrs), discrete_events = MT.get_discrete_events(flatrs), kwargs...) + return iscomplete(rs) ? complete(ssys) : ssys end """ @@ -1626,7 +1633,7 @@ function Base.convert(::Type{<:JumpSystem}, rs::ReactionSystem; name = nameof(rs (remove_conserved !== nothing) && error("Catalyst does not support removing conserved species when converting to JumpSystems.") - flatrs = Catalyst.flatten(rs) + flatrs = Catalyst.flatten(rs; complete = true) error_if_constraints(JumpSystem, flatrs) (length(MT.continuous_events(flatrs)) > 0) && @@ -1639,13 +1646,14 @@ function Base.convert(::Type{<:JumpSystem}, rs::ReactionSystem; name = nameof(rs any(isbc, get_unknowns(flatrs)) && (sts = vcat(sts, filter(isbc, get_unknowns(flatrs)))) ps = get_ps(flatrs) - JumpSystem(eqs, get_iv(flatrs), sts, ps; + jsys = JumpSystem(eqs, get_iv(flatrs), sts, ps; observed = MT.observed(flatrs), name, defaults = _merge(defaults,MT.defaults(flatrs)), checks, discrete_events = MT.discrete_events(flatrs), kwargs...) + return iscomplete(rs) ? complete(jsys) : jsys end ### Converts a reaction system to ODE or SDE problems ### @@ -1786,7 +1794,7 @@ Notes: - The default value of `combinatoric_ratelaws` will be the logical or of all `ReactionSystem`s. """ -function MT.flatten(rs::ReactionSystem; name = nameof(rs)) +function MT.flatten(rs::ReactionSystem; name = nameof(rs), complete = false) isempty(get_systems(rs)) && return rs # right now only NonlinearSystems and ODESystems can be handled as subsystems @@ -1805,7 +1813,7 @@ function MT.flatten(rs::ReactionSystem; name = nameof(rs)) spatial_ivs = get_sivs(rs), continuous_events = MT.continuous_events(rs), discrete_events = MT.discrete_events(rs), - complete = false) + complete = complete) end """ From 3aff3b035cd27a1926a7a41a02e81e3c53adeda9 Mon Sep 17 00:00:00 2001 From: Torkel Date: Mon, 18 Mar 2024 20:15:51 -0400 Subject: [PATCH 04/51] tests shoudl start passing --- src/reactionsystem.jl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/reactionsystem.jl b/src/reactionsystem.jl index dd0c376297..c7acd48d87 100644 --- a/src/reactionsystem.jl +++ b/src/reactionsystem.jl @@ -545,7 +545,7 @@ struct ReactionSystem{V <: NetworkProperties} <: function ReactionSystem(eqs, rxs, iv, sivs, unknowns, spcs, ps, var_to_name, observed, name, systems, defaults, connection_type, nps, cls, cevs, devs, metadata = nothing, complete::Bool = false; checks::Bool = true) - println("Complete 1: ", complete) + # unit checks are for ODEs and Reactions only currently nonrx_eqs = Equation[eq for eq in eqs if eq isa Equation] if checks && isempty(sivs) @@ -610,8 +610,9 @@ function ReactionSystem(eqs, iv, unknowns, ps; spatial_ivs = nothing, continuous_events = nothing, discrete_events = nothing, + metadata = nothing, complete = true) - println("Complete 2: ", complete) + name === nothing && throw(ArgumentError("The `name` keyword must be provided. Please consider using the `@named` macro")) sysnames = nameof.(systems) @@ -680,11 +681,10 @@ function ReactionSystem(eqs, iv, unknowns, ps; ReactionSystem(eqs′, rxs, iv′, sivs′, unknowns′, spcs, ps′, var_to_name, observed, name, systems, defaults, connection_type, nps, combinatoric_ratelaws, - ccallbacks, dcallbacks, complete; checks = checks) + ccallbacks, dcallbacks, metadata, complete; checks = checks) end function ReactionSystem(rxs::Vector, iv = Catalyst.DEFAULT_IV; kwargs...) - println("Complete 3: ", complete) make_ReactionSystem_internal(rxs, iv, Vector{Num}(), Vector{Num}(); kwargs...) end @@ -751,13 +751,11 @@ function make_ReactionSystem_internal(rxs_and_eqs::Vector, iv, sts_in, ps_in; else fulleqs = rxs end - println("Complete 4: ", kwargs...) ReactionSystem(fulleqs, t, stsv, psv; spatial_ivs, kwargs...) end function ReactionSystem(iv; kwargs...) - println("Complete 5: ", complete) ReactionSystem(Reaction[], iv, [], []; kwargs...) end From 6f89a06356ff122d97fffe575e5dbb5fd66a9e66 Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 19 Mar 2024 20:28:10 -0400 Subject: [PATCH 05/51] update tests --- src/Catalyst.jl | 4 +- src/networkapi.jl | 2 +- src/reaction_network.jl | 4 +- src/reactionsystem.jl | 2 +- src/spatial_reaction_systems/utility.jl | 2 +- test/dsl/dsl_model_construction.jl | 74 ++++++------- test/extensions/bifurcation_kit.jl | 4 +- test/miscellaneous_tests/api.jl | 3 + test/miscellaneous_tests/nonlinear_solve.jl | 57 +++++----- test/model_simulation/make_jacobian.jl | 46 ++++---- test/model_simulation/simulate_ODEs.jl | 101 ++++++++++-------- test/model_simulation/simulate_SDEs.jl | 53 +++++---- .../model_simulation/u0_n_parameter_inputs.jl | 44 ++++---- .../component_based_model_creation.jl | 16 +-- test/reactionsystem_structure/reactions.jl | 4 +- .../reactionsystem.jl | 50 +++++---- test/runtests.jl | 7 ++ test/test_functions.jl | 44 ++++++++ 18 files changed, 291 insertions(+), 226 deletions(-) create mode 100644 test/test_functions.jl diff --git a/src/Catalyst.jl b/src/Catalyst.jl index f0aaac63f9..1fa198dcd2 100644 --- a/src/Catalyst.jl +++ b/src/Catalyst.jl @@ -48,8 +48,8 @@ end function default_t() return ModelingToolkit.t_nounits end -const DEFAULT_t = default_t() -const DEFAULT_IV_SYM = Symbol(DEFAULT_t) +const DEFAULT_IV = default_t() +const DEFAULT_IV_SYM = Symbol(DEFAULT_IV) export default_t, default_time_deriv # as used in Catlab diff --git a/src/networkapi.jl b/src/networkapi.jl index 1926fd34e2..c1facbaaee 100644 --- a/src/networkapi.jl +++ b/src/networkapi.jl @@ -1624,7 +1624,7 @@ function validate(rs::ReactionSystem, info::String = "") rxunits *= get_unit(sub)^rx.substoich[i] end - if rxunits != rateunits + if !isequal(rxunits, rateunits) validated = false @warn(string("Reaction rate laws are expected to have units of ", rateunits, " however, ", rx, " has units of ", rxunits, ".")) diff --git a/src/reaction_network.jl b/src/reaction_network.jl index e786e47daa..73abbf31bc 100644 --- a/src/reaction_network.jl +++ b/src/reaction_network.jl @@ -480,7 +480,7 @@ end # When compound species are declared using the "@compound begin ... end" option, get a list of the compound species, and also the expression that crates them. function read_compound_options(opts) - # If the compound option is used retrive a list of compound species (need to be added to teh reaction system's species), and the option that creates them (used to declare them as compounds at the end). + # If the compound option is used retrive a list of compound species (need to be added to the reaction system's species), and the option that creates them (used to declare them as compounds at the end). if haskey(opts, :compounds) compound_expr = opts[:compounds] # Find compound species names, and append the independent variable. @@ -772,7 +772,7 @@ function read_observed_options(options, species_n_vars_declared, ivs_sorted) # Adds the observable to the list of observable names. # This is required for filtering away so these are not added to the ReactionSystem's species list. - # Again, avoid this check if we have interpoalted teh variable. + # Again, avoid this check if we have interpoalted the variable. is_escaped_expr(obs_eq.args[2]) || push!(obs_syms.args, obs_name) end diff --git a/src/reactionsystem.jl b/src/reactionsystem.jl index c7acd48d87..d27fbb394a 100644 --- a/src/reactionsystem.jl +++ b/src/reactionsystem.jl @@ -205,7 +205,7 @@ end # Checks if a metadata input has an entry :only_use_rate => true function metadata_only_use_rate_check(metadata) only_use_rate_idx = findfirst(:only_use_rate == entry[1] for entry in metadata) - isnothing(only_use_rate_idx) && return true + isnothing(only_use_rate_idx) && return false return Bool(metadata[only_use_rate_idx][2]) end diff --git a/src/spatial_reaction_systems/utility.jl b/src/spatial_reaction_systems/utility.jl index de24c829bc..03271562e8 100644 --- a/src/spatial_reaction_systems/utility.jl +++ b/src/spatial_reaction_systems/utility.jl @@ -204,7 +204,7 @@ end # Else a vector with each value corresponding to the rate at one specific edge. function compute_transport_rates(rate_law::Num, p_val_dict::Dict{SymbolicUtils.BasicSymbolic{Real}, Vector{Float64}}, num_edges::Int64) - # Finds parameters involved in rate and create a function evaluating teh rate law. + # Finds parameters involved in rate and create a function evaluating the rate law. relevant_ps = Symbolics.get_variables(rate_law) rate_law_func = drop_expr(@RuntimeGeneratedFunction(build_function(rate_law, relevant_ps...))) diff --git a/test/dsl/dsl_model_construction.jl b/test/dsl/dsl_model_construction.jl index 2426955da1..6554b2b08a 100644 --- a/test/dsl/dsl_model_construction.jl +++ b/test/dsl/dsl_model_construction.jl @@ -10,8 +10,9 @@ t = default_t() using StableRNGs rng = StableRNG(12345) -# Fetch test networks. +# Fetch test networks and functions. include("../test_networks.jl") +include("../test_functions.jl") ### Declares Testing Functions ### @@ -122,17 +123,16 @@ let push!(identical_networks_1, reaction_networks_standard[8] => different_arrow_8) for networks in identical_networks_1 - f1 = ODEFunction(convert(ODESystem, networks[1]), jac = true) - f2 = ODEFunction(convert(ODESystem, networks[2]), jac = true) - g1 = SDEFunction(convert(SDESystem, networks[1])) - g2 = SDEFunction(convert(SDESystem, networks[2])) for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] - u0 = factor * rand(rng, length(get_unknowns(networks[1]))) - p = factor * rand(rng, length(get_ps(networks[1]))) + u0_1 = rnd_u0(networks[1], rng; factor) + p_1 = rnd_ps(networks[1], rng; factor) + u0_2 = Pair.(unknowns(networks[2]), last.(u0_1)) + p_2 = Pair.(parameters(networks[2]), last.(p_1)) t = rand(rng) - @test all(abs.(f1(u0, p, t) .≈ f2(u0, p, t))) - @test all(abs.(f1.jac(u0, p, t) .≈ f2.jac(u0, p, t))) - @test all(abs.(g1(u0, p, t) .≈ g2(u0, p, t))) + + @test f_eval(networks[1], u0_1, p_1, t) ≈ f_eval(networks[2], u0_2, p_2, t) + @test jac_eval(networks[1], u0_1, p_1, t) ≈ jac_eval(networks[2], u0_2, p_2, t) + @test g_eval(networks[1], u0_1, p_1, t) ≈ g_eval(networks[2], u0_2, p_2, t) end end end @@ -189,17 +189,16 @@ let push!(identical_networks_2, reaction_networks_standard[7] => differently_written_8) for networks in identical_networks_2 - f1 = ODEFunction(convert(ODESystem, networks[1]), jac = true) - f2 = ODEFunction(convert(ODESystem, networks[2]), jac = true) - g1 = SDEFunction(convert(SDESystem, networks[1])) - g2 = SDEFunction(convert(SDESystem, networks[2])) for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] - u0 = factor * rand(rng, length(get_unknowns(networks[1]))) - p = factor * rand(rng, length(get_ps(networks[1]))) + u0_1 = rnd_u0(networks[1], rng; factor) + p_1 = rnd_ps(networks[1], rng; factor) + u0_2 = Pair.(unknowns(networks[2]), last.(u0_1)) + p_2 = Pair.(parameters(networks[2]), last.(p_1)) t = rand(rng) - @test all(f1(u0, p, t) .≈ f2(u0, p, t)) - @test all(f1.jac(u0, p, t) .≈ f2.jac(u0, p, t)) - @test all(g1(u0, p, t) .≈ g2(u0, p, t)) + + @test f_eval(networks[1], u0_1, p_1, t) ≈ f_eval(networks[2], u0_2, p_2, t) + @test jac_eval(networks[1], u0_1, p_1, t) ≈ jac_eval(networks[2], u0_2, p_2, t) + @test g_eval(networks[1], u0_1, p_1, t) ≈ g_eval(networks[2], u0_2, p_2, t) end end end @@ -233,16 +232,16 @@ let push!(parameter_sets, [0.01, 3.1, 3.2, 0.0, 2.1, 901.0, 63.5, 7, 8, 1.0]) for (i, networks) in enumerate(identical_networks_3) - f1 = ODEFunction(convert(ODESystem, networks[1]), jac = true) - f2 = ODEFunction(convert(ODESystem, networks[2]), jac = true) - g1 = SDEFunction(convert(SDESystem, networks[1])) - g2 = SDEFunction(convert(SDESystem, networks[2])) for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] - u0 = factor * rand(rng, length(get_unknowns(networks[1]))) + u0_1 = rnd_u0(networks[1], rng; factor) + p_1 = Pair.(parameters(networks[1]), parameter_sets[i]) + u0_2 = Pair.(unknowns(networks[2]), last.(u0_1)) + p_2 = [] t = rand(rng) - @test f1(u0, parameter_sets[i], t) ≈ f2(u0, [], t) - @test f1.jac(u0, parameter_sets[i], t) ≈ f2.jac(u0, [], t) - @test g1(u0, parameter_sets[i], t) ≈ g2(u0, [], t) + + @test f_eval(networks[1], u0_1, p_1, t) ≈ f_eval(networks[2], u0_2, p_2, t) + @test jac_eval(networks[1], u0_1, p_1, t) ≈ jac_eval(networks[2], u0_2, p_2, t) + @test g_eval(networks[1], u0_1, p_1, t) ≈ g_eval(networks[2], u0_2, p_2, t) end end end @@ -306,21 +305,16 @@ let (t, k6), X3 ↔ X1 end - f1 = ODEFunction(convert(ODESystem, reaction_networks_constraint[1]), jac = true) - f2 = ODEFunction(convert(ODESystem, time_network), jac = true) - g1 = SDEFunction(convert(SDESystem, reaction_networks_constraint[1])) - g2 = SDEFunction(convert(SDESystem, time_network)) for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] - u0 = factor * rand(rng, length(get_unknowns(time_network))) - κ2 = factor * rand(rng) - κ3 = factor * rand(rng) - κ6 = factor * rand(rng) τ = rand(rng) - p1 = [τ, κ2, κ3, τ, τ, κ6] - p2 = [κ2, κ3, κ6] - @test all(f1(u0, p1, τ) .≈ f2(u0, p2, τ)) - @test all(f1.jac(u0, p1, τ) .≈ f2.jac(u0, p2, τ)) - @test all(g1(u0, p1, τ) .≈ g2(u0, p2, τ)) + u = rnd_u0(reaction_networks_constraint[1], rng; factor) + p_2 = rnd_ps(time_network, rng; factor) + p_1 = [p_2; reaction_networks_constraint[1].k1 => τ; + reaction_networks_constraint[1].k4 => τ; reaction_networks_constraint[1].k5 => τ] + + @test f_eval(reaction_networks_constraint[1], u, p_1, τ) ≈ f_eval(time_network, u, p_2, τ) + @test jac_eval(reaction_networks_constraint[1], u, p_1, τ) ≈ jac_eval(time_network, u, p_2, τ) + @test g_eval(reaction_networks_constraint[1], u, p_1, τ) ≈ g_eval(time_network, u, p_2, τ) end end diff --git a/test/extensions/bifurcation_kit.jl b/test/extensions/bifurcation_kit.jl index c67c75a8a1..4ea1b74362 100644 --- a/test/extensions/bifurcation_kit.jl +++ b/test/extensions/bifurcation_kit.jl @@ -9,7 +9,7 @@ rng = StableRNG(12345) # Brusselator extended with conserved species. # Runs full computation, checks values corresponds to known values. -# Checks that teh correct bifurcation point is found at the correct position. +# Checks that the correct bifurcation point is found at the correct position. # Checks that bifurcation diagrams can be computed for systems with conservation laws. # Checks that bifurcation diagrams can be computed for systems with default values. # Checks that bifurcation diagrams can be computed for systems with non-constant rate. @@ -107,6 +107,7 @@ let end @named rn3 = compose(rn3, [rn4]) @named rn = compose(rn1, [rn2, rn3]) + rn = complete(rn) # Declares parameter values and initial u guess. @unpack X, d = rn @@ -156,6 +157,7 @@ let (l1, l2), Y1 <--> Y2 end @named rn = compose(rn1, [rn2]) + rn = complete(rn) # Creates input parameter and species vectors. @unpack X1, X2, k1, k2 = rn1 diff --git a/test/miscellaneous_tests/api.jl b/test/miscellaneous_tests/api.jl index b262cf3e90..64a2ba393c 100644 --- a/test/miscellaneous_tests/api.jl +++ b/test/miscellaneous_tests/api.jl @@ -498,6 +498,9 @@ let k, A --> B end @named sys = compose(sir, [subsys]) + sir = complete(sir) + sys = complete(sys) + symmap = [:S => 1.0, :I => 1.0, :R => 1.0, :subsys₊A => 1.0, :subsys₊B => 1.0] u0map = symmap_to_varmap(sys, symmap) pmap = symmap_to_varmap(sys, [:β => 1.0, :ν => 1.0, :subsys₊k => 1.0]) diff --git a/test/miscellaneous_tests/nonlinear_solve.jl b/test/miscellaneous_tests/nonlinear_solve.jl index fa84dbb86b..880df22534 100644 --- a/test/miscellaneous_tests/nonlinear_solve.jl +++ b/test/miscellaneous_tests/nonlinear_solve.jl @@ -20,56 +20,55 @@ let end # Creates NonlinearProblem. - u0 = rand(rng, length(unknowns(steady_state_network_1))) - p = rand(rng, length(parameters(steady_state_network_1))) - nl_prob = NonlinearProblem(steady_state_network_1, u0, p) + u0 = rnd_u0(steady_state_network_1, rng) + ps = rnd_ps(steady_state_network_1, rng) + nlprob = NonlinearProblem(steady_state_network_1, u0, ps) # Solves it using standard algorithm and simulation based algorithm. - sol1 = solve(nl_prob; abstol=1e-12, reltol=1e-12).u - sol2 = solve(nl_prob, DynamicSS(Rosenbrock23()); abstol=1e-12, reltol=1e-12).u + sol1 = solve(nlprob; abstol=1e-12, reltol=1e-12) + sol2 = solve(nlprob, DynamicSS(Rosenbrock23()); abstol=1e-12, reltol=1e-12) # Tests solutions are correct. - @test isapprox(sol1[1], p[1] / p[2]; atol=1e-10) - @test isapprox(sol1[2]^3 / factorial(3), p[3] / p[4]; atol=1e-10) - @test isapprox(sol1[3], (p[5] * p[2]) / (p[6] * p[1]); atol=1e-10) - @test isapprox(sol2[1], p[1] / p[2]; atol=1e-10) - @test isapprox(sol2[2]^3 / factorial(3), p[3] / p[4]; atol=1e-10) - @test isapprox(sol2[3], (p[5] * p[2]) / (p[6] * p[1]); atol=1e-10) + @test sol1[:X1] ≈ nlprob.ps[:k1] / nlprob.ps[:k2] atol=1e-10 + @test sol1[:X2]^3 / factorial(3) ≈ nlprob.ps[:k3] / nlprob.ps[:k4] atol=1e-10 + @test sol1[:X3] ≈ (nlprob.ps[:k5] * nlprob.ps[:k2]) / (nlprob.ps[:k6] * nlprob.ps[:k1]) atol=1e-10 + @test sol2[:X1] ≈ nlprob.ps[:k1] / nlprob.ps[:k2] atol=1e-10 + @test sol2[:X2]^3 / factorial(3) ≈ nlprob.ps[:k3] / nlprob.ps[:k4] atol=1e-10 + @test sol2[:X3] ≈ (nlprob.ps[:k5] * nlprob.ps[:k2]) / (nlprob.ps[:k6] * nlprob.ps[:k1]) atol=1e-10 end # Creates a system with multiple steady states. # Checks that corresponding ODEFunction return 0.0 in both cases. Checks for manually computed function as well. -# Checks with SYmbol `u0` and Symbolic `p`. let - # Creates steady state network, unpack the parameter values. + # Creates steady state network. steady_state_network_2 = @reaction_network begin v / 10 + hill(X, v, K, n), ∅ → X d, X → ∅ end - @unpack v, K, n, d = steady_state_network_2 # Creates NonlinearProblem. - u0 = [:X => 1.0] - p = [v => 1.0 + rand(rng), K => 0.8, n => 3, d => 1.0] - nl_prob = NonlinearProblem(steady_state_network_2, u0, p) + u0 = rnd_u0(steady_state_network_2, rng; min = 1.0) + rnd_ps_Int64 = rnd_ps(steady_state_network_2, rng) + nlprob = NonlinearProblem(steady_state_network_2, u0, ps) # Solves it using standard algorithm and simulation based algorithm. - sol1 = solve(nl_prob; abstol=1e-12, reltol=1e-12).u - sol2 = solve(nl_prob, DynamicSS(Rosenbrock23()); abstol=1e-12, reltol=1e-12).u + sol1 = solve(nlprob; abstol=1e-12, reltol=1e-12) + sol2 = solve(nlprob, DynamicSS(Rosenbrock23()); abstol=1e-12, reltol=1e-12) # Computes NonlinearFunction (manually and automatically). - nfunc = NonlinearFunction(convert(NonlinearSystem, steady_state_network_2)) + nlprob_eval_1 = remake(nlprob; u0 = [:X => sol1[1]]) + nlprob_eval_2 = remake(nlprob; u0 = [:X => sol2[1]]) function nf_manual(u,p) - X = u[1] + X = u[:X] v, K, n, d = p return v/10 + v * X^n/(X^n + K^n) - d*X end # Tests solutions are correct. - @test isapprox(nfunc(sol1, last.(p))[1], 0.0; atol=1e-10) - @test isapprox(nfunc(sol2, last.(p))[1], 0.0; atol=1e-10) - @test isapprox(nf_manual(sol1, last.(p)), 0.0; atol=1e-10) - @test isapprox(nf_manual(sol2, last.(p)), 0.0; atol=1e-10) + @test nlprob_eval_1.f(nlprob_eval_1.u0, nlprob_eval_1.p)[1] ≈ 0.0 atol=1e-10 + @test nlprob_eval_2.f(nlprob_eval_2.u0, nlprob_eval_2.p)[1] ≈ 0.0 atol=1e-10 + @test nf_manual(sol1, last.(ps)) ≈ 0.0 atol=1e-10 + @test nf_manual(sol2, last.(ps)) ≈ 0.0 atol=1e-10 end # Checks for system with conservation laws. @@ -93,8 +92,8 @@ let sol1 = solve(nl_prob_1; abstol=1e-12, reltol=1e-12) sol2 = solve(nl_prob_2, DynamicSS(Rosenbrock23()); abstol=1e-12, reltol=1e-12) - # Checks output using NonlinearFunction. + # Checks output using the ODE's drift function nfunc = NonlinearFunction(convert(NonlinearSystem, steady_state_network_3)) - @test isapprox(nfunc([sol1[X], sol1[Y], sol1[Y2], sol1[XY2]], last.(p)), [0.0, 0.0, 0.0, 0.0]; atol=1e-10) - @test isapprox(nfunc([sol2[X], sol2[Y], sol2[Y2], sol2[XY2]], last.(p)), [0.0, 0.0, 0.0, 0.0]; atol=1e-10) -end + @test f_eval([:X => sol1[X], :Y => sol1[Y], :Y2 => sol1[Y2], :XY2 => sol1[XY2]], p, 0.0) ≈ [0.0, 0.0, 0.0, 0.0] atol=1e-10 + @test f_eval([:X => sol2[X], :Y => sol2[Y], :Y2 => sol2[Y2], :XY2 => sol2[XY2]], p, 0.0) ≈ [0.0, 0.0, 0.0, 0.0] atol=1e-10 +end \ No newline at end of file diff --git a/test/model_simulation/make_jacobian.jl b/test/model_simulation/make_jacobian.jl index 4ec9deb1f2..022e384fd2 100644 --- a/test/model_simulation/make_jacobian.jl +++ b/test/model_simulation/make_jacobian.jl @@ -4,6 +4,9 @@ using Catalyst, DiffEqBase, Random, Test using StableRNGs rng = StableRNG(12345) +# Fetch test functions. +include("../test_functions.jl") + ### Run Tests ### # Checks that the jacobian is correct for networks without parameters. @@ -13,8 +16,8 @@ let (3.0, 1.0), ∅ ↔ Y (5.0, 2.0), X + Y ↔ XY end - osys = convert(ODESystem, jacobian_network_1) - test_jac = ODEFunction(osys, jac = true).jac([1.0, 1.0, 1.0], [], 0.0) + + test_jac = jac_eval(jacobian_network_1, [:X => 1.0, :Y => 1.0, :XY => 1.0], [], 0.0) real_jac = [-6.0 -5.0 2.0; -5.0 -6.0 2.0; 5.0 5.0 -2.0] @test test_jac == real_jac end @@ -26,18 +29,20 @@ let (p2, 1.0), ∅ ↔ Y (p3 * X, 1.0), X + Y ↔ XY end + @unpack X, Y, XY, p1, p2, p3 = jacobian_network_2 + for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2], repeat in 1:10 - u = factor * rand(rng, 3) - p = factor * rand(rng, 3) - local osys = convert(ODESystem, jacobian_network_2) - local test_jac = ODEFunction(osys, jac = true).jac(u, p, 0.0) - local real_jac = [-1-2 * p[3] * u[2] * u[1] -p[3]*u[1]*u[1] 1.0; - -2*p[3]*u[2]*u[1] -1-p[3] * u[1] * u[1] 1; - 2*p[3]*u[2]*u[1] p[3]*u[1]*u[1] -1.0] - @test all(abs.(test_jac .- real_jac) .< 1e-9) + u = Dict(rnd_u0(jacobian_network_2, rng; factor)) + p = Dict(rnd_ps(jacobian_network_2, rng; factor)) + test_jac = jac_eval(jacobian_network_2, u, p, 0.0) + real_jac = [-1-2 * p[p3] * u[Y] * u[X] -p[p3]*u[X]*u[X] 1.0; + -2*p[p3]*u[Y]*u[X] -1-p[p3] * u[X] * u[X] 1; + 2*p[p3]*u[Y]*u[X] p[p3]*u[X]*u[X] -1.0] + @test test_jac ≈ real_jac end end +# Checks for a more complicated network, with non-unitary stoichiometries and a hill function. let jacobian_network_3 = @reaction_network begin k1, 2A → B @@ -48,16 +53,15 @@ let k6, 0 → 2B hill(A, k7, k8, 2), ∅ → B end + @unpack A, B, C, k1, k2, k3, k4, k5, k6, k7, k8 = jacobian_network_3 + for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2], repeat in 1:10 - u = factor * rand(rng, 3) - p = factor * rand(rng, 8) - A, B, C = u - k1, k2, k3, k4, k5, k6, k7, k8 = p - local osys = convert(ODESystem, jacobian_network_3) - local test_jac = ODEFunction(osys, jac = true).jac(u, p, 0.0) - local real_jac = [-2 * k1 * A-k3 * B 2 * k2-k3 * A k4+3 * k5 * C^2 / 2; - k1 * A - k3 * B+2 * k7 * k8^2 * A / (k8^2 + A^2)^2 -k2-k3 * A k4; - k3*B k3*A -k4-3 * k5 * C^2 / 2] - @test all(abs.(test_jac .- real_jac) .< 10e-9) + u = Dict(rnd_u0(jacobian_network_3, rng; factor)) + p = Dict(rnd_ps(jacobian_network_3, rng; factor)) + test_jac = jac_eval(jacobian_network_3, u, p, 0.0) + real_jac = [-2 * p[k1] * u[A]-p[k3] * u[B] 2 * p[k2]-p[k3] * u[A] p[k4]+3 * p[k5] * u[C]^2 / 2; + p[k1] * u[A] - p[k3] * u[B]+2 * p[k7] * p[k8]^2 * u[A] / (p[k8]^2 + u[A]^2)^2 -p[k2]-p[k3] * u[A] p[k4]; + p[k3]*u[B] p[k3]*u[A] -p[k4]-3 * p[k5] * u[C]^2 / 2] + @test test_jac ≈ real_jac end -end +end \ No newline at end of file diff --git a/test/model_simulation/simulate_ODEs.jl b/test/model_simulation/simulate_ODEs.jl index f56ec1a492..63411eacd9 100644 --- a/test/model_simulation/simulate_ODEs.jl +++ b/test/model_simulation/simulate_ODEs.jl @@ -8,8 +8,9 @@ using ModelingToolkit: get_unknowns, get_ps using StableRNGs rng = StableRNG(12345) -# Fetch test networks. -@time include("../test_networks.jl") +# Fetch test networks and functions. +include("../test_networks.jl") +include("../test_functions.jl") ### Compares to Known Solution ### @@ -18,13 +19,14 @@ let exponential_decay = @reaction_network begin d, X → ∅ end for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2] - u0 = factor * rand(rng, length(get_unknowns(exponential_decay))) - p = factor * rand(rng, length(get_ps(exponential_decay))) - prob = ODEProblem(exponential_decay, u0, (0.0, 100 / factor), p) - sol = solve(prob, Rosenbrock23(), saveat = range(0.0, 100 / factor, length = 101)) - analytic_sol = map(t -> u0[1] * exp(-p[1] * t), - range(0.0, 100 / factor, length = 101)) - @test all(abs.(first.(sol.u) .- analytic_sol) .< 0.1) + u0 = rnd_u0(exponential_decay, rng; factor) + t_stops = range(0.0, 100 / factor, length = 101) + p = rnd_ps(exponential_decay, rng; factor) + prob = ODEProblem(exponential_decay, u0, (0.0, t_stops[end]), p) + + sol = solve(prob, Rosenbrock23(), saveat = t_stops, abstol = 1e-14, reltol = 1e-14) + analytic_sol = [u0[1][2] * exp(-p[1][2] * t) for t in t_stops] + @test sol[:X] ≈ analytic_sol end end @@ -37,25 +39,24 @@ let (k7, k8), ∅ ↔ X8 end - for factor in [1e-1, 1e0, 1e1, 1e2, 1e3] - u0 = factor * rand(rng, length(get_unknowns(known_equilibrium))) - p = 0.01 .+ factor * rand(rng, length(get_ps(known_equilibrium))) - prob = ODEProblem(known_equilibrium, u0, (0.0, 1000000.0), p) - sol = solve(prob, Rosenbrock23()) - @test abs.(sol.u[end][1] / sol.u[end][2] - p[2] / p[1]) < 10000 * eps() - @test abs.(sol.u[end][3] * sol.u[end][4] / sol.u[end][5] - p[4] / p[3]) < - 10000 * eps() - @test abs.((sol.u[end][6]^2 / factorial(2)) / (sol.u[end][7]^3 / factorial(3)) - - p[6] / p[5]) < 1e-8 - @test abs.(sol.u[end][8] - p[7] / p[8]) < 10000 * eps() + for factor in [1e-1, 1e0, 1e1] + u0 = rnd_u0(known_equilibrium, rng; factor) + p = rnd_ps(known_equilibrium, rng; factor, min = 0.1) + prob = ODEProblem(known_equilibrium, u0, (0.0, 100000.0), p) + sol = solve(prob, Vern7(); abstol = 1e-12, reltol = 1e-12) + + @test sol[:X1][end] / sol[:X2][end] ≈ prob.ps[:k2] / prob.ps[:k1] atol=1e-8 + @test sol[:X3][end] * sol[:X4][end] / sol[:X5][end] ≈ prob.ps[:k4] / prob.ps[:k3] atol=1e-8 + @test (sol[:X6][end]^2 / factorial(2)) / (sol[:X7][end]^3 / factorial(3)) ≈ prob.ps[:k6] / prob.ps[:k5] atol=1e-8 + @test sol[:X8][end] ≈ prob.ps[:k7] / prob.ps[:k8] atol=1e-8 end end ### Compares to Known ODE Function ### -identical_networks_1 = Vector{Pair}() - let + identical_networks_1 = Vector{Pair}() + function real_functions_1(du, u, p, t) X1, X2, X3 = u p1, p2, p3, k1, k2, k3, k4, d1, d2, d3 = p @@ -103,26 +104,32 @@ let for (i, networks) in enumerate(identical_networks_1) for factor in [1e-2, 1e-1, 1e0, 1e1] - u0 = factor * rand(rng, length(get_unknowns(networks[1]))) - p = factor * rand(rng, length(get_ps(networks[1]))) - (i == 3) && (p = min.(round.(p) .+ 1, 10)) #If parameter in exponent, want to avoid possibility of (-small u)^(decimal). Also avoid large exponents. - prob1 = ODEProblem(networks[1], u0, (0.0, 10000.0), p) - sol1 = solve(prob1, Rosenbrock23(), saveat = 1.0) - prob2 = ODEProblem(networks[2], u0, (0.0, 10000.0), p) - sol2 = solve(prob2, Rosenbrock23(), saveat = 1.0) - @test all(abs.(hcat((sol1.u .- sol2.u)...)) .< 1e-7) + u0_1 = rnd_u0(networks[1], rng; factor) + p_1 = rnd_ps(networks[1], rng; factor) + (i == 3) && (p_1 =rnd_ps_Int64(networks[1], rng)) + u0_2 = last.(u0_1) + p_2 = last.(p_1) + + prob1 = ODEProblem(networks[1], u0_1, (0.0, 10000.0), p_1) + prob2 = ODEProblem(networks[2], u0_2, (0.0, 10000.0), p_2) + sol1 = solve(prob1, Rosenbrock23(); abstol = 1e-10, reltol = 1e-10, saveat = 1.0) + sol2 = solve(prob2, Rosenbrock23(); abstol = 1e-10, reltol = 1e-10, saveat = 1.0) + @test sol1.u ≈ sol2.u end end end ### Checks Simulations Don't Error ### - let for (i, network) in enumerate(reaction_networks_all) for factor in [1e-1, 1e0, 1e1] - u0 = factor * rand(rng, length(get_unknowns(network))) - p = factor * rand(rng, length(get_ps(network))) - in(i, [[11:20...]..., 34, 37, 42]) && (p = min.(round.(p) .+ 1, 10)) #If parameter in exponent, want to avoid possibility of (-small u)^(decimal). Also avoid large exponents. + u0 = rnd_u0(network, rng; factor) + #If parameter in exponent, want to avoid possibility of (-small u)^(decimal). Also avoid large exponents. + if in(i, [[11:20...]..., 34, 37, 42]) + p = rnd_ps(network, rng) + else + p = rnd_ps(network, rng; factor) + end prob = ODEProblem(network, u0, (0.0, 1.0), p) @test SciMLBase.successful_retcode(solve(prob, Rosenbrock23())) end @@ -135,28 +142,32 @@ end let no_param_network = @reaction_network begin (1.5, 2), ∅ ↔ X end for factor in [1e0, 1e1, 1e2] - u0 = factor * rand(rng, length(get_unknowns(no_param_network))) + u0 = rnd_u0(no_param_network, rng; factor) prob = ODEProblem(no_param_network, u0, (0.0, 1000.0)) sol = solve(prob, Rosenbrock23()) - @test abs.(sol.u[end][1] - 1.5 / 2) < 1e-8 + @test sol[:X][end] ≈ 1.5 / 2.0 end end # Test solving with floating point stoichiometry. let + # Prepare model. function oderhs(du, u, p, t) du[1] = -2.5 * p[1] * u[1]^2.5 du[2] = 3 * p[1] * u[1]^2.5 nothing end - rn = @reaction_network begin k, 2.5 * A --> 3 * B end - u0 = [:A => 1.0, :B => 0.0] + rn = @reaction_network begin + k, 2.5 * A --> 3 * B + end + u = rnd_u0(rn, rng) tspan = (0.0, 1.0) p = [:k => 1.0] - oprob = ODEProblem(rn, u0, tspan, p; combinatoric_ratelaws = false) - du1 = du2 = zeros(2) - u = rand(rng, 2) - oprob.f(du1, u, [1.0], 0.0) - oderhs(du2, u, [1.0], 0.0) - @test isapprox(du1, du2, rtol = 1e3 * eps()) -end + + # Check equivalence. + du1 = du2 = zeros(2) + oprob = ODEProblem(rn, u, tspan, p; combinatoric_ratelaws = false) + oprob.f(du1, oprob.u0, oprob.p, 90.0) + oderhs(du2, last.(u), last.(p), 0.0) + @test du1 ≈ du2 +end \ No newline at end of file diff --git a/test/model_simulation/simulate_SDEs.jl b/test/model_simulation/simulate_SDEs.jl index 704e2de378..df0e6137b1 100644 --- a/test/model_simulation/simulate_SDEs.jl +++ b/test/model_simulation/simulate_SDEs.jl @@ -10,6 +10,7 @@ rng = StableRNG(12345) # Fetch test networks. include("../test_networks.jl") +include("../test_functions.jl") ### Compares to Manually Calcualted Function ### @@ -98,23 +99,21 @@ let for (i, networks) in enumerate(identical_networks) for factor in [1e-1, 1e0, 1e1], repeat in 1:3 - u0 = 100.0 .+ factor * rand(rng, length(unknowns(networks[1]))) - p = 0.01 .+ factor * rand(rng, length(parameters(networks[1]))) - (i == 2) && (u0[1] += 1000.0) - (i == 3) ? (p[2:2:6] .*= 1000.0; u0 .+= 1000) : (p[1] += 500.0) - prob1 = SDEProblem(networks[1], u0, (0.0, 100.0), p) - prob2 = SDEProblem(networks[2][1], networks[2][2], u0, (0.0, 100.0), p, - noise_rate_prototype = networks[2][3]) - du1 = similar(u0) - du2 = similar(u0) - prob1.f.f(du1, u0, p, 0.0) - prob2.f.f(du2, u0, p, 0.0) - @test all(isapprox.(du1, du2)) - g1 = zeros(numspecies(networks[1]), numreactions(networks[1])) - g2 = copy(g1) - prob1.f.g(g1, u0, p, 0.0) - prob2.f.g(g2, u0, p, 0.0) - @test all(isapprox.(g1, g2)) + # Set input values. + u0_1 = rnd_u0(networks[1], rng; factor, min = 100.0) + ps_1 = rnd_ps(networks[1], rng; factor, min = 0.01) + u0_2 = last.(u0_1) + ps_2 = last.(ps_1) + + # Check drift functions. + dt = zeros(length(unknowns(networks[1]))) + networks[2][1](dt, u0_2, ps_2, 0.0) + @test dt ≈ f_eval(networks[1], u0_1, ps_1, 0.0) + + # Check diffusion functions. + duW = zeros(length(unknowns(networks[1])), length(reactions(networks[1]))) + networks[2][2](duW, u0_2, ps_2, 0.0) + @test duW ≈ g_eval(networks[1], u0_1, ps_1, 0.0) end end end @@ -123,11 +122,11 @@ end # Tries to create a large number of problem, ensuring there are no errors (cannot solve as solution likely to go into negatives). let - for reaction_network in reaction_networks_all + for network in reaction_networks_all for factor in [1e-2, 1e-1, 1e0, 1e1] - u0 = factor * rand(rng, length(states(reaction_network))) - p = factor * rand(rng, length(parameters(reaction_network))) - prob = SDEProblem(reaction_network, u0, (0.0, 1.0), p) + u0 = rnd_u0(network, rng; factor) + ps = rnd_ps(network, rng) + prob = SDEProblem(network, u0, (0.0, 1.0), ps) end end end @@ -144,7 +143,7 @@ let sol_1_1 = solve(SDEProblem(noise_scaling_network_1, u0, (0.0, 1000.0), [:k1 => 2.0, :k2 => 0.66, :η1 => 2.0, :η2 => 2.0]), ImplicitEM(); saveat=1.0) sol_1_2 = solve(SDEProblem(noise_scaling_network_1, u0, (0.0, 1000.0), [:k1 => 2.0, :k2 => 0.66, :η1 => 2.0, :η2 => 0.2]), ImplicitEM(); saveat=1.0) sol_1_3 = solve(SDEProblem(noise_scaling_network_1, u0, (0.0, 1000.0), [:k1 => 2.0, :k2 => 0.66, :η1 => 0.2, :η2 => 0.2]), ImplicitEM(); saveat=1.0) - @test var(sol_1_1[1,:]) > var(sol_1_2[1,:]) > var(sol_1_3[1,:]) + @test var(sol_1_1[:X1]) > var(sol_1_2[:X1]) > var(sol_1_3[:X1]) noise_scaling_network_2 = @reaction_network begin @parameters η[1:2] @@ -154,7 +153,7 @@ let sol_2_1 = solve(SDEProblem(noise_scaling_network_2, u0, (0.0, 1000.0), [k1 => 2.0, k2 => 0.66, η[1] => 2.0, η[2] => 2.0]), ImplicitEM(); saveat=1.0) sol_2_2 = solve(SDEProblem(noise_scaling_network_2, u0, (0.0, 1000.0), [k1 => 2.0, k2 => 0.66, η[1] => 2.0, η[2] => 0.2]), ImplicitEM(); saveat=1.0) sol_2_3 = solve(SDEProblem(noise_scaling_network_2, u0, (0.0, 1000.0), [k1 => 2.0, k2 => 0.66, η[1] => 0.2, η[2] => 0.2]), ImplicitEM(); saveat=1.0) - @test var(sol_2_1[1,:]) > var(sol_2_2[1,:]) > var(sol_2_3[1,:]) + @test var(sol_2_1[:X1]) > var(sol_2_2[:X1]) > var(sol_2_3[:X1]) end # Tests using default values for noise scaling. @@ -231,7 +230,7 @@ let u0 = [:X1 => 1000.0, :X2 => 1000.0, :X3 => 1000.0, :X4 => 1000.0, :X5 => 1000.0, :N1 => 3.0, :N3 => 0.33] ps = [:p => 1000.0, :d => 1.0, :η1 => 1.0, :η2 => 1.4, :η3 => 0.33, :η4 => 4.0] sol = solve(SDEProblem(noise_scaling_network, u0, (0.0, 1000.0), ps), ImplicitEM(); saveat=1.0, adaptive=false, dt=0.1) - @test var(sol[:X1]) > var(sol[:X2]) > var(sol[:X3]) > var(sol[:X4]) > var(sol[:X5]) + @test_broken var(sol[:X1]) > var(sol[:X2]) > var(sol[:X3]) > var(sol[:X4]) > var(sol[:X5]) # Likely need this fix: /~https://github.com/SciML/ModelingToolkit.jl/issues/2556 end # Tests the `remake_noise_scaling` function. @@ -292,11 +291,9 @@ end let no_param_network = @reaction_network begin (1.2, 5), X1 ↔ X2 end for factor in [1e3, 1e4] - u0 = factor * (1.0 .+ rand(rng, length(unknowns(no_param_network)))) + u0 = rnd_u0(no_param_network, rng; factor) prob = SDEProblem(no_param_network, u0, (0.0, 1000.0)) sol = solve(prob, ImplicitEM()) - vals1 = getindex.(sol.u[1:end], 1) - vals2 = getindex.(sol.u[1:end], 2) - @test mean(vals1) > mean(vals2) + @test mean(sol[:X1]) > mean(sol[:X2]) end end diff --git a/test/model_simulation/u0_n_parameter_inputs.jl b/test/model_simulation/u0_n_parameter_inputs.jl index 6b3a0adf03..e2e5a92e3d 100644 --- a/test/model_simulation/u0_n_parameter_inputs.jl +++ b/test/model_simulation/u0_n_parameter_inputs.jl @@ -24,10 +24,10 @@ let @species X1(t) X2(t) X3(t) X4(t) X5(t) X6(t) X(t) for factor = [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] - u0_1 = factor*rand(rng,length(get_unknowns(test_network))) + u0_1 = factor*rand(rng,length(unknowns(test_network))) u0_2 = [X1=>u0_1[1], X2=>u0_1[2], X3=>u0_1[3], X4=>u0_1[4], X5=>u0_1[5]] u0_3 = [X2=>u0_1[2], X5=>u0_1[5], X4=>u0_1[4], X3=>u0_1[3], X1=>u0_1[1]] - p_1 = 0.01 .+ factor*rand(rng,length(get_ps(test_network))) + p_1 = 0.01 .+ factor*rand(rng,length(parameters(test_network))) p_2 = [p1=>p_1[1], p2=>p_1[2], p3=>p_1[3], k1=>p_1[4], k2=>p_1[5], k3=>p_1[6], v1=>p_1[7], K1=>p_1[8], d1=>p_1[9], d2=>p_1[10], d3=>p_1[11], d4=>p_1[12], d5=>p_1[13]] @@ -36,35 +36,35 @@ let k1=>p_1[4]] sols = [] - push!(sols,solve(ODEProblem(test_osys,u0_1,(0.,10.),p_1),Rosenbrock23())) - push!(sols,solve(ODEProblem(test_osys,u0_1,(0.,10.),p_2),Rosenbrock23())) - push!(sols,solve(ODEProblem(test_osys,u0_1,(0.,10.),p_3),Rosenbrock23())) - push!(sols,solve(ODEProblem(test_osys,u0_2,(0.,10.),p_1),Rosenbrock23())) - push!(sols,solve(ODEProblem(test_osys,u0_2,(0.,10.),p_2),Rosenbrock23())) - push!(sols,solve(ODEProblem(test_osys,u0_2,(0.,10.),p_3),Rosenbrock23())) - push!(sols,solve(ODEProblem(test_osys,u0_3,(0.,10.),p_1),Rosenbrock23())) - push!(sols,solve(ODEProblem(test_osys,u0_3,(0.,10.),p_2),Rosenbrock23())) - push!(sols,solve(ODEProblem(test_osys,u0_3,(0.,10.),p_3),Rosenbrock23())) + push!(sols, solve(ODEProblem(test_osys,u0_1, (0.,10.), p_1), Rosenbrock23())) + push!(sols, solve(ODEProblem(test_osys,u0_1, (0.,10.), p_2), Rosenbrock23())) + push!(sols, solve(ODEProblem(test_osys,u0_1, (0.,10.), p_3), Rosenbrock23())) + push!(sols, solve(ODEProblem(test_osys,u0_2, (0.,10.), p_1), Rosenbrock23())) + push!(sols, solve(ODEProblem(test_osys,u0_2, (0.,10.), p_2), Rosenbrock23())) + push!(sols, solve(ODEProblem(test_osys,u0_2, (0.,10.), p_3), Rosenbrock23())) + push!(sols, solve(ODEProblem(test_osys,u0_3, (0.,10.), p_1), Rosenbrock23())) + push!(sols, solve(ODEProblem(test_osys,u0_3, (0.,10.), p_2), Rosenbrock23())) + push!(sols, solve(ODEProblem(test_osys,u0_3, (0.,10.), p_3), Rosenbrock23())) ends = map(sol -> sol.u[end],sols) for i in 1:length(u0_1) - @test abs(maximum(getindex.(ends,1))-minimum(first.(getindex.(ends,1)))) < 1e-5 + @test abs(maximum(getindex.(ends,1)) - minimum(first.(getindex.(ends,1)))) < 1e-5 end - u0_1 = rand(rng,1:Int64(factor*100),length(get_unknowns(test_network))) + u0_1 = rand(rng, 1:Int64(factor*100), length(unknowns(test_network))) u0_2 = [X1=>u0_1[1], X2=>u0_1[2], X3=>u0_1[3], X4=>u0_1[4], X5=>u0_1[5]] u0_3 = [X2=>u0_1[2], X5=>u0_1[5], X4=>u0_1[4], X3=>u0_1[3], X1=>u0_1[1]] discrete_probs = [] - push!(discrete_probs,DiscreteProblem(test_network,u0_1,(0.,1.),p_1)) - push!(discrete_probs,DiscreteProblem(test_network,u0_1,(0.,1.),p_2)) - push!(discrete_probs,DiscreteProblem(test_network,u0_1,(0.,1.),p_3)) - push!(discrete_probs,DiscreteProblem(test_network,u0_2,(0.,1.),p_1)) - push!(discrete_probs,DiscreteProblem(test_network,u0_2,(0.,1.),p_2)) - push!(discrete_probs,DiscreteProblem(test_network,u0_2,(0.,1.),p_3)) - push!(discrete_probs,DiscreteProblem(test_network,u0_3,(0.,1.),p_1)) - push!(discrete_probs,DiscreteProblem(test_network,u0_3,(0.,1.),p_2)) - push!(discrete_probs,DiscreteProblem(test_network,u0_3,(0.,1.),p_3)) + push!(discrete_probs, DiscreteProblem(test_network, u0_1, (0.,1.), p_1)) + push!(discrete_probs, DiscreteProblem(test_network, u0_1, (0.,1.), p_2)) + push!(discrete_probs, DiscreteProblem(test_network, u0_1, (0.,1.), p_3)) + push!(discrete_probs, DiscreteProblem(test_network, u0_2, (0.,1.), p_1)) + push!(discrete_probs, DiscreteProblem(test_network, u0_2, (0.,1.), p_2)) + push!(discrete_probs, DiscreteProblem(test_network, u0_2, (0.,1.), p_3)) + push!(discrete_probs, DiscreteProblem(test_network, u0_3, (0.,1.), p_1)) + push!(discrete_probs, DiscreteProblem(test_network, u0_3, (0.,1.), p_2)) + push!(discrete_probs, DiscreteProblem(test_network, u0_3, (0.,1.), p_3)) for i in 2:9 @test discrete_probs[1].p == discrete_probs[i].p diff --git a/test/programmatic_model_creation/component_based_model_creation.jl b/test/programmatic_model_creation/component_based_model_creation.jl index cf50636a8a..f1643ced77 100644 --- a/test/programmatic_model_creation/component_based_model_creation.jl +++ b/test/programmatic_model_creation/component_based_model_creation.jl @@ -95,8 +95,8 @@ let nlprob = NonlinearProblem(sys2, u₀, pvals) sol = solve(nlprob, NLSolveJL(), abstol = 1e-9) @test sol[sys₁.P] ≈ sol[sys₂.P] ≈ sol[sys₃.P] - @test sol[sys₁.m]≈sol[sys₂.m] atol=1e-7 - @test sol[sys₁.m]≈sol[sys₃.m] atol=1e-7 + @test sol[sys₁.m] ≈ sol[sys₂.m] atol=1e-7 + @test sol[sys₁.m] ≈ sol[sys₃.m] atol=1e-7 @test sol[sys₁.R] ≈ sol[sys₂.R] ≈ sol[sys₃.R] # Flattening. @@ -107,8 +107,8 @@ let nlprob = NonlinearProblem(sys2, u₀, pvals) sol = solve(nlprob, NLSolveJL(), abstol = 1e-9) @test sol[sys₁.P] ≈ sol[sys₂.P] ≈ sol[sys₃.P] - @test sol[sys₁.m]≈sol[sys₂.m] atol=1e-7 - @test sol[sys₁.m]≈sol[sys₃.m] atol=1e-7 + @test sol[sys₁.m] ≈ sol[sys₂.m] atol=1e-7 + @test sol[sys₁.m] ≈ sol[sys₃.m] atol=1e-7 @test sol[sys₁.R] ≈ sol[sys₂.R] ≈ sol[sys₃.R] # Test constraints. @@ -124,8 +124,8 @@ let nlprob = NonlinearProblem(sys2, u₀, pvals) sol = solve(nlprob, NLSolveJL(), abstol = 1e-9) @test sol[sys₁.P] ≈ sol[sys₂.P] ≈ sol[sys₃.P] - @test sol[sys₁.m]≈sol[sys₂.m] atol=1e-7 - @test sol[sys₁.m]≈sol[sys₃.m] atol=1e-7 + @test sol[sys₁.m] ≈ sol[sys₂.m] atol=1e-7 + @test sol[sys₁.m] ≈ sol[sys₃.m] atol=1e-7 @test sol[sys₁.R] ≈ sol[sys₂.R] ≈ sol[sys₃.R] # Test constraint system variables are accessible through Base.getproperty @@ -240,8 +240,8 @@ let nlprob = NonlinearProblem(sys2, u₀, pvals) sol = solve(nlprob, NLSolveJL(), abstol = 1e-9) @test sol[sys₁.P] ≈ sol[sys₂.P] ≈ sol[sys₃.P] - @test sol[sys₁.m]≈sol[sys₂.m] atol=1e-7 - @test sol[sys₁.m]≈sol[sys₃.m] atol=1e-7 + @test sol[sys₁.m] ≈ sol[sys₂.m] atol=1e-7 + @test sol[sys₁.m] ≈ sol[sys₃.m] atol=1e-7 @test sol[sys₁.R] ≈ sol[sys₂.R] ≈ sol[sys₃.R] end diff --git a/test/reactionsystem_structure/reactions.jl b/test/reactionsystem_structure/reactions.jl index 0746e3dfbe..24c3dcf636 100644 --- a/test/reactionsystem_structure/reactions.jl +++ b/test/reactionsystem_structure/reactions.jl @@ -1,5 +1,5 @@ ### Preparations ### - +hi_world() # Fetch packages. using Catalyst, Test @@ -85,6 +85,6 @@ let r1 = Reaction(k, [X], [X2], [2], [1]) r2 = Reaction(k, [X], [X2], [2], [1]; metadata=metadata) - @test isequal(Catalyst.get_noise_scaling(r1), 1.0) + @test_throws Exception Catalyst.get_noise_scaling(r1) @test isequal(Catalyst.get_noise_scaling(r2), η) end \ No newline at end of file diff --git a/test/reactionsystem_structure/reactionsystem.jl b/test/reactionsystem_structure/reactionsystem.jl index bafb1f05bc..6ba1bb554e 100644 --- a/test/reactionsystem_structure/reactionsystem.jl +++ b/test/reactionsystem_structure/reactionsystem.jl @@ -1,6 +1,6 @@ ### Fetch Packages and Set Global Variables ### -# Fetch pakcages. +# Fetch packages. using Catalyst, LinearAlgebra, JumpProcesses, Test, OrdinaryDiffEq, StochasticDiffEq t = default_t() const MT = ModelingToolkit @@ -126,15 +126,15 @@ let ModelingToolkit.get_defaults(nlsys) == defs - u0map = [A => 5.0] # was 0.5 - pmap = [k[1] => 5.0] # was 1. + u0map = [A => 5.0] + pmap = [k[1] => 5.0] prob = ODEProblem(rs, u0map, (0, 10.0), pmap) - @test prob.p[1] == 5.0 + @test prob.ps[k[1]] == 5.0 @test prob.u0[1] == 5.0 u0 = [10.0, 11.0, 12.0, 13.0] ps = [float(x) for x in 100:119] prob = ODEProblem(rs, u0, (0, 10.0), ps) - @test prob.p == ps + @test [prob.ps[k[i]] for i in 1:20] == ps @test prob.u0 == u0 end @@ -143,22 +143,26 @@ end # Test by evaluating drift and diffusion terms. let - p = rand(rng, length(k)) - u = rand(rng, length(k)) - du = oderhs(u, p, 0.0) - G = sdenoise(u, p, 0.0) + u = rnd_u0(rs, rng) + p = rnd_ps(rs, rng) + du = oderhs(last.(u), last.(p), 0.0) + G = sdenoise(last.(u), last.(p), 0.0) sdesys = convert(SDESystem, rs) sf = SDEFunction{false}(sdesys, unknowns(rs), parameters(rs)) - du2 = sf.f(u, p, 0.0) - @test norm(du - du2) < 100 * eps() - G2 = sf.g(u, p, 0.0) - @test norm(G - G2) < 100 * eps() + sprob = SDEProblem(rs, u, (0.0, 0.0), ps) + du2 = sf.f(sprob.u0, sprob.p, 0.0) + + du2 = sf.f(sprob.u0, sprob.p, 0.0) + @test_broken norm(du - du2) < 100 * eps() + G2 = sf.g(sprob.u0, sprob.p, 0.0) + @test_broken norm(G - G2) < 100 * eps() # Test conversion to NonlinearSystem. ns = convert(NonlinearSystem, rs) + nlprob = NonlinearProblem(rs, u, ps) fnl = eval(generate_function(ns)[2]) dunl = similar(du) - fnl(dunl, u, p) + fnl(dunl, nlprob.u0, nlprob.p) @test norm(du - dunl) < 100 * eps() end @@ -193,7 +197,7 @@ let midxs = 1:14 cidxs = 15:18 vidxs = 19:20 - @test all(map(i -> typeof(equations(js)[i]) <: JumpProcesses.MassActionJump, midxs)) + @test_broken all(map(i -> typeof(equations(js)[i]) <: JumpProcesses.MassActionJump, midxs)) @test all(map(i -> typeof(equations(js)[i]) <: JumpProcesses.ConstantRateJump, cidxs)) @test all(map(i -> typeof(equations(js)[i]) <: JumpProcesses.VariableRateJump, vidxs)) @@ -239,13 +243,13 @@ let maj = MassActionJump(symmaj.param_mapper(pars), symmaj.reactant_stoch, symmaj.net_stoch, symmaj.param_mapper, scale_rates = false) for i in midxs - @test abs(jumps[i].scaled_rates - maj.scaled_rates[i]) < 100 * eps() - @test jumps[i].reactant_stoch == maj.reactant_stoch[i] - @test jumps[i].net_stoch == maj.net_stoch[i] + @test_broken abs(jumps[i].scaled_rates - maj.scaled_rates[i]) < 100 * eps() + @test_broken jumps[i].reactant_stoch == maj.reactant_stoch[i] + @test_broken jumps[i].net_stoch == maj.net_stoch[i] end for i in cidxs crj = ModelingToolkit.assemble_crj(js, equations(js)[i], unknownoid) - @test isapprox(crj.rate(u0, p, ttt), jumps[i].rate(u0, p, ttt)) + @test_broken isapprox(crj.rate(u0, p, ttt), jumps[i].rate(u0, p, ttt)) fake_integrator1 = (u = zeros(6), p = p, t = 0.0) fake_integrator2 = deepcopy(fake_integrator1) crj.affect!(fake_integrator1) @@ -254,7 +258,7 @@ let end for i in vidxs crj = ModelingToolkit.assemble_vrj(js, equations(js)[i], unknownoid) - @test isapprox(crj.rate(u0, p, ttt), jumps[i].rate(u0, p, ttt)) + @test_broken isapprox(crj.rate(u0, p, ttt), jumps[i].rate(u0, p, ttt)) fake_integrator1 = (u = zeros(6), p = p, t = 0.0) fake_integrator2 = deepcopy(fake_integrator1) crj.affect!(fake_integrator1) @@ -457,12 +461,12 @@ let du1 = zeros(4) du2 = zeros(4) sprob = SDEProblem(ssys, u0map, tspan, pmap; check_length = false) - sprob.f(du1, u0, p, 1.0) + sprob.f(du1, sprob.u0, sprob.p, 1.0) fs!(du2, u0, p, 1.0) @test isapprox(du1, du2) dg1 = zeros(4, 4) dg2 = zeros(4, 4) - sprob.g(dg1, u0, p, 1.0) + sprob.g(dg1, sprob.u0, sprob.p, 1.0) gs!(dg2, u0, p, t) @test isapprox(dg1, dg2) @@ -514,7 +518,7 @@ let umean += sol(10.0, idxs = [B1, B2, B3, C]) end umean /= Nsims - @test isapprox(umean[1], umean[2]; rtol = 1e-2) + @test_broken isapprox(umean[1], umean[2]; rtol = 1e-2) # `idxs` keywordargument currently broken for `SDEProblem`s (/~https://github.com/SciML/SciMLBase.jl/issues/581). @test isapprox(umean[1], umean[3]; rtol = 1e-2) @test umean[4] == 10 end diff --git a/test/runtests.jl b/test/runtests.jl index c9cfa47ae9..df5bc08e91 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,11 +1,18 @@ ### Fetch the require packages ### using SafeTestsets +println("Started") +function hi_world() + println("Hello WOrld") +end + ### Run the tests ### @time begin ### Tests the properties of ReactionSystems. ### @time @safetestset "Reactions" begin include("reactionsystem_structure/reactions.jl") end + + exit() @time @safetestset "ReactionSystem" begin include("reactionsystem_structure/reactionsystem.jl") end @time @safetestset "Higher Order Reactions" begin include("reactionsystem_structure/higher_order_reactions.jl") end diff --git a/test/test_functions.jl b/test/test_functions.jl new file mode 100644 index 0000000000..46436cf35b --- /dev/null +++ b/test/test_functions.jl @@ -0,0 +1,44 @@ +# Various functions that are useful for running the tests, and used across several test sets. + + +# Initial Condition/Parameter Generators ### + +# Generates a random initial condition (in the form of a map). Each value is a Float64. +function rnd_u0(sys, rng::StableRNG; factor = 1.0, min = 0.0) + return [u => min + factor * rand(rng) for u in unknowns(sys)] +end + +# Generates a random initial condition (in the form of a map). Each value is a Int64. +function rnd_u0_Int64(sys, rng::StableRNG; n = 5, min = 0) + return [u => min + factor * rand(rng, 1:n) for u in unknowns(sys)] +end + +# Generates a random parameter set (in the form of a map). Each value is a Float64. +function rnd_ps(sys, rng::StableRNG; factor = 1.0, min = 0.0) + return [p => min + factor * rand(rng) for p in parameters(sys)] +end + +# Generates a random parameter set (in the form of a map). Each value is a Float64. +function rnd_ps_Int64(sys, rng::StableRNG; n = 5, min = 0) + return [p => min + factor * rand(rng, 1:n) for p in parameters(sys)] +end + +### System function evaluation ### + +# Evaluates the the drift function of the ODE corresponding to a reaction network. +function f_eval(rs::ReactionSystem, u, p, τ) + prob = ODEProblem(rs, u, (0.0, 0.0), p) + return prob.f(prob.u0, prob.p, τ) +end + +# Evaluates the the Jacobian of the drift function of the ODE corresponding to a reaction network. +function jac_eval(rs::ReactionSystem, u, p, t) + prob = ODEProblem(rs, u, (0.0, 0.0), p; jac = true) + return prob.f.jac(prob.u0, prob.p, t) +end + +# Evaluates the the diffusion function of the SDE corresponding to a reaction network. +function g_eval(rs::ReactionSystem, u, p, t) + prob = SDEProblem(rs, u, (0.0, 0.0), p) + return prob.g(prob.u0, prob.p, t) +end From e06d903a5224337c89cb7b1aa6c9f6e63d45b60d Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 19 Mar 2024 20:50:50 -0400 Subject: [PATCH 06/51] test up --- test/reactionsystem_structure/reactionsystem.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/reactionsystem_structure/reactionsystem.jl b/test/reactionsystem_structure/reactionsystem.jl index 6ba1bb554e..10c3a17c4c 100644 --- a/test/reactionsystem_structure/reactionsystem.jl +++ b/test/reactionsystem_structure/reactionsystem.jl @@ -9,6 +9,9 @@ const MT = ModelingToolkit using StableRNGs rng = StableRNG(12345) +# Fetch test networks and functions. +include("../test_functions.jl") + # Create test network. @parameters k[1:20] @species A(t) B(t) C(t) D(t) From 2de81e0ad2ca0a45b2bb34ec393240af670f367f Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 19 Mar 2024 20:51:57 -0400 Subject: [PATCH 07/51] up --- test/reactionsystem_structure/reactionsystem.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/reactionsystem_structure/reactionsystem.jl b/test/reactionsystem_structure/reactionsystem.jl index 10c3a17c4c..ac6992889e 100644 --- a/test/reactionsystem_structure/reactionsystem.jl +++ b/test/reactionsystem_structure/reactionsystem.jl @@ -152,17 +152,17 @@ let G = sdenoise(last.(u), last.(p), 0.0) sdesys = convert(SDESystem, rs) sf = SDEFunction{false}(sdesys, unknowns(rs), parameters(rs)) - sprob = SDEProblem(rs, u, (0.0, 0.0), ps) + sprob = SDEProblem(rs, u, (0.0, 0.0), p) du2 = sf.f(sprob.u0, sprob.p, 0.0) du2 = sf.f(sprob.u0, sprob.p, 0.0) - @test_broken norm(du - du2) < 100 * eps() + @test norm(du - du2) < 100 * eps() G2 = sf.g(sprob.u0, sprob.p, 0.0) - @test_broken norm(G - G2) < 100 * eps() + @test norm(G - G2) < 100 * eps() # Test conversion to NonlinearSystem. ns = convert(NonlinearSystem, rs) - nlprob = NonlinearProblem(rs, u, ps) + nlprob = NonlinearProblem(rs, u, p) fnl = eval(generate_function(ns)[2]) dunl = similar(du) fnl(dunl, nlprob.u0, nlprob.p) From a187b317382b9a9ac53563ea6a15d9a6563a803e Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 19 Mar 2024 20:53:56 -0400 Subject: [PATCH 08/51] revert broken tests to normal tests --- test/reactionsystem_structure/reactionsystem.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/reactionsystem_structure/reactionsystem.jl b/test/reactionsystem_structure/reactionsystem.jl index ac6992889e..f6945cd9d3 100644 --- a/test/reactionsystem_structure/reactionsystem.jl +++ b/test/reactionsystem_structure/reactionsystem.jl @@ -200,7 +200,7 @@ let midxs = 1:14 cidxs = 15:18 vidxs = 19:20 - @test_broken all(map(i -> typeof(equations(js)[i]) <: JumpProcesses.MassActionJump, midxs)) + @test all(map(i -> typeof(equations(js)[i]) <: JumpProcesses.MassActionJump, midxs)) @test all(map(i -> typeof(equations(js)[i]) <: JumpProcesses.ConstantRateJump, cidxs)) @test all(map(i -> typeof(equations(js)[i]) <: JumpProcesses.VariableRateJump, vidxs)) @@ -247,8 +247,8 @@ let symmaj.param_mapper, scale_rates = false) for i in midxs @test_broken abs(jumps[i].scaled_rates - maj.scaled_rates[i]) < 100 * eps() - @test_broken jumps[i].reactant_stoch == maj.reactant_stoch[i] - @test_broken jumps[i].net_stoch == maj.net_stoch[i] + @test jumps[i].reactant_stoch == maj.reactant_stoch[i] + @test jumps[i].net_stoch == maj.net_stoch[i] end for i in cidxs crj = ModelingToolkit.assemble_crj(js, equations(js)[i], unknownoid) From c1a60ff4ceef7987861d5e06e6cff1270932e248 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 22 Mar 2024 16:55:18 -0400 Subject: [PATCH 09/51] update --- Project.toml | 2 +- test/extensions/homotopy_continuation.jl | 12 +++++++----- test/miscellaneous_tests/api.jl | 2 ++ test/miscellaneous_tests/nonlinear_solve.jl | 7 ++++++- test/miscellaneous_tests/symbolic_stoichiometry.jl | 8 ++++++-- test/miscellaneous_tests/units.jl | 8 ++++++++ .../higher_order_reactions.jl | 5 ++++- test/reactionsystem_structure/reactions.jl | 2 +- test/reactionsystem_structure/reactionsystem.jl | 6 ++++-- test/runtests.jl | 7 ------- .../lattice_reaction_systems_ODEs.jl | 4 +++- test/spatial_reaction_systems/simulate_PDEs.jl | 1 + test/test_functions.jl | 12 ++++++------ test/visualization/latexify.jl | 1 + 14 files changed, 50 insertions(+), 27 deletions(-) diff --git a/Project.toml b/Project.toml index c819c6690a..994631aa99 100644 --- a/Project.toml +++ b/Project.toml @@ -44,7 +44,7 @@ HomotopyContinuation = "2.9" LaTeXStrings = "1.3.0" Latexify = "0.14, 0.15, 0.16" MacroTools = "0.5.5" -ModelingToolkit = "9.5" +ModelingToolkit = "9.6" Parameters = "0.12" Reexport = "0.2, 1.0" Requires = "1.0" diff --git a/test/extensions/homotopy_continuation.jl b/test/extensions/homotopy_continuation.jl index 56af70177d..3005b14975 100644 --- a/test/extensions/homotopy_continuation.jl +++ b/test/extensions/homotopy_continuation.jl @@ -2,6 +2,9 @@ using Catalyst, Test import HomotopyContinuation +# Fetch test functions. +include("../test_functions.jl") + ### Run Tests ### # Tests for network without conservation laws. @@ -9,7 +12,7 @@ import HomotopyContinuation # Tests for Symbolics initial condition input. # Tests for different types (Symbol/Symbolics) for parameters and initial conditions. # Tests that attempts to find steady states of system with conservation laws, while u0 is not provided, gives an error. -let +@test_broken let # Requires /~https://github.com/SciML/ModelingToolkit.jl/issues/2566 to be fixed rs = @reaction_network begin (k1,k2), X1 <--> X2 (k3,k4), 2X2 + X3 <--> X2_2X3 @@ -27,7 +30,7 @@ end # Tests for network with multiple steady state. # Tests for Symbol parameter input. -# Tests tha passing kwargs to HC.solve does not error. +# Tests that passing kwargs to HC.solve does not error. let wilhelm_2009_model = @reaction_network begin k1, Y --> 2X @@ -50,7 +53,7 @@ end # Tests correctness in presence of default values. # Tests where some default values are overwritten with other values. # Tests where input ps/u0 are tuples with mixed types. -let +@test_broken let # Requires /~https://github.com/SciML/ModelingToolkit.jl/issues/2566 to be fixed rs_1 = @reaction_network begin @parameters kX1=1.0 kX2=2.0 kY1=12345.0 @species X1(t)=0.1 X2(t)=0.2 Y1(t)=12345.0 @@ -90,10 +93,9 @@ let ps = [:v => 5.0, :K => 2.5, :n => 3, :d => 1.0] sss = hc_steady_states(rs, ps; filter_negative=false, show_progress=false) - f = ODEFunction(convert(ODESystem, rs)) @test length(sss) == 4 for ss in sss - @test isapprox(f(sss[1], last.(ps), 0.0)[1], 0.0; atol=1e-12) + @test isapprox(f_eval(rs,sss[1], last.(ps), 0.0)[1], 0.0; atol=1e-12) end @test_throws Exception hc_steady_states(rs, [:v => 5.0, :K => 2.5, :n => 2.7, :d => 1.0]; show_progress=false) diff --git a/test/miscellaneous_tests/api.jl b/test/miscellaneous_tests/api.jl index 64a2ba393c..66e72e3e73 100644 --- a/test/miscellaneous_tests/api.jl +++ b/test/miscellaneous_tests/api.jl @@ -521,7 +521,9 @@ let end # Test conservation law elimination. +# Conservation laws currently broken (you get stuck in an infinite loop in MTK or something). let + return (@test_broken false) rn = @reaction_network begin (k1, k2), A + B <--> C (m1, m2), D <--> E diff --git a/test/miscellaneous_tests/nonlinear_solve.jl b/test/miscellaneous_tests/nonlinear_solve.jl index 880df22534..b59790df4c 100644 --- a/test/miscellaneous_tests/nonlinear_solve.jl +++ b/test/miscellaneous_tests/nonlinear_solve.jl @@ -8,6 +8,9 @@ using Random, Test using StableRNGs rng = StableRNG(12345) +# Fetch test networks and functions. +include("../test_functions.jl") + ### Run Tests ### # Creates a simple problem and find steady states just different approaches. Compares to analytic solution. @@ -48,7 +51,7 @@ let # Creates NonlinearProblem. u0 = rnd_u0(steady_state_network_2, rng; min = 1.0) - rnd_ps_Int64 = rnd_ps(steady_state_network_2, rng) + ps = rnd_ps(steady_state_network_2, rng) nlprob = NonlinearProblem(steady_state_network_2, u0, ps) # Solves it using standard algorithm and simulation based algorithm. @@ -74,6 +77,8 @@ end # Checks for system with conservation laws. # Checks using interfacing with output solution. let + # Conservation laws currently broken (you get stuck in an infinite loop in MTK or something). + return (@test_broken false) # Creates steady state network, unpack the parameter values. steady_state_network_3 = @reaction_network begin (p,d), 0 <--> X diff --git a/test/miscellaneous_tests/symbolic_stoichiometry.jl b/test/miscellaneous_tests/symbolic_stoichiometry.jl index fa758e56f0..3079e989a4 100644 --- a/test/miscellaneous_tests/symbolic_stoichiometry.jl +++ b/test/miscellaneous_tests/symbolic_stoichiometry.jl @@ -3,7 +3,8 @@ t = default_t() ### Base Tests ### -let +# Probably requires us to permit Int64 parameters for this to work. +@test_broken let @parameters k α @species A(t), B(t), C(t), D(t) rxs = [Reaction(t * k, [A], [B], [2 * α^2], [k + α * C]) @@ -188,7 +189,10 @@ end ### Simple Solving Tests on SIR Model ### -let +# Gives an error. I can fix so there is not, but unsure what part of the code is what we actually are +# testing for, and what we are not. Sam, if you have any opinions on what to preserve in this test or not, +# tell me before I try to rewrite. +@test_broken let @parameters α β γ k @species S(t), I(t), R(t) rxs = [Reaction(α, [S, I], [I], [1, 1], [2]), diff --git a/test/miscellaneous_tests/units.jl b/test/miscellaneous_tests/units.jl index 4a81bccf8e..d32a32e7f7 100644 --- a/test/miscellaneous_tests/units.jl +++ b/test/miscellaneous_tests/units.jl @@ -6,6 +6,10 @@ const MT = ModelingToolkit ### Run Tests ### let + # Units not working right now. Not familiar with Unitful, someone else would have to have a look. + # Probably we should just figure DynamicQuantities out directly and deprecate all of this. + return (@test_broken false) + @parameters α [unit=u"μM/s"] β [unit=u"s"^(-1)] γ [unit=u"μM*s"^(-1)] @variables t [unit=u"s"] @species A(t) [unit=u"μM"] B(t) [unit=u"μM"] C(t) [unit=u"μM"] @@ -54,6 +58,10 @@ end # units in the DSL let + # Units not working right now. Not familiar with Unitful, someone else would have to have a look. + # Probably we should just figure DynamicQuantities out directly and deprecate all of this. + return (@test_broken false) + rn = @reaction_network begin @ivs t [unit=u"s"] @parameters α [unit=u"μM/s"] β [unit=u"s"^(-1)] γ [unit=u"μM*s"^(-1)] diff --git a/test/reactionsystem_structure/higher_order_reactions.jl b/test/reactionsystem_structure/higher_order_reactions.jl index 7299568f34..4a4ca22d0e 100644 --- a/test/reactionsystem_structure/higher_order_reactions.jl +++ b/test/reactionsystem_structure/higher_order_reactions.jl @@ -51,7 +51,10 @@ let end # Tests that the discrete jump systems are equal. -let +# Currently fails because binomial only takes Int input (and X is Float64). +# I see several solutions, but depends on whether we allow species to be specified as Int64. +# I have marked this one as broken for now. +@test_broken let higher_order_network_3 = @reaction_network begin p, ∅ ⟼ X1 r1 * binomial(X1, 2), 2X1 ⟾ 3X2 diff --git a/test/reactionsystem_structure/reactions.jl b/test/reactionsystem_structure/reactions.jl index 24c3dcf636..65a67ed14d 100644 --- a/test/reactionsystem_structure/reactions.jl +++ b/test/reactionsystem_structure/reactions.jl @@ -1,5 +1,5 @@ ### Preparations ### -hi_world() + # Fetch packages. using Catalyst, Test diff --git a/test/reactionsystem_structure/reactionsystem.jl b/test/reactionsystem_structure/reactionsystem.jl index f6945cd9d3..6c7b2c82e8 100644 --- a/test/reactionsystem_structure/reactionsystem.jl +++ b/test/reactionsystem_structure/reactionsystem.jl @@ -165,8 +165,10 @@ let nlprob = NonlinearProblem(rs, u, p) fnl = eval(generate_function(ns)[2]) dunl = similar(du) - fnl(dunl, nlprob.u0, nlprob.p) - @test norm(du - dunl) < 100 * eps() + @test_broken let # The next line throws an error. + fnl(dunl, nlprob.u0, nlprob.p) + @test norm(du - dunl) < 100 * eps() + end end # Test with JumpSystem. diff --git a/test/runtests.jl b/test/runtests.jl index df5bc08e91..c9cfa47ae9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,18 +1,11 @@ ### Fetch the require packages ### using SafeTestsets -println("Started") -function hi_world() - println("Hello WOrld") -end - ### Run the tests ### @time begin ### Tests the properties of ReactionSystems. ### @time @safetestset "Reactions" begin include("reactionsystem_structure/reactions.jl") end - - exit() @time @safetestset "ReactionSystem" begin include("reactionsystem_structure/reactionsystem.jl") end @time @safetestset "Higher Order Reactions" begin include("reactionsystem_structure/higher_order_reactions.jl") end diff --git a/test/spatial_reaction_systems/lattice_reaction_systems_ODEs.jl b/test/spatial_reaction_systems/lattice_reaction_systems_ODEs.jl index 6bfd32d3c0..cdefc6ebc9 100644 --- a/test/spatial_reaction_systems/lattice_reaction_systems_ODEs.jl +++ b/test/spatial_reaction_systems/lattice_reaction_systems_ODEs.jl @@ -3,7 +3,6 @@ # Fetch packages. using OrdinaryDiffEq using Random, Statistics, SparseArrays, Test -t = default_t() # Fetch test networks. include("../spatial_test_networks.jl") @@ -12,6 +11,9 @@ include("../spatial_test_networks.jl") using StableRNGs rng = StableRNG(12345) +# Sets defaults +t = default_t() + ### Tests Simulations Don't Error ### for grid in [small_2d_grid, short_path, small_directed_cycle] # Non-stiff case diff --git a/test/spatial_reaction_systems/simulate_PDEs.jl b/test/spatial_reaction_systems/simulate_PDEs.jl index ce2e06660f..b7edcee73c 100644 --- a/test/spatial_reaction_systems/simulate_PDEs.jl +++ b/test/spatial_reaction_systems/simulate_PDEs.jl @@ -17,6 +17,7 @@ end ### Run Tests ### let + t = default_t() @parameters k[1:7] D[1:3] n0[1:3] A @variables x y @species U(x, y, t) V(x, y, t) W(x, y, t) diff --git a/test/test_functions.jl b/test/test_functions.jl index 46436cf35b..205b8c66a9 100644 --- a/test/test_functions.jl +++ b/test/test_functions.jl @@ -4,23 +4,23 @@ # Initial Condition/Parameter Generators ### # Generates a random initial condition (in the form of a map). Each value is a Float64. -function rnd_u0(sys, rng::StableRNG; factor = 1.0, min = 0.0) +function rnd_u0(sys, rng; factor = 1.0, min = 0.0) return [u => min + factor * rand(rng) for u in unknowns(sys)] end # Generates a random initial condition (in the form of a map). Each value is a Int64. -function rnd_u0_Int64(sys, rng::StableRNG; n = 5, min = 0) - return [u => min + factor * rand(rng, 1:n) for u in unknowns(sys)] +function rnd_u0_Int64(sys, rng; n = 5, min = 0) + return [u => min + rand(rng, 1:n) for u in unknowns(sys)] end # Generates a random parameter set (in the form of a map). Each value is a Float64. -function rnd_ps(sys, rng::StableRNG; factor = 1.0, min = 0.0) +function rnd_ps(sys, rng; factor = 1.0, min = 0.0) return [p => min + factor * rand(rng) for p in parameters(sys)] end # Generates a random parameter set (in the form of a map). Each value is a Float64. -function rnd_ps_Int64(sys, rng::StableRNG; n = 5, min = 0) - return [p => min + factor * rand(rng, 1:n) for p in parameters(sys)] +function rnd_ps_Int64(sys, rng; n = 5, min = 0) + return [p => min + rand(rng, 1:n) for p in parameters(sys)] end ### System function evaluation ### diff --git a/test/visualization/latexify.jl b/test/visualization/latexify.jl index 00169a3c6b..13cf447263 100644 --- a/test/visualization/latexify.jl +++ b/test/visualization/latexify.jl @@ -230,6 +230,7 @@ end ### Checks Reaction Network - Equations Combination ### let + t = default_t() base_network = @reaction_network begin k*r, X --> 0 end From fcc4bd23c1bc74d1ce97e3dca9e672b82d9b11d0 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 22 Mar 2024 16:57:06 -0400 Subject: [PATCH 10/51] up --- test/reactionsystem_structure/reactionsystem.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/reactionsystem_structure/reactionsystem.jl b/test/reactionsystem_structure/reactionsystem.jl index 6c7b2c82e8..74f51e3aa4 100644 --- a/test/reactionsystem_structure/reactionsystem.jl +++ b/test/reactionsystem_structure/reactionsystem.jl @@ -523,7 +523,7 @@ let umean += sol(10.0, idxs = [B1, B2, B3, C]) end umean /= Nsims - @test_broken isapprox(umean[1], umean[2]; rtol = 1e-2) # `idxs` keywordargument currently broken for `SDEProblem`s (/~https://github.com/SciML/SciMLBase.jl/issues/581). + @test isapprox(umean[1], umean[2]; rtol = 1e-2) @test isapprox(umean[1], umean[3]; rtol = 1e-2) @test umean[4] == 10 end From c146f085979afdbc0b5ae09037fb11dc56bc8757 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 22 Mar 2024 17:02:47 -0400 Subject: [PATCH 11/51] a test is no longer broken due to mtk update --- test/model_simulation/simulate_SDEs.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/model_simulation/simulate_SDEs.jl b/test/model_simulation/simulate_SDEs.jl index df0e6137b1..3bb6ad3de9 100644 --- a/test/model_simulation/simulate_SDEs.jl +++ b/test/model_simulation/simulate_SDEs.jl @@ -230,7 +230,7 @@ let u0 = [:X1 => 1000.0, :X2 => 1000.0, :X3 => 1000.0, :X4 => 1000.0, :X5 => 1000.0, :N1 => 3.0, :N3 => 0.33] ps = [:p => 1000.0, :d => 1.0, :η1 => 1.0, :η2 => 1.4, :η3 => 0.33, :η4 => 4.0] sol = solve(SDEProblem(noise_scaling_network, u0, (0.0, 1000.0), ps), ImplicitEM(); saveat=1.0, adaptive=false, dt=0.1) - @test_broken var(sol[:X1]) > var(sol[:X2]) > var(sol[:X3]) > var(sol[:X4]) > var(sol[:X5]) # Likely need this fix: /~https://github.com/SciML/ModelingToolkit.jl/issues/2556 + @test var(sol[:X1]) > var(sol[:X2]) > var(sol[:X3]) > var(sol[:X4]) > var(sol[:X5]) end # Tests the `remake_noise_scaling` function. From 3f61438f6a5a195181380ce1d2ec45c74706d8c3 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 22 Mar 2024 17:04:09 -0400 Subject: [PATCH 12/51] remove PEtab from docs (add back once it works for MTK v9) --- docs/Project.toml | 2 -- docs/pages.jl | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index f77ca28a2c..cf248f2507 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -17,7 +17,6 @@ OptimizationNLopt = "4e6fcdb7-1186-4e1f-a706-475e75c168bb" OptimizationOptimJL = "36348300-93cb-4f02-beb5-3c3902f8871e" OptimizationOptimisers = "42dfb2eb-d2b4-4451-abcd-913932933ac1" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" -PEtab = "48d54b35-e43e-4a66-a5a1-dde6b987cf69" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" QuasiMonteCarlo = "8a4e6c94-4038-4cdc-81c3-7e6ffdb2a71b" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" @@ -47,7 +46,6 @@ OptimizationNLopt = "0.1.8" OptimizationOptimJL = "0.1.14" OptimizationOptimisers = "0.1.1" OrdinaryDiffEq = "6" -PEtab = "2" Plots = "1.36" SciMLBase = "2.13" SciMLSensitivity = "7.19" diff --git a/docs/pages.jl b/docs/pages.jl index 6ee0489c19..66058a65ef 100644 --- a/docs/pages.jl +++ b/docs/pages.jl @@ -17,7 +17,7 @@ pages = Any["Home" => "index.md", "catalyst_applications/nonlinear_solve.md", "catalyst_applications/bifurcation_diagrams.md"], "Inverse Problems" => Any["inverse_problems/optimization_ode_param_fitting.md", - "inverse_problems/petab_ode_param_fitting.md", + #"inverse_problems/petab_ode_param_fitting.md", "inverse_problems/structural_identifiability.md", "Inverse problem examples" => Any["inverse_problems/examples/ode_fitting_oscillation.md"]], "FAQs" => "faqs.md", From 4843a26ef6fd46c5e3f851e4abd32416aeed0d30 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 22 Mar 2024 17:44:58 -0400 Subject: [PATCH 13/51] up --- docs/pages.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages.jl b/docs/pages.jl index 66058a65ef..9f9d07000c 100644 --- a/docs/pages.jl +++ b/docs/pages.jl @@ -1,6 +1,6 @@ pages = Any["Home" => "index.md", "Introduction to Catalyst" => Any["introduction_to_catalyst/catalyst_for_new_julia_users.md", - "introduction_to_catalyst/introduction_to_catalyst.md" ], + "introduction_to_catalyst/introduction_to_catalyst.md"], "Catalyst Functionality" => Any["catalyst_functionality/dsl_description.md", "catalyst_functionality/programmatic_CRN_construction.md", "catalyst_functionality/compositional_modeling.md", From 9e6e74ecf99646269be0e2d5bc01338033ee7102 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 22 Mar 2024 17:45:10 -0400 Subject: [PATCH 14/51] broken test --- test/model_simulation/simulate_SDEs.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/model_simulation/simulate_SDEs.jl b/test/model_simulation/simulate_SDEs.jl index 3bb6ad3de9..ad2bf632ef 100644 --- a/test/model_simulation/simulate_SDEs.jl +++ b/test/model_simulation/simulate_SDEs.jl @@ -230,7 +230,7 @@ let u0 = [:X1 => 1000.0, :X2 => 1000.0, :X3 => 1000.0, :X4 => 1000.0, :X5 => 1000.0, :N1 => 3.0, :N3 => 0.33] ps = [:p => 1000.0, :d => 1.0, :η1 => 1.0, :η2 => 1.4, :η3 => 0.33, :η4 => 4.0] sol = solve(SDEProblem(noise_scaling_network, u0, (0.0, 1000.0), ps), ImplicitEM(); saveat=1.0, adaptive=false, dt=0.1) - @test var(sol[:X1]) > var(sol[:X2]) > var(sol[:X3]) > var(sol[:X4]) > var(sol[:X5]) + @test_broken var(sol[:X1]) > var(sol[:X2]) > var(sol[:X3]) > var(sol[:X4]) > var(sol[:X5]) end # Tests the `remake_noise_scaling` function. From eca9e9dd0f36b477e9bb21ae21ce655e94659bb5 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 22 Mar 2024 20:25:59 -0400 Subject: [PATCH 15/51] update --- src/reaction_network.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/reaction_network.jl b/src/reaction_network.jl index 73abbf31bc..2d838ece0c 100644 --- a/src/reaction_network.jl +++ b/src/reaction_network.jl @@ -362,6 +362,7 @@ function make_reaction_system(ex::Expr; name = :(gensym(:ReactionSystem))) # Reads options. default_reaction_metadata = :([]) + check_default_noise_scaling!(default_reaction_metadata, options) compound_expr, compound_species = read_compound_options(options) check_default_noise_scaling!(default_reaction_metadata, options) default_reaction_metadata = expr_equal_vector_to_pairs(default_reaction_metadata) @@ -601,7 +602,7 @@ function get_reactions(exprs::Vector{Expr}, reactions = Vector{ReactionStruct}(u for line in exprs # Reads core reaction information. arrow, rate, reaction, metadata = read_reaction_line(line) - + # Checks the type of arrow used, and creates the corresponding reaction(s). Returns them in an array. if in(arrow, double_arrows) if typeof(rate) != Expr || rate.head != :tuple From 03c7380c557ef7ccd28ee3d89985594cbd66d82b Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 22 Mar 2024 20:41:31 -0400 Subject: [PATCH 16/51] unmark broken test, no longer broken --- test/model_simulation/simulate_SDEs.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/model_simulation/simulate_SDEs.jl b/test/model_simulation/simulate_SDEs.jl index ad2bf632ef..3bb6ad3de9 100644 --- a/test/model_simulation/simulate_SDEs.jl +++ b/test/model_simulation/simulate_SDEs.jl @@ -230,7 +230,7 @@ let u0 = [:X1 => 1000.0, :X2 => 1000.0, :X3 => 1000.0, :X4 => 1000.0, :X5 => 1000.0, :N1 => 3.0, :N3 => 0.33] ps = [:p => 1000.0, :d => 1.0, :η1 => 1.0, :η2 => 1.4, :η3 => 0.33, :η4 => 4.0] sol = solve(SDEProblem(noise_scaling_network, u0, (0.0, 1000.0), ps), ImplicitEM(); saveat=1.0, adaptive=false, dt=0.1) - @test_broken var(sol[:X1]) > var(sol[:X2]) > var(sol[:X3]) > var(sol[:X4]) > var(sol[:X5]) + @test var(sol[:X1]) > var(sol[:X2]) > var(sol[:X3]) > var(sol[:X4]) > var(sol[:X5]) end # Tests the `remake_noise_scaling` function. From cfa6b25d1668b5302f9f620ad609225ad5ba2824 Mon Sep 17 00:00:00 2001 From: Torkel Date: Sun, 24 Mar 2024 13:40:06 -0400 Subject: [PATCH 17/51] save changes --- test/model_simulation/simulate_SDEs.jl | 2 +- test/model_simulation/simulate_jumps.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/model_simulation/simulate_SDEs.jl b/test/model_simulation/simulate_SDEs.jl index 3bb6ad3de9..cf9de307d7 100644 --- a/test/model_simulation/simulate_SDEs.jl +++ b/test/model_simulation/simulate_SDEs.jl @@ -12,7 +12,7 @@ rng = StableRNG(12345) include("../test_networks.jl") include("../test_functions.jl") -### Compares to Manually Calcualted Function ### +### Compares to Manually Computed Function ### let identical_networks = Vector{Pair}() diff --git a/test/model_simulation/simulate_jumps.jl b/test/model_simulation/simulate_jumps.jl index bbb70d11f8..28d90e0812 100644 --- a/test/model_simulation/simulate_jumps.jl +++ b/test/model_simulation/simulate_jumps.jl @@ -11,7 +11,7 @@ rng = StableRNG(12345) # Fetch test networks. include("../test_networks.jl") -### Compares to Manually Calcualted Function ### +### Compares to Manually Computed Function ### let identical_networks = Vector{Pair}() From a569190462176ecbb65f3bcd2c064474b3bbd9fa Mon Sep 17 00:00:00 2001 From: Torkel Date: Mon, 25 Mar 2024 18:23:10 -0400 Subject: [PATCH 18/51] rewors --- test/dsl/custom_functions.jl | 33 ++--- test/dsl/dsl_basics.jl | 120 +++++++++------- test/dsl/dsl_model_construction.jl | 43 +++--- test/dsl/dsl_options.jl | 16 +-- test/extensions/bifurcation_kit.jl | 6 +- test/extensions/homotopy_continuation.jl | 6 +- test/extensions/structural_identifiability.jl | 23 +-- test/miscellaneous_tests/api.jl | 71 +++++----- test/miscellaneous_tests/compound_macro.jl | 132 ++++++++++-------- test/miscellaneous_tests/events.jl | 19 ++- test/miscellaneous_tests/nonlinear_solve.jl | 7 +- .../miscellaneous_tests/reaction_balancing.jl | 21 ++- .../symbolic_stoichiometry.jl | 7 +- test/model_simulation/make_jacobian.jl | 7 +- test/model_simulation/simulate_ODEs.jl | 110 +++++++++------ test/model_simulation/simulate_SDEs.jl | 124 +++++++++++----- test/model_simulation/simulate_jumps.jl | 112 ++++++++------- test/network_analysis/conservation_laws.jl | 17 +-- test/network_analysis/network_properties.jl | 15 +- .../component_based_model_creation.jl | 10 +- .../programmatic_model_expansion.jl | 15 +- .../higher_order_reactions.jl | 32 +++-- test/reactionsystem_structure/reactions.jl | 2 +- .../reactionsystem.jl | 18 ++- test/test_functions.jl | 12 +- test/visualization/graphs.jl | 3 + test/visualization/latexify.jl | 107 +++++++------- 27 files changed, 624 insertions(+), 464 deletions(-) diff --git a/test/dsl/custom_functions.jl b/test/dsl/custom_functions.jl index 6364362428..0f3d408ffb 100644 --- a/test/dsl/custom_functions.jl +++ b/test/dsl/custom_functions.jl @@ -1,4 +1,3 @@ - ### Fetch Packages and Set Global Variables ### using DiffEqBase, Catalyst, Random, Symbolics, Test using ModelingToolkit: get_unknowns, get_ps @@ -7,6 +6,9 @@ t = default_t() using StableRNGs rng = StableRNG(12345) +# Fetch test functions. +include("../test_functions.jl") + ### Tests Custom Functions ### let new_hill(x, v, k, n) = v * x^n / (k^n + x^n) @@ -33,32 +35,13 @@ let v5 * (X7^2) / (K5^2 + X7^2 + Y7^2), X7 + Y7 --> Z7 end - function permute_ps(pvals, rn1, rn2) - ps1 = parameters(rn1) - ps2 = parameters(rn2) - pvals2 = similar(pvals) - for (i, p) in enumerate(ps2) - pidx = findfirst(isequal(p), ps1) - pvals2[i] = pvals[pidx] - end - pvals2 - end - - f1 = ODEFunction(convert(ODESystem, custom_function_network_1), jac = true) - f2 = ODEFunction(convert(ODESystem, custom_function_network_2), jac = true) - g1 = SDEFunction(convert(SDESystem, custom_function_network_1)) - g2 = SDEFunction(convert(SDESystem, custom_function_network_2)) for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2] - u0 = factor * rand(rng, length(get_unknowns(custom_function_network_1))) - p = factor * rand(rng, length(get_ps(custom_function_network_2))) - - # needed as this code assumes an ordering of the parameters and species... - p2 = permute_ps(p, custom_function_network_1, custom_function_network_2) - + u0 = rnd_u0(custom_function_network_1, rng; factor) + ps = rnd_ps(custom_function_network_1, rng; factor) t = rand(rng) - @test all(abs.(f1(u0, p, t) .- f2(u0, p2, t)) .< 10e-10) - @test all(abs.(f1.jac(u0, p, t) .- f2.jac(u0, p2, t)) .< 10e-10) - @test all(abs.(g1(u0, p, t) .- g2(u0, p2, t)) .< 10e-10) + @test f_eval(custom_function_network_1, u0, ps, t) ≈ f_eval(custom_function_network_2, u0, ps, t) + @test jac_eval(custom_function_network_1, u0, ps, t) ≈ jac_eval(custom_function_network_2, u0, ps, t) + @test g_eval(custom_function_network_1, u0, ps, t) ≈ g_eval(custom_function_network_2, u0, ps, t) end end diff --git a/test/dsl/dsl_basics.jl b/test/dsl/dsl_basics.jl index d970242062..20b9254090 100644 --- a/test/dsl/dsl_basics.jl +++ b/test/dsl/dsl_basics.jl @@ -2,11 +2,15 @@ ### Fetch Packages and Set Global Variables ### +# Fetch packages. using Catalyst, ModelingToolkit + +# Set creates the `t` independent variable. t = default_t() ### Naming Tests ### +# Test that the correct name is generated. let @parameters k @species A(t) @@ -63,63 +67,58 @@ end ### Test Interpolation Within the DSL ### -@parameters α k k1 k2 -@species A(t) B(t) C(t) D(t) - +# Tests basic interpolation cases. let - AA = A - AAA = A^2 + B - rn = @reaction_network rn begin - @parameters k - @species A(t) B(t) C(t) D(t) - k*$AAA, C --> D - end - rn2 = ReactionSystem([Reaction(k*AAA, [C], [D])], t; name=:rn) - @test rn == rn2 + # Declares parameters and species used across the test. + @parameters α k k1 k2 + @species A(t) B(t) C(t) D(t) - rn = @reaction_network rn begin - @parameters k - @species A(t) C(t) D(t) - k, $AA + C --> D - end - rn2 = ReactionSystem([Reaction(k, [AA,C], [D])], t; name=:rn) - @test rn == rn2 -end + let + AA = A + AAA = A^2 + B + rn = @reaction_network rn begin + @parameters k + @species A(t) B(t) C(t) D(t) + k*$AAA, C --> D + end + rn2 = ReactionSystem([Reaction(k*AAA, [C], [D])], t; name=:rn) + @test rn == rn2 -let - BB = B; A2 = A - rn = @reaction_network rn begin - @parameters k1 k2 - (k1,k2), C + $A2 + $BB + $A2 <--> $BB + $BB + rn = @reaction_network rn begin + @parameters k + @species A(t) C(t) D(t) + k, $AA + C --> D + end + rn2 = ReactionSystem([Reaction(k, [AA,C], [D])], t; name=:rn) + @test rn == rn2 end - rn2 = ReactionSystem([Reaction(k1, [C, A, B], [B], [1,2,1],[2]), - Reaction(k2, [B], [C, A, B], [2], [1,2,1])], - t; name=:rn) - @test rn == rn2 -end -let - AA = A - kk1 = k^2*A - kk2 = k1+k2 - rn = @reaction_network rn begin - @parameters α k k1 k2 - α+$kk1*$kk2*$AA, 2*$AA + B --> $AA + let + BB = B; A2 = A + rn = @reaction_network rn begin + @parameters k1 k2 + (k1,k2), C + $A2 + $BB + $A2 <--> $BB + $BB + end + rn2 = ReactionSystem([Reaction(k1, [C, A, B], [B], [1,2,1],[2]), + Reaction(k2, [B], [C, A, B], [2], [1,2,1])], + t; name=:rn) + @test rn == rn2 end - rn2 = ReactionSystem([Reaction(α+kk1*kk2*AA, [A, B], [A], [2, 1], [1])], t; name=:rn) - @test rn == rn2 -end -@testset "make_reaction_system can be called from another module" begin - ex = quote - (Ka, Depot --> Central) - (CL / Vc, Central --> 0) + let + AA = A + kk1 = k^2*A + kk2 = k1+k2 + rn = @reaction_network rn begin + @parameters α k k1 k2 + α+$kk1*$kk2*$AA, 2*$AA + B --> $AA + end + rn2 = ReactionSystem([Reaction(α+kk1*kk2*AA, [A, B], [A], [2, 1], [1])], t; name=:rn) + @test rn == rn2 end - # Line number nodes aren't ignored so have to be manually removed - Base.remove_linenums!(ex) - @test eval(Catalyst.make_reaction_system(ex)) isa ReactionSystem end +# Miscellaneous interpolation tests. Unsure what they do here (not related to DSL). let rx = @reaction k*h, A + 2*B --> 3*C + D @parameters k h @@ -133,10 +132,21 @@ let @test rx == Reaction(b+ex, [A,C], nothing, [2,1], nothing) end +# Creates a reaction network using `eval` and internal function. +let + ex = quote + (Ka, Depot --> Central) + (CL / Vc, Central --> 0) + end + # Line number nodes aren't ignored so have to be manually removed + Base.remove_linenums!(ex) + @test eval(Catalyst.make_reaction_system(ex)) isa ReactionSystem +end + ### Tests Reaction Metadata ### -# Tests construction for various types of metadata. -# Tests accessor functions. +# Tests construction for various types of reaction metadata. +# Tests reaction metadata accessor functions. let # Creates reactions directly. @variables t @@ -252,7 +262,7 @@ let @test isequal(rn1,rn2) end -### Other tests ### +### Other Tests ### # Test floating point stoichiometry work. let @@ -272,7 +282,7 @@ let @test rn == mixedsys end -# Test variables that appear only in rates and aren't ps +# Test that variables that appear only in rates and aren't ps # are categorized as species. let rn = @reaction_network begin @@ -312,7 +322,7 @@ let @test length(equations(osys2)) == 2 end -# test @variables in DSL +# Test @variables in DSL. let rn = @reaction_network tester begin @parameters k1 @@ -341,7 +351,7 @@ let end end -# ivs test +# Test ivs in DSL. let rn = @reaction_network ivstest begin @ivs s x @@ -350,11 +360,13 @@ let @species A(s,x) B(s) C(x) k*k2*D, E*A +B --> F*C + C2 end + @parameters k k2 @variables s x D(x) E(s) F(s,x) @species A(s,x) B(s) C(x) C2(s,x) rx = Reaction(k*k2*D, [A, B], [C, C2], [E, 1], [F, 1]) @named ivstest = ReactionSystem([rx], s; spatial_ivs = [x]) + @test ivstest == rn @test issetequal(unknowns(rn), [D, E, F, A, B, C, C2]) @test issetequal(species(rn), [A, B, C, C2]) @@ -362,7 +374,7 @@ let @test issetequal(Catalyst.get_sivs(rn), [x]) end -# array variables test +# Array variables test. let rn = @reaction_network arrtest begin @parameters k[1:2] a diff --git a/test/dsl/dsl_model_construction.jl b/test/dsl/dsl_model_construction.jl index 6554b2b08a..9d3587cb35 100644 --- a/test/dsl/dsl_model_construction.jl +++ b/test/dsl/dsl_model_construction.jl @@ -76,7 +76,7 @@ let basic_test(reaction_networks_weird[2], 4, [:X, :Y, :Z], [:k1, :k2, :k3, :k4]) end -# Tries making various systems. +# Compares networks to networks created using different arrow types. let identical_networks_1 = Vector{Pair}() @@ -137,7 +137,7 @@ let end end -# Tests that networks expressed in different ways are identical. +# Compares networks to networks written in different ways. let identical_networks_2 = Vector{Pair}() @@ -203,7 +203,7 @@ let end end -# Test networks without parameters. +# Compares networks to networks written without parameters let identical_networks_3 = Vector{Pair}() parameter_sets = [] @@ -217,8 +217,8 @@ let (sqrt(3.7), exp(1.9)), X4 ⟷ X1 + X2 end push!(identical_networks_3, reaction_networks_standard[9] => no_parameters_9) - push!(parameter_sets, - [1.5, 1, 2, 0.01, 2.3, 1001, π, 42, 19.9, 999.99, sqrt(3.7), exp(1.9)]) + push!(parameter_sets, [:p1 => 1.5, :p2 => 1, :p3 => 2, :d1 => 0.01, :d2 => 2.3, :d3 => 1001, + :k1 => π, :k2 => 42, :k3 => 19.9, :k4 => 999.99, :k5 => sqrt(3.7), :k6 => exp(1.9)]) no_parameters_10 = @reaction_network begin 0.01, ∅ ⟶ X1 @@ -229,19 +229,17 @@ let 1.0, X5 ⟶ ∅ end push!(identical_networks_3, reaction_networks_standard[10] => no_parameters_10) - push!(parameter_sets, [0.01, 3.1, 3.2, 0.0, 2.1, 901.0, 63.5, 7, 8, 1.0]) + push!(parameter_sets, [:p => 0.01, :k1 => 3.1, :k2 => 3.2, :k3 => 0.0, :k4 => 2.1, :k5 => 901.0, + :k6 => 63.5, :k7 => 7, :k8 => 8, :d => 1.0]) - for (i, networks) in enumerate(identical_networks_3) + for (networks, p_1) in zip(identical_networks_3, parameter_sets) for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] - u0_1 = rnd_u0(networks[1], rng; factor) - p_1 = Pair.(parameters(networks[1]), parameter_sets[i]) - u0_2 = Pair.(unknowns(networks[2]), last.(u0_1)) - p_2 = [] + u0 = rnd_u0(networks[1], rng; factor) t = rand(rng) - @test f_eval(networks[1], u0_1, p_1, t) ≈ f_eval(networks[2], u0_2, p_2, t) - @test jac_eval(networks[1], u0_1, p_1, t) ≈ jac_eval(networks[2], u0_2, p_2, t) - @test g_eval(networks[1], u0_1, p_1, t) ≈ g_eval(networks[2], u0_2, p_2, t) + @test f_eval(networks[1], u0, p_1, t) ≈ f_eval(networks[2], u0, [], t) + @test jac_eval(networks[1], u0, p_1, t) ≈ jac_eval(networks[2], u0, [], t) + @test g_eval(networks[1], u0, p_1, t) ≈ g_eval(networks[2], u0, [], t) end end end @@ -318,7 +316,7 @@ let end end -# Test various names as varriables. +# CHeck that various symbols can be used as species/parameter names. let @reaction_network begin (a, A), n ⟷ N @@ -359,7 +357,7 @@ let end end -# Test that I works. +# Test that I works as a name. let rn = @reaction_network begin k1, S + I --> 2I @@ -370,16 +368,7 @@ let @test any(isequal(I), unknowns(rn)) end -# Test names work. -let - rn = @reaction_network SIR1 begin - k1, S + I --> 2I - k2, I --> R - end - @test nameof(rn) == :SIR1 -end - -# Tests some arrow variants. +# Tests backwards and double arrows. let rn1 = @reaction_network arrowtest begin (a1, a2), C <--> 0 @@ -406,7 +395,7 @@ let @test isequal((@reaction k, 0 --> X), (@reaction k, 0 ⥟ X)) end -# Test forbidden and special symbols. +# Test that symbols with special mean, or that are forbidden, are handled properly. let test_network = @reaction_network begin t * k, X --> ∅ end @test length(species(test_network)) == 1 diff --git a/test/dsl/dsl_options.jl b/test/dsl/dsl_options.jl index 3666c44eaf..20f5930196 100644 --- a/test/dsl/dsl_options.jl +++ b/test/dsl/dsl_options.jl @@ -1,12 +1,12 @@ #! format: off ### Fetch Packages and Set Global Variables ### -using Catalyst, ModelingToolkit, OrdinaryDiffEq, Plots +using Catalyst, ModelingToolkit, OrdinaryDiffEq, Plots, Test t = default_t() -### Run Tests ### +### Tests @parameters and @species Options ### -# Test creating networks with/without options. +# Test creating networks with/without options. Compares they are all identical. let @reaction_network begin (k1, k2), A <--> B end @reaction_network begin @@ -104,7 +104,7 @@ let @test all(==(n1), (n2, n3, n4, n5, n6, n7, n8, n9, n10)) end -# Tests that when either @species or @parameters is given, the other is infered properly. +# Tests that when either @species or @parameters is given, the other is inferred properly. let rn1 = @reaction_network begin k*X, A + B --> 0 @@ -145,7 +145,6 @@ end # Test inferring with stoichiometry symbols and interpolation. let @parameters k g h gg X y [isconstantspecies = true] - t = Catalyst.DEFAULT_IV @species A(t) B(t) BB(t) C(t) rni = @reaction_network inferred begin @@ -220,7 +219,7 @@ let @test issetequal(parameters(rn11), @parameters k1 k2 X2) end -##Checks that some created networks are identical. +# Checks that networks created using different notation are identical. let rn12 = @reaction_network rnname begin (k1, k2), A <--> B end rn13 = @reaction_network rnname begin @@ -387,7 +386,7 @@ let @test plot(sol; idxs=[:X, :Y]).series_list[2].plotattributes[:y][end] ≈ 3.0 end -# Compares programmatic and DSL system with observables. +# Compares programmatic and DSL systems with observables. let # Model declarations. rn_dsl = @reaction_network begin @@ -479,7 +478,7 @@ end # Declares observables implicitly/explicitly. # Cannot test `isequal(rn1, rn2)` because the two sets of observables have some obscure Symbolics -# substructure that is different. +# substructure that are different. let # Basic case. rn1 = @reaction_network rn_observed begin @@ -632,6 +631,7 @@ let end ### Tests Completeness Designations ### + let # Creates models with/without the `@incomplete` option. rn1 = @reaction_network begin diff --git a/test/extensions/bifurcation_kit.jl b/test/extensions/bifurcation_kit.jl index 4ea1b74362..79443682fb 100644 --- a/test/extensions/bifurcation_kit.jl +++ b/test/extensions/bifurcation_kit.jl @@ -1,7 +1,9 @@ -### Fetch Packages ### +### Prepares Tests ### + +# Fetch packages. using BifurcationKit, Catalyst, Test -# Sets rnd number. +# Sets stable rng number. using StableRNGs rng = StableRNG(12345) diff --git a/test/extensions/homotopy_continuation.jl b/test/extensions/homotopy_continuation.jl index 3005b14975..d366c42f46 100644 --- a/test/extensions/homotopy_continuation.jl +++ b/test/extensions/homotopy_continuation.jl @@ -1,4 +1,6 @@ -### Fetch Packages ### +### Prepares Tests ### + +# Fetch packages. using Catalyst, Test import HomotopyContinuation @@ -95,7 +97,7 @@ let @test length(sss) == 4 for ss in sss - @test isapprox(f_eval(rs,sss[1], last.(ps), 0.0)[1], 0.0; atol=1e-12) + @test f_eval(rs,sss[1], last.(ps), 0.0)[1] ≈ 0.0 atol=1e-12 end @test_throws Exception hc_steady_states(rs, [:v => 5.0, :K => 2.5, :n => 2.7, :d => 1.0]; show_progress=false) diff --git a/test/extensions/structural_identifiability.jl b/test/extensions/structural_identifiability.jl index 9c9cfb1217..9473001c33 100644 --- a/test/extensions/structural_identifiability.jl +++ b/test/extensions/structural_identifiability.jl @@ -1,11 +1,9 @@ -### Fetch Packages ### +### Prepares Tests ### -using Catalyst, Test -using StructuralIdentifiability - - -### Helper Function ### +# Fetch packages. +using Catalyst, StructuralIdentifiability, Test +# Helper function for checking that results are correct identifiability calls from different packages. # Converts the output dicts from StructuralIdentifiability functions from "weird symbol => stuff" to "symbol => stuff" (the output have some strange meta data which prevents equality checks, this enables this). # Structural identifiability also provides variables like x (rather than x(t)). This is a bug, but we have to convert to make it work (now just remove any (t) to make them all equal). function sym_dict(dict_in) @@ -203,17 +201,6 @@ let @test make_si_ode(gw_osc_complt; measured_quantities=[gw_osc_complt.M*gw_osc_complt.E]) isa ODE end -# Check that `prob_threshold` alternative kwarg works. -let - rs = @reaction_network begin - p, X --> 0 - end - @unpack X = rs - - assess_identifiability(rs; measured_quantities=[X], prob_threshold=0.9) - assess_identifiability(rs; measured_quantities=[X], prob_threshold=0.999) -end - # Tests for hierarchical model with conservation laws at both top and internal levels. let # Identifiability analysis for Catalyst model. @@ -255,7 +242,7 @@ let # Check outputs. @test sym_dict(gi_1) == sym_dict(gi_3) @test sym_dict(li_1) == sym_dict(li_3) - @test length(ifs_1)-2 == length(ifs_2)-2 == length(ifs_3) # In the first case, the conservation law parameter is also identifiable. + @test (length(ifs_1) - 2) == (length(ifs_2) - 2) == length(ifs_3) # In the first case, the conservation law parameter is also identifiable. # Checks output for the SI converted version of the catalyst model. # For nested systems with conservation laws, conserved quantities like Γ[1], cannot be replaced back. diff --git a/test/miscellaneous_tests/api.jl b/test/miscellaneous_tests/api.jl index 66e72e3e73..55dddabfc6 100644 --- a/test/miscellaneous_tests/api.jl +++ b/test/miscellaneous_tests/api.jl @@ -1,17 +1,22 @@ #! format: off -### Fetch Packages and Test Networks ### -using Catalyst, DiffEqBase, ModelingToolkit, Test, OrdinaryDiffEq, NonlinearSolve -using StochasticDiffEq +### Prepares Tests ### + +# Fetch packages. +using Catalyst, NonlinearSolve, OrdinaryDiffEq, SparseArrays, StochasticDiffEq, Test using LinearAlgebra: norm -using SparseArrays using ModelingToolkit: value + +# Sets the default `t` to use. t = default_t() +# Fetch test networks. include("../test_networks.jl") -### Base Tests ### +### Tests Basic Getters ### +# Checks various getter functions. +# Uses several system-modifying functions, and should probably be rewritten not to use these. let @parameters k1 k2 @species S(t) I(t) R(t) @@ -84,6 +89,7 @@ let @test numreactionparams(rs) == 3 end +# Tests `substoichmat` and `prodstoichmat` getters. let rnmat = @reaction_network begin α, S + 2I --> 2I @@ -100,6 +106,17 @@ let @test pmat == prodstoichmat(rnmat) == Matrix(prodstoichmat(rnmat, sparse = true)) end +# Tests `reactionparamsmap`, `reactionrates`, and `symmap_to_varmap` getters. +let + rn = @reaction_network begin + (p,d), 0 <--> X + (kB,kD), 2X <--> X + end + @unpack p, d, kB, kD = rn + isequal(reactionparamsmap(rn), Dict([p => 1, d => 2, kB => 3, kD => 4])) + issetequal(reactionrates(rn), [p, d, kB, kD]) + isequal(symmap_to_varmap(rn, [:p => 1.0, :kB => 3.0]), [p => 1.0, kB => 3.0]) +end ### Test Intermediate Complexes Reaction Networks ### @@ -121,7 +138,6 @@ function testnetwork(rn, B, Z, Δ, lcs, d, subrn, lcd; skiprxtest = false) @test sum(linkagedeficiencies(rn)) <= deficiency(rn) end - # Mass-action non-catalytic. let rn = @reaction_network begin @@ -307,7 +323,7 @@ let testnetwork(rn, B, Z, Δ, lcs, 0, subrn, lcd) end -### Testing Reversibility ### +### Tests Reversibility ### # Test function. function testreversibility(rn, B, rev, weak_rev) @@ -316,6 +332,7 @@ function testreversibility(rn, B, rev, weak_rev) @test isweaklyreversible(rn, subrn) == weak_rev end +# Tests reversibility for networks with known reversibility. let rn = @reaction_network begin (k2, k1), A1 <--> A2 + A3 @@ -329,7 +346,6 @@ let weak_rev = false testreversibility(rn, reactioncomplexes(rn)[2], rev, weak_rev) end - let rn = @reaction_network begin (k2, k1), A1 <--> A2 + A3 @@ -343,7 +359,6 @@ let weak_rev = false testreversibility(rn, reactioncomplexes(rn)[2], rev, weak_rev) end - let rn = @reaction_network begin k1, A --> B @@ -353,7 +368,6 @@ let weak_rev = false testreversibility(rn, reactioncomplexes(rn)[2], rev, weak_rev) end - let rn = @reaction_network begin k1, A --> B @@ -364,7 +378,6 @@ let weak_rev = false testreversibility(rn, reactioncomplexes(rn)[2], rev, weak_rev) end - let rn = @reaction_network begin (k2, k1), A <--> 2B @@ -376,7 +389,6 @@ let weak_rev = true testreversibility(rn, reactioncomplexes(rn)[2], rev, weak_rev) end - let rn = @reaction_network begin (k2, k1), A + E <--> AE @@ -386,7 +398,6 @@ let weak_rev = false testreversibility(rn, reactioncomplexes(rn)[2], rev, weak_rev) end - let rn = @reaction_network begin (k2, k1), A + E <--> AE @@ -396,14 +407,12 @@ let weak_rev = true testreversibility(rn, reactioncomplexes(rn)[2], rev, weak_rev) end - let rn = @reaction_network begin (k2, k1), A + B <--> 2A end rev = true weak_rev = true testreversibility(rn, reactioncomplexes(rn)[2], rev, weak_rev) end - let rn = @reaction_network begin k1, A + B --> 3A @@ -415,7 +424,6 @@ let weak_rev = true testreversibility(rn, reactioncomplexes(rn)[2], rev, weak_rev) end - let rn = @reaction_network begin (k2, k1), A + E <--> AE @@ -428,9 +436,7 @@ let testreversibility(rn, reactioncomplexes(rn)[2], rev, weak_rev) end -# ------------------------------------------------------------------------- # - -### More Tests ### +### Other Tests ### let myrn = [reaction_networks_standard; reaction_networks_hill; reaction_networks_real] @@ -444,6 +450,10 @@ let end # Test defaults. +# Uses mutating stuff (`setdefaults!`) and order dependent input (`species(rn) .=> u0`). +# If you want to test this here @Sam I can write a new one that simualtes using defaults. +# If so, tell me if you have anything specific you want to check though, or I will just implement +# it as I would. let rn = @reaction_network begin α, S + I --> 2I @@ -592,8 +602,7 @@ let @test isapprox(g2[istsidxs, :], g3) end - -# Non-integer stoichiometry. +# Tests non-integer stoichiometry. let function test_stoich(T, rn) @test eltype(substoichmat(rn)) == T @@ -616,20 +625,6 @@ let test_stoich(Int, rn2) end -### Miscelenesous Tests ### - -# Tests various additional API functions. -let - rn = @reaction_network begin - (p,d), 0 <--> X - (kB,kD), 2X <--> X - end - @unpack p, d, kB, kD = rn - isequal(reactionparamsmap(rn), Dict([p => 1, d => 2, kB => 3, kD => 4])) - issetequal(reactionrates(rn), [p, d, kB, kD]) - isequal(symmap_to_varmap(rn, [:p => 1.0, :kB => 3.0]), [p => 1.0, kB => 3.0]) -end - ### Test Polynomial Transformation Functionality ### # Tests normal network. @@ -639,7 +634,7 @@ let (kB,kD), 2X <--> X2 end ns = convert(NonlinearSystem, rn) - neweqs = getfield.(equations(ns),:rhs) + neweqs = getfield.(equations(ns), :rhs) poly = Catalyst.to_multivariate_poly(neweqs) @test length(poly) == 2 end @@ -650,7 +645,7 @@ let (p/X,d), 0 <--> X end ns = convert(NonlinearSystem, rn) - neweqs = getfield.(equations(ns),:rhs) + neweqs = getfield.(equations(ns), :rhs) poly = Catalyst.to_multivariate_poly(neweqs) @test length(poly) == 1 end @@ -659,6 +654,6 @@ end let rn = @reaction_network ns = convert(NonlinearSystem, rn) - neweqs = getfield.(equations(ns),:rhs) + neweqs = getfield.(equations(ns), :rhs) @test_throws MethodError Catalyst.to_multivariate_poly(neweqs) end diff --git a/test/miscellaneous_tests/compound_macro.jl b/test/miscellaneous_tests/compound_macro.jl index 1eb7e43623..1c130822ce 100644 --- a/test/miscellaneous_tests/compound_macro.jl +++ b/test/miscellaneous_tests/compound_macro.jl @@ -1,7 +1,14 @@ +### Prepares Tests ### + +# Fetch packages. using Catalyst, Test + +# Sets the default `t` to use. t = default_t() -### Tests Main Macro Creation Forms ### +### Test Macro Basic Functionality ### + +# Miscellaneous basic usage. let @species C(t) H(t) O(t) @parameters p1 p2 @@ -60,36 +67,7 @@ let @test iscompound(rn.NH3_5) end -### Test Various Independent Variables ### -let - @variables x y z - @species C(t) H(x) N(x) O(t) P(t,x) S(x,y) - - # Checks that wrong (or absent) independent variable produces errors. - @test_throws Exception @eval @compound CO2(t,x) ~ C + 2O - @test_throws Exception @eval @compound (NH4(s), [output=true]) ~ N + 4H - @test_throws Exception @eval @compound (H2O = 2.0) ~ 2H + O - @test_throws Exception @eval @compound PH4(x) ~ P + 4H - @test_throws Exception @eval @compound SO2(t,y) ~ S + 2O - - # Creates compounds. - @compound CO2 ~ C + 2O - @compound (NH4, [output=true]) ~ N + 4H - @compound (H2O(t,x) = 2.0) ~ 2H + O - @compound PH4(t,x) ~ P + 4H - @compound SO2(t,x,y) ~ S + 2O - - # Checks they have the correct independent variables. - @test issetequal(arguments(ModelingToolkit.unwrap(CO2)), [t]) - @test issetequal(arguments(ModelingToolkit.unwrap(NH4)), [x]) - @test issetequal(arguments(ModelingToolkit.unwrap(H2O)), [t, x]) - @test issetequal(arguments(ModelingToolkit.unwrap(PH4)), [t, x]) - @test issetequal(arguments(ModelingToolkit.unwrap(SO2)), [t, x, y]) -end - -### Other Minor Tests ### - -# Test base functionality in two cases. +# Simple test case 1. let @species C(t) H(t) O(t) @compound C6H12O2 ~ 6C + 12H + 2O @@ -106,6 +84,7 @@ let @test all(!iscompound(i) for i in components(C6H12O2)) end +# Simple test case 2. let @species O(t) @compound O2 ~ 2O @@ -120,37 +99,38 @@ let @test all(!iscompound(i) for i in components(O2)) end -# Checks that compounds cannot be created from non-existing species. -let - @species C(t) H(t) - @test_throws Exception @compound C6H12O2 ~ 6C + 12H + 2O -end -let - @test_throws Exception @compound O2 ~ 2O -end +### Independent Variables ### -# Checks that nested components works as expected. +# Test using different independent variable combinations. let - @species C(t) H(t) O(t) - @compound OH ~ 1O + 1H - @compound C3H5OH3 ~ 3C + 5H + 3OH - - @test !iscompound(O) - @test !iscompound(H) - @test iscompound(OH) - @test iscompound(C3H5OH3) - @test !(all(!iscompound(i) for i in components(C3H5OH3))) - - @test !iscompound(components(C3H5OH3)[1]) - @test !iscompound(components(C3H5OH3)[2]) - @test iscompound(components(C3H5OH3)[3]) + @variables x y z + @species C(t) H(x) N(x) O(t) P(t,x) S(x,y) - @test isequal([C, H, OH], components(C3H5OH3)) - @test isequal([O, H], components(components(C3H5OH3)[3])) - @test isequal([3, 5, 3], coefficients(C3H5OH3)) + # Checks that wrong (or absent) independent variable produces errors. + @test_throws Exception @eval @compound CO2(t,x) ~ C + 2O + @test_throws Exception @eval @compound (NH4(s), [output=true]) ~ N + 4H + @test_throws Exception @eval @compound (H2O = 2.0) ~ 2H + O + @test_throws Exception @eval @compound PH4(x) ~ P + 4H + @test_throws Exception @eval @compound SO2(t,y) ~ S + 2O + + # Creates compounds. + @compound CO2 ~ C + 2O + @compound (NH4, [output=true]) ~ N + 4H + @compound (H2O(t,x) = 2.0) ~ 2H + O + @compound PH4(t,x) ~ P + 4H + @compound SO2(t,x,y) ~ S + 2O + + # Checks they have the correct independent variables. + @test issetequal(arguments(ModelingToolkit.unwrap(CO2)), [t]) + @test issetequal(arguments(ModelingToolkit.unwrap(NH4)), [x]) + @test issetequal(arguments(ModelingToolkit.unwrap(H2O)), [t, x]) + @test issetequal(arguments(ModelingToolkit.unwrap(PH4)), [t, x]) + @test issetequal(arguments(ModelingToolkit.unwrap(SO2)), [t, x, y]) end -# Checks that interpolation works. +### Interpolation Tests ### + +# Case 1. let @species C(t) H(t) O(t) s = C @@ -165,6 +145,7 @@ let @test isequal(component_coefficients(C6H12O2_1), component_coefficients(C6H12O2_2)) end +# Case 2. let @species C(t) H(t) @compound Cyclopentadiene ~ 5C + 6H @@ -179,6 +160,7 @@ let @test isequal(coefficients(C10H12)[1], 2) end +# Case 3. let @species H(t) @@ -202,6 +184,7 @@ let @test isequal(coefficients(H2_3),coefficients(H2_4)) end +# Case 4. let @parameters alpha = 2 @species H(t) @@ -216,6 +199,7 @@ let @test isequal(coefficients(H2_1), @parameters alpha = 2) end +# Case 5. let @species A(t) B = A @@ -232,7 +216,7 @@ end ### Check @compounds Macro ### -# Basic syntax. +# Basic @compounds syntax. let @species C(t) H(t) O(t) @compound OH ~ 1O + 1H @@ -254,7 +238,7 @@ let @test isequal(component_coefficients(C3H5OH3), component_coefficients(C3H5OH3_alt)) end -# Interpolation +# Interpolation in @compounds. let @species s1(t) s2(t) s3(t) s2_alt = s2 @@ -273,6 +257,36 @@ let @test isequal(component_coefficients(comp), component_coefficients(comp_alt)) end +### Other Tests ### + +# Checks that compounds cannot be created from non-existing species. +let + @species C(t) H(t) + @test_throws Exception @compound C6H12O2 ~ 6C + 12H + 2O + @test_throws Exception @compound O2 ~ 2O +end + +# Checks that nested components works as expected. +let + @species C(t) H(t) O(t) + @compound OH ~ 1O + 1H + @compound C3H5OH3 ~ 3C + 5H + 3OH + + @test !iscompound(O) + @test !iscompound(H) + @test iscompound(OH) + @test iscompound(C3H5OH3) + @test !(all(!iscompound(i) for i in components(C3H5OH3))) + + @test !iscompound(components(C3H5OH3)[1]) + @test !iscompound(components(C3H5OH3)[2]) + @test iscompound(components(C3H5OH3)[3]) + + @test isequal([C, H, OH], components(C3H5OH3)) + @test isequal([O, H], components(components(C3H5OH3)[3])) + @test isequal([3, 5, 3], coefficients(C3H5OH3)) +end + ### Compounds in DSL ### # Checks with a single compound. diff --git a/test/miscellaneous_tests/events.jl b/test/miscellaneous_tests/events.jl index 5c019d5a16..776e2aa13b 100644 --- a/test/miscellaneous_tests/events.jl +++ b/test/miscellaneous_tests/events.jl @@ -1,9 +1,17 @@ -using Test, Catalyst, ModelingToolkit, OrdinaryDiffEq +### Prepares Tests ### + +# Fetch packages. +using Catalyst, OrdinaryDiffEq, Test + +# Sets the default `t` and `D` to use. t = default_t() D = default_time_deriv() +### Basic Tests ### + # Test discrete event is propagated to ODE solver correctly. let + # Creates model. @variables V(t)=1.0 eqs = [D(V) ~ V] discrete_events = [1.0 => [V ~ 1.0]] @@ -11,16 +19,19 @@ let @named rs = ReactionSystem([rxs; eqs], t; discrete_events) @test length(ModelingToolkit.discrete_events(rs)) == 1 @test length(ModelingToolkit.continuous_events(rs)) == 0 + + # Tests in simulation. setdefaults!(rs, [:A => 0.0]) osys = convert(ODESystem, rs) @test length(ModelingToolkit.discrete_events(osys)) == 1 oprob = ODEProblem(osys, [], (0.0, 20.0)) sol = solve(oprob, Tsit5()) - @test isapprox(sol(10 + 10 * eps(), idxs = V), 1.0) + @test sol(10 + 10 * eps(), idxs = V) ≈ 1.0 end # Test continuous event is propagated to the ODE solver. let + # Creates model. @parameters α=5.0 β=1.0 @species V(t) = 0.0 rxs = [Reaction(α, nothing, [V]), Reaction(β, [V], nothing)] @@ -28,9 +39,11 @@ let @named rs = ReactionSystem(rxs, t; continuous_events) @test length(ModelingToolkit.discrete_events(rs)) == 0 @test length(ModelingToolkit.continuous_events(rs)) == 1 + + # Tests in simulation. osys = convert(ODESystem, rs) @test length(ModelingToolkit.continuous_events(osys)) == 1 oprob = ODEProblem(rs, [], (0.0, 20.0)) sol = solve(oprob, Tsit5()) - @test isapprox(sol(20.0, idxs = V), 2.5) + @test sol(20.0, idxs = V) ≈ 2.5 end diff --git a/test/miscellaneous_tests/nonlinear_solve.jl b/test/miscellaneous_tests/nonlinear_solve.jl index b59790df4c..c2040bc1c4 100644 --- a/test/miscellaneous_tests/nonlinear_solve.jl +++ b/test/miscellaneous_tests/nonlinear_solve.jl @@ -1,14 +1,14 @@ -### Fetch Packages ### +### Prepares Tests ### # Fetch packages. using Catalyst, NonlinearSolve, OrdinaryDiffEq, SteadyStateDiffEq using Random, Test -# Sets rnd number. +# Sets stable rng number. using StableRNGs rng = StableRNG(12345) -# Fetch test networks and functions. +# Fetch test functions. include("../test_functions.jl") ### Run Tests ### @@ -98,7 +98,6 @@ let sol2 = solve(nl_prob_2, DynamicSS(Rosenbrock23()); abstol=1e-12, reltol=1e-12) # Checks output using the ODE's drift function - nfunc = NonlinearFunction(convert(NonlinearSystem, steady_state_network_3)) @test f_eval([:X => sol1[X], :Y => sol1[Y], :Y2 => sol1[Y2], :XY2 => sol1[XY2]], p, 0.0) ≈ [0.0, 0.0, 0.0, 0.0] atol=1e-10 @test f_eval([:X => sol2[X], :Y => sol2[Y], :Y2 => sol2[Y2], :XY2 => sol2[XY2]], p, 0.0) ≈ [0.0, 0.0, 0.0, 0.0] atol=1e-10 end \ No newline at end of file diff --git a/test/miscellaneous_tests/reaction_balancing.jl b/test/miscellaneous_tests/reaction_balancing.jl index 1bd0e656db..9ac2052bbd 100644 --- a/test/miscellaneous_tests/reaction_balancing.jl +++ b/test/miscellaneous_tests/reaction_balancing.jl @@ -1,7 +1,14 @@ +### Prepares Tests ### + +# Fetch packages. using Catalyst, Test + +# Sets the default `t` to use. t = default_t() -#Check that balancing works. +### Basic Tests ### + +# Functionality tests (a). let @parameters k @species H(t) O(t) @@ -26,6 +33,7 @@ let @test isequal(brxs, brxs_macro) end +# Functionality tests (a). let @parameters k @species C(t) H(t) O(t) @@ -49,6 +57,9 @@ let @test isequal(brxs, brxs_macro) end + +### Test Across Various Reactions ### + # @reaction k, H2O --> H2O let @species H(t) O(t) @@ -351,7 +362,9 @@ let @test isequal(balanced_rx, first(brxs)) end -# Infinite solutions +### Special Cases ### + +# Reaction with infinite solutions. let @species C(t) H(t) O(t) @compound CO ~ C + O @@ -366,7 +379,7 @@ let @test length(brxs) == 2 end -# No way to balance +# Reaction that cannot be balanced. let @species Fe(t) S(t) O(t) H(t) N(t) @@ -382,7 +395,7 @@ let @test isempty(brxs) end -# test errors on compounds of compounds +# Test that balancing with compounds of compounds yields an error. let @species C(t) H(t) O(t) @compound CO ~ C + O diff --git a/test/miscellaneous_tests/symbolic_stoichiometry.jl b/test/miscellaneous_tests/symbolic_stoichiometry.jl index 3079e989a4..60df395937 100644 --- a/test/miscellaneous_tests/symbolic_stoichiometry.jl +++ b/test/miscellaneous_tests/symbolic_stoichiometry.jl @@ -1,4 +1,9 @@ -using Catalyst, OrdinaryDiffEq, Test, LinearAlgebra, JumpProcesses +### Prepares Tests ### + +# Fetch packages. +using Catalyst, JumpProcesses, LinearAlgebra, OrdinaryDiffEq, Test + +# Sets the default `t` to use. t = default_t() ### Base Tests ### diff --git a/test/model_simulation/make_jacobian.jl b/test/model_simulation/make_jacobian.jl index 022e384fd2..2c56f5f044 100644 --- a/test/model_simulation/make_jacobian.jl +++ b/test/model_simulation/make_jacobian.jl @@ -1,6 +1,9 @@ -### Fetch Packages and Reaction Networks ### -using Catalyst, DiffEqBase, Random, Test +### Prepares Tests ### +# Fetch packages. +using Catalyst, DiffEqBase, Test + +# Sets stable rng number. using StableRNGs rng = StableRNG(12345) diff --git a/test/model_simulation/simulate_ODEs.jl b/test/model_simulation/simulate_ODEs.jl index 63411eacd9..7296498408 100644 --- a/test/model_simulation/simulate_ODEs.jl +++ b/test/model_simulation/simulate_ODEs.jl @@ -1,22 +1,23 @@ -### Fetch Packages and Reaction Networks ### +### Prepares Tests ### # Fetch packages. -using Catalyst, OrdinaryDiffEq, Random, Test -using ModelingToolkit: get_unknowns, get_ps +using Catalyst, OrdinaryDiffEq, Test -# Sets rnd number. +# Sets stable rng number. using StableRNGs rng = StableRNG(12345) -# Fetch test networks and functions. -include("../test_networks.jl") +# Fetch test functions and networks. include("../test_functions.jl") +include("../test_networks.jl") ### Compares to Known Solution ### # Exponential decay, should be identical to the (known) analytical solution. let - exponential_decay = @reaction_network begin d, X → ∅ end + exponential_decay = @reaction_network begin + d, X → ∅ + end for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2] u0 = rnd_u0(exponential_decay, rng; factor) @@ -52,10 +53,13 @@ let end end -### Compares to Known ODE Function ### - +# Compares simulations generated through Catalyst with those generated by manually created functions. let - identical_networks_1 = Vector{Pair}() + # Manually declares ODEs to compare Catalyst-generated simulations to. + catalyst_networks = [] + manual_networks = [] + u0_syms = [] + ps_syms = [] function real_functions_1(du, u, p, t) X1, X2, X3 = u @@ -65,7 +69,10 @@ let du[3] = p3 + 2 * k1 * X2 - 2 * k2 * X1 * X3^2 / factorial(2) + k3 * X1 - k4 * X3 - d3 * X3 end - push!(identical_networks_1, reaction_networks_standard[1] => real_functions_1) + push!(catalyst_networks, reaction_networks_standard[1]) + push!(manual_networks, real_functions_1) + push!(u0_syms, [:X1, :X2, :X3]) + push!(ps_syms, [:p1, :p2, :p3, :k1, :k2, :k3, :k4, :d1, :d2, :d3]) function real_functions_2(du, u, p, t) X1, X2 = u @@ -73,7 +80,10 @@ let du[1] = v1 * K1 / (K1 + X2) - d * X1 * X2 du[2] = v2 * X1 / (K2 + X1) - d * X1 * X2 end - push!(identical_networks_1, reaction_networks_standard[2] => real_functions_2) + push!(catalyst_networks, reaction_networks_standard[2]) + push!(manual_networks, real_functions_2) + push!(u0_syms, [:X1, :X2]) + push!(ps_syms, [:v1, :K1, :v2, :K2, :d]) function real_functions_3(du, u, p, t) X1, X2, X3 = u @@ -82,7 +92,10 @@ let du[2] = v2 * K2^n2 / (K2^n2 + X1^n2) - d2 * X2 du[3] = v3 * K3^n3 / (K3^n3 + X2^n3) - d3 * X3 end - push!(identical_networks_1, reaction_networks_hill[2] => real_functions_3) + push!(catalyst_networks, reaction_networks_hill[2]) + push!(manual_networks, real_functions_3) + push!(u0_syms, [:X1, :X2, :X3]) + push!(ps_syms, [:v1, :v2, :v3, :K1, :K2, :K3, :n1, :n2, :n3, :d1, :d2, :d3]) function real_functions_4(du, u, p, t) X1, X2, X3 = u @@ -91,7 +104,10 @@ let du[2] = -k3 * X2 + k4 * X3 + k1 * X1 - k2 * X2 du[3] = -k5 * X3 + k6 * X1 + k3 * X2 - k4 * X3 end - push!(identical_networks_1, reaction_networks_constraint[1] => real_functions_4) + push!(catalyst_networks, reaction_networks_constraint[1]) + push!(manual_networks, real_functions_4) + push!(u0_syms, [:X1, :X2, :X3]) + push!(ps_syms, [:k1, :k2, :k3, :k4, :k5, :k6]) function real_functions_5(du, u, p, t) X, Y, Z = u @@ -100,37 +116,47 @@ let du[2] = k2 * log(12 + X) * X - k3 * log(3 + Y) * Y du[3] = k3 * log(3 + Y) * Y - log(5, 6 + k4) * Z end - push!(identical_networks_1, reaction_networks_weird[2] => real_functions_5) + push!(catalyst_networks, reaction_networks_weird[2]) + push!(manual_networks, real_functions_5) + push!(u0_syms, [:X, :Y, :Z]) + push!(ps_syms, [:k1, :k2, :k3, :k4]) - for (i, networks) in enumerate(identical_networks_1) + for (rn_catalyst, rn_manual, u0_sym, ps_sym) in zip(catalyst_networks, manual_networks, u0_syms, ps_syms) for factor in [1e-2, 1e-1, 1e0, 1e1] - u0_1 = rnd_u0(networks[1], rng; factor) - p_1 = rnd_ps(networks[1], rng; factor) - (i == 3) && (p_1 =rnd_ps_Int64(networks[1], rng)) - u0_2 = last.(u0_1) - p_2 = last.(p_1) - - prob1 = ODEProblem(networks[1], u0_1, (0.0, 10000.0), p_1) - prob2 = ODEProblem(networks[2], u0_2, (0.0, 10000.0), p_2) - sol1 = solve(prob1, Rosenbrock23(); abstol = 1e-10, reltol = 1e-10, saveat = 1.0) - sol2 = solve(prob2, Rosenbrock23(); abstol = 1e-10, reltol = 1e-10, saveat = 1.0) - @test sol1.u ≈ sol2.u + # Set input values. + u0_1 = rnd_u0(rn_catalyst, rng; factor = factor) + ps_1 = rnd_ps(rn_catalyst, rng; factor = factor) + (nameof(rn_catalyst) == :rnh2) && (p_1 = rnd_ps_Int64(rn_catalyst, rng)) + u0_2 = map_to_vec(u0_1, u0_sym) + ps_2 = map_to_vec(ps_1, ps_sym) + + # Check drift functions. + dt = zeros(length(u0_1)) + rn_manual(dt, u0_2, ps_2, 0.0) + @test dt ≈ f_eval(rn_catalyst, u0_1, ps_1, 0.0) + + # Compares that simulations are identical. + oprob_1 = ODEProblem(rn_catalyst, u0_1, (0.0, 100.0), ps_1) + oprob_2 = ODEProblem(rn_manual, u0_2, (0.0, 100.0), ps_2) + sol1 = solve(oprob_1, Rosenbrock23()) + sol2 = solve(oprob_2, Rosenbrock23()) + @test sol1[u0_sym] ≈ sol2.u end end end ### Checks Simulations Don't Error ### let - for (i, network) in enumerate(reaction_networks_all) + for (i, rn) in enumerate(reaction_networks_all) for factor in [1e-1, 1e0, 1e1] - u0 = rnd_u0(network, rng; factor) - #If parameter in exponent, want to avoid possibility of (-small u)^(decimal). Also avoid large exponents. + u0 = rnd_u0(rn, rng; factor) + # If parameter in exponent, this avoids potential (-small u)^(decimal) and large exponents. if in(i, [[11:20...]..., 34, 37, 42]) - p = rnd_ps(network, rng) + ps = rnd_ps(rn, rng) else - p = rnd_ps(network, rng; factor) + ps = rnd_ps(rn, rng; factor) end - prob = ODEProblem(network, u0, (0.0, 1.0), p) + prob = ODEProblem(rn, u0, (0.0, 1.0), ps) @test SciMLBase.successful_retcode(solve(prob, Rosenbrock23())) end end @@ -138,9 +164,11 @@ end ### Other Tests ### -# No parameter test. +# Tests simulating a network without parameters. let - no_param_network = @reaction_network begin (1.5, 2), ∅ ↔ X end + no_param_network = @reaction_network begin + (1.5, 2), ∅ ↔ X + end for factor in [1e0, 1e1, 1e2] u0 = rnd_u0(no_param_network, rng; factor) prob = ODEProblem(no_param_network, u0, (0.0, 1000.0)) @@ -151,7 +179,7 @@ end # Test solving with floating point stoichiometry. let - # Prepare model. + # Prepare model with/without Catalyst. function oderhs(du, u, p, t) du[1] = -2.5 * p[1] * u[1]^2.5 du[2] = 3 * p[1] * u[1]^2.5 @@ -160,14 +188,16 @@ let rn = @reaction_network begin k, 2.5 * A --> 3 * B end - u = rnd_u0(rn, rng) + u_1 = rnd_u0(rn, rng) + p_1 = [:k => 1.0] + u_2 = map_to_vec(u_1, [:A, :B]) + p_2 = map_to_vec(p_1, [:k]) tspan = (0.0, 1.0) - p = [:k => 1.0] # Check equivalence. du1 = du2 = zeros(2) - oprob = ODEProblem(rn, u, tspan, p; combinatoric_ratelaws = false) + oprob = ODEProblem(rn, u_1, tspan, p_1; combinatoric_ratelaws = false) oprob.f(du1, oprob.u0, oprob.p, 90.0) - oderhs(du2, last.(u), last.(p), 0.0) + oderhs(du2, u_2, p_2, 0.0) @test du1 ≈ du2 end \ No newline at end of file diff --git a/test/model_simulation/simulate_SDEs.jl b/test/model_simulation/simulate_SDEs.jl index cf9de307d7..58a614a0de 100644 --- a/test/model_simulation/simulate_SDEs.jl +++ b/test/model_simulation/simulate_SDEs.jl @@ -1,21 +1,26 @@ -### Fetch Packages and Reaction Networks ### +### Prepares Tests ### # Fetch packages. -using Catalyst, Random, Statistics, StochasticDiffEq, Test -using ModelingToolkit: get_postprocess_fbody +using Catalyst, Statistics, StochasticDiffEq, Test -# Sets rnd number. +# Sets stable rng number. using StableRNGs rng = StableRNG(12345) -# Fetch test networks. -include("../test_networks.jl") +# Fetch test functions and networks. include("../test_functions.jl") +include("../test_networks.jl") + -### Compares to Manually Computed Function ### +### Basic Tests ### +# Compares simulations generated through Catalyst with those generated by manually created functions. let - identical_networks = Vector{Pair}() + # Manually declares SDEs to compare Catalyst-generated simulations to. + catalyst_networks = [] + manual_networks = [] + u0_syms = [] + ps_syms = [] function real_f_1(du, u, p, t) X1, X2, X3 = u @@ -43,8 +48,10 @@ let du[3, 4] = sqrt(k3 * X2) du[3, 5] = -sqrt(d * X3) end - push!(identical_networks, - reaction_networks_standard[8] => (real_f_1, real_g_1, zeros(3, 5))) + push!(catalyst_networks, reaction_networks_standard[8]) + push!(manual_networks, (f = real_f_1, g = real_g_1, nrp = zeros(3, 5))) + push!(u0_syms, [:X1, :X2, :X3]) + push!(ps_syms, [:p, :k1, :k2, :k3, :d]) function real_f_2(du, u, p, t) X1, = u @@ -57,8 +64,10 @@ let du[1, 1] = sqrt(v / 10 + v * X1^n / (X1^n + K^n)) du[1, 2] = -sqrt(d * X1) end - push!(identical_networks, - reaction_networks_hill[6] => (real_f_2, real_g_2, zeros(1, 2))) + push!(catalyst_networks, reaction_networks_hill[6]) + push!(manual_networks, (f = real_f_2, g = real_g_2, nrp = zeros(1, 2))) + push!(u0_syms, [:X1]) + push!(ps_syms, [:v, :K, :n, :d]) function real_f_3(du, u, p, t) X1, X2, X3, X4, X5, X6, X7 = u @@ -94,45 +103,88 @@ let du[7, 5] = sqrt(k5 * X5 * X6) du[7, 6] = -sqrt(k6 * X7) end - push!(identical_networks, - reaction_networks_constraint[9] => (real_f_3, real_g_3, zeros(7, 6))) + push!(catalyst_networks, reaction_networks_constraint[9]) + push!(manual_networks, (f = real_f_3, g = real_g_3, nrp = zeros(7, 6))) + push!(u0_syms, [:X1, :X2, :X3, :X4, :X5, :X6, :X7]) + push!(ps_syms, [:k1, :k2, :k3, :k4, :k5, :k6]) - for (i, networks) in enumerate(identical_networks) - for factor in [1e-1, 1e0, 1e1], repeat in 1:3 + for (rn_catalyst, rn_manual, u0_sym, ps_sym) in zip(catalyst_networks, manual_networks, u0_syms, ps_syms) + for factor in [1e-1, 1e0], repeat in 1:3 # Set input values. - u0_1 = rnd_u0(networks[1], rng; factor, min = 100.0) - ps_1 = rnd_ps(networks[1], rng; factor, min = 0.01) - u0_2 = last.(u0_1) - ps_2 = last.(ps_1) + u0_1 = rnd_u0(rn_catalyst, rng; factor, min = 100.0) + ps_1 = rnd_ps(rn_catalyst, rng; factor, min = 0.01) + u0_2 = map_to_vec(u0_1, u0_sym) + ps_2 = map_to_vec(ps_1, ps_sym) # Check drift functions. - dt = zeros(length(unknowns(networks[1]))) - networks[2][1](dt, u0_2, ps_2, 0.0) - @test dt ≈ f_eval(networks[1], u0_1, ps_1, 0.0) + dt = zeros(length(u0_1)) + rn_manual.f(dt, u0_2, ps_2, 0.0) + @test dt ≈ f_eval(rn_catalyst, u0_1, ps_1, 0.0) # Check diffusion functions. - duW = zeros(length(unknowns(networks[1])), length(reactions(networks[1]))) - networks[2][2](duW, u0_2, ps_2, 0.0) - @test duW ≈ g_eval(networks[1], u0_1, ps_1, 0.0) + duW = zeros(size(rn_manual.nrp)) + rn_manual.g(duW, u0_2, ps_2, 0.0) + @test duW ≈ g_eval(rn_catalyst, u0_1, ps_1, 0.0) + + # Compares simulation with identical seed. + # Cannot test the third network (requires very fiddly parameters for CLE to be defined). + if nameof(rn_catalyst) != :rnc9 + sprob_1 = SDEProblem(rn_catalyst, u0_1, (0.0, 1.0), ps_1) + sprob_2 = SDEProblem(rn_manual.f, rn_manual.g, u0_2, (0.0, 1.0), ps_2; noise_rate_prototype = rn_manual.nrp) + sol1 = solve(sprob_1, ImplicitEM(); seed = 1234) + sol2 = solve(sprob_2, ImplicitEM(); seed = 1234) + @test sol1[u0_sym] ≈ sol2.u + end end end end -### Checks Simulations Don't Error ### - -# Tries to create a large number of problem, ensuring there are no errors (cannot solve as solution likely to go into negatives). +# Checks that simulations for a large number of potential systems are completed (and don't error). +# (cannot solve as solution likely to go into negatives). let - for network in reaction_networks_all - for factor in [1e-2, 1e-1, 1e0, 1e1] - u0 = rnd_u0(network, rng; factor) - ps = rnd_ps(network, rng) - prob = SDEProblem(network, u0, (0.0, 1.0), ps) - end + for rn in reaction_networks_all + u0 = rnd_u0(rn, rng) + ps = rnd_ps(rn, rng) + sprob = SDEProblem(rn, u0, (0.0, 1.0), ps) + init(sprob, ImplicitEM()) end end ### Noise Scaling ### +# Compares noise scaling with manually declared noise function. +let + function real_g_3(du, u, P, t) + X1, X2 = u + η1, η2, p, k1, k2, d = P + fill!(du, 0) + du[1, 1] = η1 * sqrt(p) + du[1, 2] = - η2 * sqrt(k1 * X1) + du[1, 3] = (2 * η2 + 1) * sqrt(k2 * X2) + du[1, 4] = 0.0 + du[2, 1] = 0.0 + du[2, 2] = η2 * sqrt(k1 * X1) + du[2, 3] = - (2 * η2 + 1) * sqrt(k2 * X2) + du[2, 4] = 0.0 + return du + end + + noise_scaling_network = @reaction_network begin + @parameters η1 η2 + @default_noise_scaling η1 + p, 0 --> X1 + (k1, k2), X1 ↔ X2, ([noise_scaling=η2],[noise_scaling=2 * η2 + 1]) + d, X2 --> 0, [noise_scaling = 0.0] + end + + u0_1 = rnd_u0(noise_scaling_network, rng) + ps_1 = rnd_ps(noise_scaling_network, rng) + u0_2 = map_to_vec(u0_1, [:X1, :X2]) + ps_2 = map_to_vec(ps_1, [:η1, :η2, :p, :k1, :k2, :d]) + + @test g_eval(noise_scaling_network, u0_1, ps_1, 0.0) == real_g_3(zeros(2, 4), u0_2, ps_2, 0.0) +end + # Tests with multiple noise scaling parameters directly in the macro. let noise_scaling_network_1 = @reaction_network begin @@ -287,7 +339,7 @@ end ### Other Tests ### -# No parameter test. +# Tests simulating a network without parameters. let no_param_network = @reaction_network begin (1.2, 5), X1 ↔ X2 end for factor in [1e3, 1e4] diff --git a/test/model_simulation/simulate_jumps.jl b/test/model_simulation/simulate_jumps.jl index 28d90e0812..2f8adb66cd 100644 --- a/test/model_simulation/simulate_jumps.jl +++ b/test/model_simulation/simulate_jumps.jl @@ -1,20 +1,26 @@ -### Fetch Packages and Reaction Networks ### +### Prepares Tests ### # Fetch packages. -using Catalyst, JumpProcesses, Random, Statistics, Test, SciMLBase -using ModelingToolkit: get_unknowns, get_ps +using Catalyst, JumpProcesses, Statistics, Test -# Sets rnd number. +# Sets stable rng number. using StableRNGs rng = StableRNG(12345) -# Fetch test networks. +# Fetch test functions and networks. +include("../test_functions.jl") include("../test_networks.jl") -### Compares to Manually Computed Function ### +### Basic Tests ### + +# Compares jump simulations generated through hCatalyst, and manually created systems. let - identical_networks = Vector{Pair}() + # Manually declares jumps to compare Catalyst-generated jump simulations to. + catalyst_networks = [] + manual_networks = [] + u0_syms = [] + ps_syms = [] rate_1_1(u, p, t) = p[1] rate_1_2(u, p, t) = p[2] * u[1] @@ -42,9 +48,12 @@ let jump_1_8 = ConstantRateJump(rate_1_8, affect_1_8!) jumps_1 = (jump_1_1, jump_1_2, jump_1_3, jump_1_4, jump_1_5, jump_1_6, jump_1_7, jump_1_8) - push!(identical_networks, reaction_networks_standard[6] => jumps_1) + push!(catalyst_networks, reaction_networks_standard[5]) + push!(manual_networks, jumps_1) + push!(u0_syms, [:X1, :X2, :X3, :X4]) + push!(ps_syms, [:p, :k1, :k2, :k3, :k4, :k5, :k6, :d]) - rate_2_1(u, p, t) = p[1] / 10 + u[1]^p[3] / (u[1]^p[3] + p[2]^p[3]) + rate_2_1(u, p, t) = p[1] / 10 + p[1] * (u[1]^p[3]) / (u[1]^p[3] + p[2]^p[3]) rate_2_2(u, p, t) = p[4] * u[1] * u[2] rate_2_3(u, p, t) = p[5] * u[3] rate_2_4(u, p, t) = p[6] * u[3] @@ -70,7 +79,10 @@ let jump_2_6 = ConstantRateJump(rate_2_6, affect_2_6!) jump_2_7 = ConstantRateJump(rate_2_7, affect_2_7!) jumps_2 = (jump_2_1, jump_2_2, jump_2_3, jump_2_4, jump_2_5, jump_2_6, jump_2_7) - push!(identical_networks, reaction_networks_hill[7] => jumps_2) + push!(catalyst_networks, reaction_networks_hill[7]) + push!(manual_networks, jumps_2) + push!(u0_syms, [:X1, :X2, :X3]) + push!(ps_syms, [:v, :K, :n, :k1, :k2, :k3, :d]) rate_3_1(u, p, t) = p[1] * binomial(u[1], 1) rate_3_2(u, p, t) = p[2] * binomial(u[2], 2) @@ -91,56 +103,60 @@ let jump_3_5 = ConstantRateJump(rate_3_5, affect_3_5!) jump_3_6 = ConstantRateJump(rate_3_6, affect_3_6!) jumps_3 = (jump_3_1, jump_3_2, jump_3_3, jump_3_4, jump_3_5, jump_3_6) - push!(identical_networks, reaction_networks_constraint[5] => jumps_3) + push!(catalyst_networks, reaction_networks_constraint[5]) + push!(manual_networks, jumps_3) + push!(u0_syms, [:X1, :X2, :X3, :X4]) + push!(ps_syms, [:k1, :k2, :k3, :k4, :k5, :k6]) + + # Loops through all cases, checks that identical simulations are generated with/without Catalyst. + for (rn_catalyst, rn_manual, u0_sym, ps_sym) in zip(catalyst_networks, manual_networks, u0_syms, ps_syms) + for factor in [5, 50] + u0_1 = rnd_u0_Int64(rn_catalyst, rng; n = factor) + ps_1 = rnd_ps(rn_catalyst, rng; factor = factor/100.0) + dprob_1 = DiscreteProblem(rn_catalyst, u0_1, (0.0, 100.0), ps_1) + jprob_1 = JumpProblem(rn_catalyst, dprob_1, Direct(); rng) + sol1 = solve(jprob_1, SSAStepper(); seed = 1234, saveat = 1.0) + + u0_2 = map_to_vec(u0_1, u0_sym) + ps_2 = map_to_vec(ps_1, ps_sym) + dprob_2 = DiscreteProblem(u0_2, (0.0, 100.0), ps_2) + jprob_2 = JumpProblem(dprob_2, Direct(), rn_manual...; rng) + sol2 = solve(jprob_2, SSAStepper(); seed = 1234, saveat = 1.0) - for (i, networks) in enumerate(identical_networks) - for factor in [1e-2, 1e-1, 1e0, 1e1] - (i == 3) && (factor > 1e-1) && continue # Large numbers seems to crash it. - u0 = rand(rng, 1:Int64(factor * 100), length(get_unknowns(networks[1]))) - p = factor * rand(rng, length(get_ps(networks[1]))) - prob1 = JumpProblem(networks[1], - DiscreteProblem(networks[1], u0, (0.0, 1000.0), p), - Direct()) - sol1 = solve(prob1, SSAStepper()) - prob2 = JumpProblem(DiscreteProblem(u0, (0.0, 1000.0), p), Direct(), - networks[2]...) - sol2 = solve(prob2, SSAStepper()) - for i in 1:length(u0) - vals1 = getindex.(sol1.u, i) - vals2 = getindex.(sol1.u, i) - (mean(vals2) > 0.001) && @test 0.8 < mean(vals1) / mean(vals2) < 1.25 - (std(vals2) > 0.001) && @test 0.8 < std(vals1) / std(vals2) < 1.25 + if nameof(rn_catalyst) == :rnh7 + # Have spent a few hours figuring this one out. For certain seeds it actually works, + # but others not. This feels weird, and I didn't get any longer. I tried using non-random + # parameters/initial conditions, and removing the non-hill function reactions. Problem + # still persists. + @test_broken sol1[u0_sym] == sol2.u + else + @test sol1[u0_sym] == sol2.u end end end end -### Checks Simulations Don't Error ### - +# Checks that simulations for a large number of potential systems are completed (and don't error). let - for network in reaction_networks_all - for factor in [1e0] - u0 = rand(rng, 1:Int64(factor * 100), length(get_unknowns(network))) - p = factor * rand(rng, length(get_ps(network))) - prob = JumpProblem(network, DiscreteProblem(network, u0, (0.0, 1.0), p), - Direct()) - @test SciMLBase.successful_retcode(solve(prob, SSAStepper())) - end + for rn in reaction_networks_all + u0 = rnd_u0_Int64(rn, rng) + ps = rnd_ps(rn, rng) + dprob = DiscreteProblem(rn, u0, (0.0, 1.0), ps) + jprob = JumpProblem(rn, dprob, Direct(); rng) + @test SciMLBase.successful_retcode(solve(jprob, SSAStepper(); seed = 1234)) end end ### Other Tests ### -# No parameter test. +# Tests simulating a network without parameters. let - no_param_network = @reaction_network begin (1.2, 5), X1 ↔ X2 end - for factor in [1e1] - u0 = rand(rng, 1:Int64(factor * 100), length(get_unknowns(no_param_network))) - prob = JumpProblem(no_param_network, - DiscreteProblem(no_param_network, u0, (0.0, 1000.0)), Direct()) - sol = solve(prob, SSAStepper()) - vals1 = getindex.(sol.u[1:end], 1) - vals2 = getindex.(sol.u[1:end], 2) - @test mean(vals1) > mean(vals2) + no_param_network = @reaction_network begin + (1.2, 5), X1 ↔ X2 end + u0 = rnd_u0_Int64(no_param_network, rng) + dprob = DiscreteProblem(no_param_network, u0, (0.0, 1000.0)) + jprob = JumpProblem(no_param_network, dprob, Direct(); rng) + sol = solve(jprob, SSAStepper(); seed = 1234) + @test mean(sol[:X1]) > mean(sol[:X2]) end diff --git a/test/network_analysis/conservation_laws.jl b/test/network_analysis/conservation_laws.jl index 8abbedfebd..0a4ab1301a 100644 --- a/test/network_analysis/conservation_laws.jl +++ b/test/network_analysis/conservation_laws.jl @@ -1,11 +1,14 @@ -### Fetch Packages and Reaction Networks ### -using Catalyst, Test -using LinearAlgebra +### Prepares Tests ### +# Fetch packages. +using Catalyst, LinearAlgebra, Test + +# Fetch test networks. include("../test_networks.jl") -### Run Tests ### +### Basic Tests ### +# Tests basic functionality on system with known conservation laws. let rn = @reaction_network begin (1, 2), A + B <--> C @@ -37,8 +40,7 @@ let @test any(D[j, :] == C[i, :] for i in 1:size(C, 1), j in 1:size(D, 1)) end -### Checks Test Networks - +# Tests conservation law computation on large number of networks where we know which have conservation laws. let Cs_standard = map(conservationlaws, reaction_networks_standard) @test all(size(C, 1) == 0 for C in Cs_standard) @@ -53,8 +55,7 @@ let @test all(consequiv.(Matrix{Int}.(Cs_constraint), reaction_network_constraints)) end -### Tests Additional Conservation Law Functions ### - +# Tests additional conservation law-related functions. let rn = @reaction_network begin (k1, k2), X1 <--> X2 diff --git a/test/network_analysis/network_properties.jl b/test/network_analysis/network_properties.jl index fcf476f8c1..0680aa9432 100644 --- a/test/network_analysis/network_properties.jl +++ b/test/network_analysis/network_properties.jl @@ -1,8 +1,11 @@ -#! format: off +### Prepares Tests ### -using Catalyst, Test +# Fetch packages. +using Catalyst, LinearAlgebra, Test -# Test on MAPK network. +### Basic Tests ### + +# Tests network analysis functions on MAPK network (by comparing to manually computed outputs). let MAPK = @reaction_network MAPK begin (k₁, k₂),KKK + E1 <--> KKKE1 @@ -52,7 +55,8 @@ let # end end -# Test on a network. + +# Tests network analysis functions on a second network (by comparing to manually computed outputs). let rn2 = @reaction_network begin (k₁, k₂), E + S1 <--> ES1 @@ -89,7 +93,8 @@ let # end end -# Test on a network. + +# Tests network analysis functions on third network (by comparing to manually computed outputs). let rn3 = @reaction_network begin (k₁, k₂), A11 <--> 0 diff --git a/test/programmatic_model_creation/component_based_model_creation.jl b/test/programmatic_model_creation/component_based_model_creation.jl index f1643ced77..095f347875 100644 --- a/test/programmatic_model_creation/component_based_model_creation.jl +++ b/test/programmatic_model_creation/component_based_model_creation.jl @@ -1,9 +1,15 @@ #! format: off -using ModelingToolkit, Catalyst, LinearAlgebra, OrdinaryDiffEq, Test -using SciMLNLSolve + +### Prepares Tests ### + +# Fetch packages. +using Catalyst, LinearAlgebra, OrdinaryDiffEq, SciMLNLSolve, Test using ModelingToolkit: nameof + +# Fetch test networks. t = default_t() + ### Run Tests ### # Repressilator model. diff --git a/test/programmatic_model_creation/programmatic_model_expansion.jl b/test/programmatic_model_creation/programmatic_model_expansion.jl index 7d63e174c8..11a3e0dcdf 100644 --- a/test/programmatic_model_creation/programmatic_model_expansion.jl +++ b/test/programmatic_model_creation/programmatic_model_expansion.jl @@ -1,25 +1,27 @@ #! format: off -### Fetch Packages, Reaction Networks, Declare Global Variables ### +### Prepares Tests ### # Fetch packages. using Catalyst, Test using ModelingToolkit: get_ps, get_unknowns, get_eqs, get_systems, get_iv, getname, nameof -t = default_t() -# Sets rnd number. +# Sets stable rng number. using StableRNGs rng = StableRNG(12345) +# Fetch test networks. +t = default_t() + # Fetch test networks. include("../test_networks.jl") -# Test Function +# Declares a helper function. function unpacksys(sys) get_eqs(sys), get_iv(sys), get_ps(sys), nameof(sys), get_systems(sys) end -### Run Tests ### +### Basic Tests ### # Tests construction of empty reaction networks. let @@ -47,6 +49,7 @@ let end # Tests accessing parameters and species added with network API. +# Should probably be removed if we remove mutating stuff? let empty_network_3 = @reaction_network begin @incomplete @@ -60,6 +63,7 @@ let end # Tests creating a network and adding reactions. +# This test seems weird? let unfinished_network = @reaction_network begin @parameters k0 k1 k2 k3 k4 @@ -81,6 +85,7 @@ let end # Compares test network to identical network constructed via @add_reactions. +# @add_reactions is getting deprecated though? let identical_networks = Vector{Pair}() diff --git a/test/reactionsystem_structure/higher_order_reactions.jl b/test/reactionsystem_structure/higher_order_reactions.jl index 4a4ca22d0e..ca2eb6c7e8 100644 --- a/test/reactionsystem_structure/higher_order_reactions.jl +++ b/test/reactionsystem_structure/higher_order_reactions.jl @@ -1,14 +1,19 @@ ### Fetch Packages and Set Global Variables ### +### Prepares Tests ### + # Fetch packages. using DiffEqBase, Catalyst, JumpProcesses, Random, Statistics, Test using ModelingToolkit: get_unknowns, get_ps -# Sets rnd number. +# Sets stable rng number. using StableRNGs rng = StableRNG(12345) -# Delare globaly used network. +# Fetch test functions. +include("../test_functions.jl") + +# Declares a network used throughout all tests. higher_order_network_1 = @reaction_network begin p, ∅ ⟼ X1 r1, 2X1 ⟼ 3X2 @@ -20,9 +25,9 @@ higher_order_network_1 = @reaction_network begin d, 2X10 ⟼ ∅ end -### Run Tests ### +### Basic Tests ### -# Tests that deterministic and stochastic differential functions are identical. +# Tests that ODE and SDE functions are correct (by comparing to network with manually written higher order rates). let higher_order_network_2 = @reaction_network begin p, ∅ ⟾ X1 @@ -36,21 +41,18 @@ let d * X10^2 / factorial(2), 2X10 ⟾ ∅ end - f1 = ODEFunction(convert(ODESystem, higher_order_network_1), jac = true) - f2 = ODEFunction(convert(ODESystem, higher_order_network_2), jac = true) - g1 = SDEFunction(convert(SDESystem, higher_order_network_1)) - g2 = SDEFunction(convert(SDESystem, higher_order_network_2)) - for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] - u0 = factor * rand(rng, length(get_unknowns(higher_order_network_1))) - p = factor * rand(rng, length(get_ps(higher_order_network_2))) + for factor in [1e-1, 1e0, 1e1, 1e2] + u0 = rnd_u0(higher_order_network_1, rng; factor) + ps = rnd_ps(higher_order_network_1, rng; factor) t = rand(rng) - @test all(abs.(f1(u0, p, t) .- f2(u0, p, t)) .< 100 * eps()) - @test all(abs.(f1.jac(u0, p, t) .- f2.jac(u0, p, t)) .< 100 * eps()) - @test all(abs.(g1(u0, p, t) .- g2(u0, p, t)) .< 100 * eps()) + + @test f_eval(higher_order_network_1, u0, ps, t) == f_eval(higher_order_network_2, u0, ps, t) + @test jac_eval(higher_order_network_1, u0, ps, t) == jac_eval(higher_order_network_2, u0, ps, t) + @test g_eval(higher_order_network_1, u0, ps, t) == g_eval(higher_order_network_2, u0, ps, t) end end -# Tests that the discrete jump systems are equal. +# Tests that Jump Systems are correct (by comparing to network with manually written higher order rates). # Currently fails because binomial only takes Int input (and X is Float64). # I see several solutions, but depends on whether we allow species to be specified as Int64. # I have marked this one as broken for now. diff --git a/test/reactionsystem_structure/reactions.jl b/test/reactionsystem_structure/reactions.jl index 65a67ed14d..df0ffc9830 100644 --- a/test/reactionsystem_structure/reactions.jl +++ b/test/reactionsystem_structure/reactions.jl @@ -74,7 +74,7 @@ let @test isequal(Catalyst.getmetadata(r, :md_6), (0.1, 2.0)) end -# Noise scaling metadata. +# tests the noise scaling metadata. let @variables t @parameters k η diff --git a/test/reactionsystem_structure/reactionsystem.jl b/test/reactionsystem_structure/reactionsystem.jl index 74f51e3aa4..6c4d12496a 100644 --- a/test/reactionsystem_structure/reactionsystem.jl +++ b/test/reactionsystem_structure/reactionsystem.jl @@ -1,18 +1,24 @@ ### Fetch Packages and Set Global Variables ### # Fetch packages. -using Catalyst, LinearAlgebra, JumpProcesses, Test, OrdinaryDiffEq, StochasticDiffEq -t = default_t() +using Catalyst, LinearAlgebra, JumpProcesses, OrdinaryDiffEq, StochasticDiffEq, Test const MT = ModelingToolkit -# Sets rnd number. +t = default_t() + +# Sets stable rng number. using StableRNGs rng = StableRNG(12345) -# Fetch test networks and functions. +# Sets the default `t` to use. +t = default_t() + +# Fetch test functions. include("../test_functions.jl") -# Create test network. +### Creates Basic Test Network ### + +# Create the network. @parameters k[1:20] @species A(t) B(t) C(t) D(t) rxs = [Reaction(k[1], nothing, [A]), # 0 -> A @@ -94,7 +100,7 @@ function sdenoise(u, k, t) return G end -### Main Tests ### +### Basic Tests ### # Test equation only constructor. let diff --git a/test/test_functions.jl b/test/test_functions.jl index 205b8c66a9..af4ead7cb2 100644 --- a/test/test_functions.jl +++ b/test/test_functions.jl @@ -1,7 +1,9 @@ # Various functions that are useful for running the tests, and used across several test sets. +# Fetches the (required) Random package. +using Random -# Initial Condition/Parameter Generators ### +### Initial Condition/Parameter Generators ### # Generates a random initial condition (in the form of a map). Each value is a Float64. function rnd_u0(sys, rng; factor = 1.0, min = 0.0) @@ -23,6 +25,14 @@ function rnd_ps_Int64(sys, rng; n = 5, min = 0) return [p => min + rand(rng, 1:n) for p in parameters(sys)] end +# Used to convert a generated initial condition/parameter set to a vector that can be used for normal +# DiffEq functions (that are created for manual comparison). Requires order list of symbols. +function map_to_vec(map, syms) + syms_dict = Dict([ModelingToolkit.getname(entry[1]) => entry[2] for entry in map]) + issetequal(keys(syms_dict), syms) || error("Map symbols ($(keys(syms_dict))) and symbol vector symbols ($(syms)) do not match.") + return [syms_dict[sym] for sym in syms] +end + ### System function evaluation ### # Evaluates the the drift function of the ODE corresponding to a reaction network. diff --git a/test/visualization/graphs.jl b/test/visualization/graphs.jl index 21548d57fa..2c0199a4c2 100644 --- a/test/visualization/graphs.jl +++ b/test/visualization/graphs.jl @@ -1,6 +1,9 @@ +# Fetch packages. using Catalyst, Graphviz_jll ### Basic Tests ### + +# Check basic functionality. let rn = @reaction_network begin α, S + I --> 2I diff --git a/test/visualization/latexify.jl b/test/visualization/latexify.jl index 13cf447263..db5a97a7e3 100644 --- a/test/visualization/latexify.jl +++ b/test/visualization/latexify.jl @@ -1,11 +1,15 @@ #! format: off -### Fetch Packages and Reaction Networks ### -using Catalyst, Latexify +### Preparations ### + +# Fetch packages. +using Catalyst, Latexify, Test + +# Fetch test networks. include("../test_networks.jl") ############################ -### CURRENTLY NOT ACITVE ### +### CURRENTLY NOT ACTIVE ### ### REQUIRES REWRITING ### ############################ @@ -27,6 +31,8 @@ include("../test_networks.jl") ### Basic Tests ### +# Tests functions on basic network (1). +# Tests on network with special functions (hill etc.). let rn = @reaction_network begin hillr(X2,v1,K1,n1)*hill(X4,v1,K1,n1), ∅ → X1 @@ -105,6 +111,7 @@ let ", "\r\n"=>"\n") end +# Tests basic functions on simple network (2). let rn = @reaction_network begin (hill(B, p_a, k, n), d_a), 0 ↔ A @@ -131,50 +138,6 @@ let ", "\r\n"=>"\n") end -# Test empty system. -let - empty_rn = ReactionSystem(Reaction[]; name=:EmptySys) - - # Latexify.@generate_test latexify(empty_rn) - @test latexify(empty_rn) == replace( - raw"ReactionSystem EmptySys has no reactions or equations.", "\r\n"=>"\n") -end - -# Test for /~https://github.com/SciML/Catalyst.jl/issues/473. -let - rn = @reaction_network begin - k*Y, Y --> ∅ - end - - # Latexify.@generate_test latexify(rn) - @test_broken latexify(rn) == replace( - raw"\begin{align*} - \varnothing &\xrightarrow{p} (m + n)\mathrm{X} - \end{align*} - ", "\r\n"=>"\n") -end - - -# Test using various `env` options. -let - rn = @reaction_network begin - (p,d), 0 <--> X - end - chem_latex = latexify(rn; env = :arrows) - @test chem_latex == latexify(rn; env = :chem) - @test chem_latex == latexify(rn; env = :chemical) - @test chem_latex == latexify(rn; env = :arrow) - @test_throws Exception latexify(rn; env = :wrong_env) -end - -# Tests that the `mathrm` option affects the output. -let - rn = @reaction_network begin - (k1,k2), 2X <--> X2 - end - @test latexify(rn; mathrm = true) != latexify(rn; mathrm = false) -end - # Tests for system with parametric stoichiometry. let rn = @reaction_network begin @@ -188,9 +151,9 @@ let ", "\r\n"=>"\n") end -### Tests `form` Option ### +### Tests the `form` Option ### -# Check for large number of networks. +# Check option work for a large number of systems (and do not error). let for rn in reaction_networks_standard @test latexify(rn)==latexify(rn; form=:reactions) @@ -226,9 +189,53 @@ let @test_throws ErrorException latexify(rn; form=:xxx) end +### Other Tests ### + +# Test using various `env` options. +let + rn = @reaction_network begin + (p,d), 0 <--> X + end + chem_latex = latexify(rn; env = :arrows) + @test chem_latex == latexify(rn; env = :chem) + @test chem_latex == latexify(rn; env = :chemical) + @test chem_latex == latexify(rn; env = :arrow) + @test_throws Exception latexify(rn; env = :wrong_env) +end + +# Tests that the `mathrm` option affects the output. +let + rn = @reaction_network begin + (k1,k2), 2X <--> X2 + end + @test latexify(rn; mathrm = true) != latexify(rn; mathrm = false) +end -### Checks Reaction Network - Equations Combination ### +# Test on an empty system. +let + empty_rn = ReactionSystem(Reaction[]; name=:EmptySys) + + # Latexify.@generate_test latexify(empty_rn) + @test latexify(empty_rn) == replace( + raw"ReactionSystem EmptySys has no reactions or equations.", "\r\n"=>"\n") +end + +# Test for /~https://github.com/SciML/Catalyst.jl/issues/473. +# (Error where there's an equation in the reaction rate) +let + rn = @reaction_network begin + k*Y, Y --> ∅ + end + + # Latexify.@generate_test latexify(rn) + @test_broken latexify(rn) == replace( + raw"\begin{align*} + \varnothing &\xrightarrow{p} (m + n)\mathrm{X} + \end{align*} + ", "\r\n"=>"\n") +end +# Checks when combined with equations (nonlinear system). let t = default_t() base_network = @reaction_network begin From 4832dfeb41944e9c5758e564955d83b82d02473d Mon Sep 17 00:00:00 2001 From: Torkel Date: Mon, 25 Mar 2024 18:48:31 -0400 Subject: [PATCH 19/51] up --- test/dsl/dsl_options.jl | 981 +++++++----------- .../programmatic_model_expansion.jl | 2 +- 2 files changed, 393 insertions(+), 590 deletions(-) diff --git a/test/dsl/dsl_options.jl b/test/dsl/dsl_options.jl index 20f5930196..a4c19467b8 100644 --- a/test/dsl/dsl_options.jl +++ b/test/dsl/dsl_options.jl @@ -1,652 +1,455 @@ -#! format: off +### Prepares Tests ### -### Fetch Packages and Set Global Variables ### -using Catalyst, ModelingToolkit, OrdinaryDiffEq, Plots, Test -t = default_t() - -### Tests @parameters and @species Options ### +# Fetch packages. +using DiffEqBase, Catalyst, Random, Test +using ModelingToolkit: operation, istree, get_unknowns, get_ps, get_eqs, get_systems, + get_iv, nameof -# Test creating networks with/without options. Compares they are all identical. -let - @reaction_network begin (k1, k2), A <--> B end - @reaction_network begin - @parameters k1 k2 - (k1, k2), A <--> B - end - @reaction_network begin - @parameters k1 k2 - @species A(t) B(t) - (k1, k2), A <--> B - end - @reaction_network begin - @species A(t) B(t) - (k1, k2), A <--> B - end +# Sets stable rng number. +using StableRNGs +rng = StableRNG(12345) - @reaction_network begin - @parameters begin - k1 - k2 - end - (k1, k2), A <--> B - end - @reaction_network begin - @species begin - A(t) - B(t) - end - (k1, k2), A <--> B - end - @reaction_network begin - @parameters begin - k1 - k2 - end - @species begin - A(t) - B(t) - end - (k1, k2), A <--> B - end - - n1 = @reaction_network rnname begin (k1, k2), A <--> B end - n2 = @reaction_network rnname begin - @parameters k1 k2 - (k1, k2), A <--> B - end - n3 = @reaction_network rnname begin - @species A(t) B(t) - (k1, k2), A <--> B - end - n4 = @reaction_network rnname begin - @parameters k1 k2 - @species A(t) B(t) - (k1, k2), A <--> B - end - n5 = @reaction_network rnname begin - (k1, k2), A <--> B - @parameters k1 k2 - end - n6 = @reaction_network rnname begin - (k1, k2), A <--> B - @species A(t) B(t) - end - n7 = @reaction_network rnname begin - (k1, k2), A <--> B - @parameters k1 k2 - @species A(t) B(t) - end - n8 = @reaction_network rnname begin - @parameters begin - k1 - k2 - end - (k1, k2), A <--> B - end - n9 = @reaction_network rnname begin - @species begin - A(t) - B(t) - end - (k1, k2), A <--> B - end - n10 = @reaction_network rnname begin - @parameters begin - k1 - k2 - end - @species begin - A(t) - B(t) - end - (k1, k2), A <--> B - end - @test all(==(n1), (n2, n3, n4, n5, n6, n7, n8, n9, n10)) -end - -# Tests that when either @species or @parameters is given, the other is inferred properly. -let - rn1 = @reaction_network begin - k*X, A + B --> 0 - end - @test issetequal(species(rn1), @species A(t) B(t)) - @test issetequal(parameters(rn1), @parameters k X) - - rn2 = @reaction_network begin - @species A(t) B(t) X(t) - k*X, A + B --> 0 - end - @test issetequal(species(rn2), @species A(t) B(t) X(t)) - @test issetequal(parameters(rn2), @parameters k) +# Sets the default `t` to use. +t = default_t() - rn3 = @reaction_network begin - @parameters k - k*X, A + B --> 0 - end - @test issetequal(species(rn3), @species A(t) B(t)) - @test issetequal(parameters(rn3), @parameters k X) +# Fetch test networks and functions. +include("../test_networks.jl") +include("../test_functions.jl") - rn4 = @reaction_network begin - @species A(t) B(t) X(t) - @parameters k - k*X, A + B --> 0 - end - @test issetequal(species(rn4), @species A(t) B(t) X(t)) - @test issetequal(parameters(rn4), @parameters k) +### Declares Testing Functions ### - rn5 = @reaction_network begin - @parameters k B [isconstantspecies=true] - k*X, A + B --> 0 - end - @test issetequal(species(rn5), @species A(t)) - @test issetequal(parameters(rn5), @parameters k B X) +function unpacksys(sys) + get_eqs(sys), get_iv(sys), get_unknowns(sys), get_ps(sys), nameof(sys), get_systems(sys) end -# Test inferring with stoichiometry symbols and interpolation. -let - @parameters k g h gg X y [isconstantspecies = true] - @species A(t) B(t) BB(t) C(t) - - rni = @reaction_network inferred begin - $k*X, $y + g*A + h*($gg)*B + $BB * C --> k*C - end - @test issetequal(species(rni), [A, B, BB, C]) - @test issetequal(parameters(rni), [k, g, h, gg, X, y]) +opname(x) = istree(x) ? nameof(operation(x)) : nameof(x) +alleq(xs, ys) = all(isequal(x, y) for (x, y) in zip(xs, ys)) - rnii = @reaction_network inferred begin - @species BB(t) - @parameters y [isconstantspecies = true] - k*X, y + g*A + h*($gg)*B + BB * C --> k*C +# Gets all the reactants in a set of equations. +function all_reactants(eqs) + all_reactants = [] + for eq in eqs + append!(all_reactants, opname.(eq.substrates)) + append!(all_reactants, opname.(eq.products)) end - @test rnii == rni + return Set{Symbol}(unique(all_reactants)) end -# Tests that when some species or parameters are left out, the others are set properly. -let - rn6 = @reaction_network begin - @species A(t) - k*X, A + B --> 0 - end - @test issetequal(species(rn6), @species A(t) B(t)) - @test issetequal(parameters(rn6), @parameters k X) - - rn7 = @reaction_network begin - @species A(t) X(t) - k*X, A + B --> 0 - end - @test issetequal(species(rn7), @species A(t) X(t) B(t)) - @test issetequal(parameters(rn7), @parameters k) - - rn7 = @reaction_network begin - @parameters B [isconstantspecies=true] - k*X, A + B --> 0 - end - @test issetequal(species(rn7), @species A(t)) - @test issetequal(parameters(rn7), @parameters B k X) +# Gets all parameters (where every reaction rate is constant) +function all_parameters(eqs) + return Set(unique(map(eq -> opname(eq.rate), eqs))) +end - rn8 = @reaction_network begin - @parameters B [isconstantspecies=true] k - k*X, A + B --> 0 - end - @test issetequal(species(rn8), @species A(t)) - @test issetequal(parameters(rn8), @parameters B k X) - - rn9 = @reaction_network begin - @parameters k1 X1 - @species A1(t) B1(t) - k1*X1, A1 + B1 --> 0 - k2*X2, A2 + B2 --> 0 - end - @test issetequal(species(rn9), @species A1(t) B1(t) A2(t) B2(t)) - @test issetequal(parameters(rn9), @parameters k1 X1 k2 X2) - - rn10 = @reaction_network begin - @parameters k1 X2 B2 [isconstantspecies=true] - @species A1(t) X1(t) - k1*X1, A1 + B1 --> 0 - k2*X2, A2 + B2 --> 0 - end - @test issetequal(species(rn10), @species A1(t) X1(t) B1(t) A2(t)) - @test issetequal(parameters(rn10), @parameters k1 X2 B2 k2) - - rn11 = @reaction_network begin - @parameters k1 k2 - @species X1(t) - k1*X1, A1 + B1 --> 0 - k2*X2, A2 + B2 --> 0 - end - @test issetequal(species(rn11), @species X1(t) A1(t) A2(t) B1(t) B2(t)) - @test issetequal(parameters(rn11), @parameters k1 k2 X2) +# Perform basic tests. +function basic_test(rn, N, unknowns_syms, p_syms) + eqs, iv, unknowns, ps, name, systems = unpacksys(rn) + @test length(eqs) == N + @test opname(iv) == :t + @test length(unknowns) == length(unknowns_syms) + @test issetequal(map(opname, unknowns), unknowns_syms) + @test all_reactants(eqs) == Set(unknowns_syms) + @test length(ps) == length(p_syms) + @test issetequal(map(opname, ps), p_syms) end -# Checks that networks created using different notation are identical. +### Basic Tests ### + +# Test basic properties of networks. let - rn12 = @reaction_network rnname begin (k1, k2), A <--> B end - rn13 = @reaction_network rnname begin - @parameters k1 k2 - (k1, k2), A <--> B - end - rn14 = @reaction_network rnname begin - @species A(t) B(t) - (k1, k2), A <--> B - end - rn15 = @reaction_network rnname begin - @parameters k1 k2 - @species A(t) B(t) - (k1, k2), A <--> B - end - @test all(==(rn12), (rn13, rn14, rn15)) + basic_test(reaction_networks_standard[1], 10, [:X1, :X2, :X3], + [:p1, :p2, :p3, :k1, :k2, :k3, :k4, :d1, :d2, :d3]) + @test all_parameters(get_eqs(reaction_networks_standard[1])) == + Set([:p1, :p2, :p3, :k1, :k2, :k3, :k4, :d1, :d2, :d3]) + basic_test(reaction_networks_standard[2], 3, [:X1, :X2], [:v1, :K1, :v2, :K2, :d]) + basic_test(reaction_networks_standard[3], 10, [:X1, :X2, :X3, :X4], + [:v1, :K1, :v2, :K2, :k1, :k2, :k3, :k4, :d]) + basic_test(reaction_networks_standard[4], 8, [:X1, :X2, :X3, :X4], + [:v1, :K1, :v2, :K2, :v3, :K3, :v4, :K4, :d1, :d2, :d3, :d4]) + basic_test(reaction_networks_standard[5], 8, [:X1, :X2, :X3, :X4], + [:p, :k1, :k2, :k3, :k4, :k5, :k6, :d]) + @test all_parameters(get_eqs(reaction_networks_standard[5])) == + Set([:p, :k1, :k2, :k3, :k4, :k5, :k6, :d]) + basic_test(reaction_networks_hill[1], 4, [:X1, :X2], + [:v1, :v2, :K1, :K2, :n1, :n2, :d1, :d2]) + basic_test(reaction_networks_constraint[1], 6, [:X1, :X2, :X3], + [:k1, :k2, :k3, :k4, :k5, :k6]) + basic_test(reaction_networks_real[1], 4, [:X, :Y], [:A, :B]) + basic_test(reaction_networks_weird[1], 2, [:X], [:p, :d]) + basic_test(reaction_networks_weird[2], 4, [:X, :Y, :Z], [:k1, :k2, :k3, :k4]) end -# Checks that the rights things are put in vectors. +# Compares networks to networks created using different arrow types. let - rn18 = @reaction_network rnname begin - @parameters p d1 d2 - @species A(t) B(t) - p, 0 --> A - 1, A --> B - (d1, d2), (A, B) --> 0 - end - rn19 = @reaction_network rnname begin - p, 0 --> A - 1, A --> B - (d1, d2), (A, B) --> 0 - end - @test rn18 == rn19 - - @parameters p d1 d2 - @species A(t) B(t) - @test isequal(parameters(rn18)[1], p) - @test isequal(parameters(rn18)[2], d1) - @test isequal(parameters(rn18)[3], d2) - @test isequal(species(rn18)[1], A) - @test isequal(species(rn18)[2], B) - - rn20 = @reaction_network rnname begin - @species X(t) - @parameters S - mm(X,v,K), 0 --> Y - (k1,k2), 2Y <--> Y2 - d*Y, S*(Y2+Y) --> 0 - end - rn21 = @reaction_network rnname begin - @species X(t) Y(t) Y2(t) - @parameters v K k1 k2 d S - mm(X,v,K), 0 --> Y - (k1,k2), 2Y <--> Y2 - d*Y, S*(Y2+Y) --> 0 - end - rn22 = @reaction_network rnname begin - @species X(t) Y2(t) - @parameters d k1 - mm(X,v,K), 0 --> Y - (k1,k2), 2Y <--> Y2 - d*Y, S*(Y2+Y) --> 0 + networks_1 = [] + networks_2 = [] + + different_arrow_1 = @reaction_network begin + (p1, p2, p3), ∅ > (X1, X2, X3) + (k1, k2), X2 ↔ X1 + 2X3 + (k3, k4), X1 ⟷ X3 + (d1, d2, d3), (X1, X2, X3) → ∅ + end + push!(networks_1, reaction_networks_standard[1]) + push!(networks_2, different_arrow_1) + + different_arrow_2 = @reaction_network begin + mmr(X2, v1, K1), ∅ → X1 + mm(X1, v2, K2), ∅ ↣ X2 + d, X1 + X2 ↦ ∅ + end + push!(networks_1, reaction_networks_standard[2]) + push!(networks_2, different_arrow_2) + + different_arrow_3 = @reaction_network begin + mm(X2, v1, K1), ∅ ⇾ X1 + mm(X3, v2, K2), ∅ ⟶ X2 + (k1, k2), X1 ⇄ X3 + (k3, k4), X3 + X2 ⇆ X4 + X1 + d, (X1, X2, X3, X4) ⟼ ∅ + end + push!(networks_1, reaction_networks_standard[3]) + push!(networks_2, different_arrow_3) + + different_arrow_4 = @reaction_network begin + mmr(X4, v1, K1), ∅ ⥟ X1 + mmr(X1, v2, K2), ∅ ⥟ X2 + mmr(X2, v3, K3), ∅ ⇀ X3 + mmr(X3, v4, K4), ∅ ⇁ X4 + (d1, d2, d3, d4), (X1, X2, X3, X4) --> ∅ + end + push!(networks_1, reaction_networks_standard[4]) + push!(networks_2, different_arrow_4) + + # Yes the name is different, I wanted one with several single direction arrows. + different_arrow_8 = @reaction_network begin + p, 2X1 < ∅ + k1, X2 ← X1 + (k2, k3), X3 ⟻ X2 + d, ∅ ↼ X3 + end + push!(networks_1, reaction_networks_standard[8]) + push!(networks_2, different_arrow_8) + + for (rn_1, rn_2) in zip(networks_1, networks_2) + for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] + u0 = rnd_u0(rn_1, rng; factor) + p = rnd_ps(rn_1, rng; factor) + t = rand(rng) + + @test f_eval(rn_1, u0, p, t) ≈ f_eval(rn_2, u0, p, t) + @test jac_eval(rn_1, u0, p, t) ≈ jac_eval(rn_2, u0, p, t) + @test g_eval(rn_1, u0, p, t) ≈ g_eval(rn_2, u0, p, t) + end end - @test all(==(rn20), (rn21, rn22)) - @parameters v K k1 k2 d S - @species X(t) Y(t) Y2(t) - @test issetequal(parameters(rn22),[v K k1 k2 d S]) - @test issetequal(species(rn22), [X Y Y2]) end -# Tests that defaults work. +# Compares two networks with different parameter/species names. let - rn26 = @reaction_network rnname begin - @parameters p=1.0 d1 d2=5 - @species A(t) B(t)=4 - p, 0 --> A - 1, A --> B - (d1, d2), (A, B) --> 0 - end - - rn27 = @reaction_network rnname begin - @parameters p1=1.0 p2=2.0 k1=4.0 k2=5.0 v=8.0 K=9.0 n=3 d=10.0 - @species X(t)=4.0 Y(t)=3.0 X2Y(t)=2.0 Z(t)=1.0 - (p1,p2), 0 --> (X,Y) - (k1,k2), 2X + Y --> X2Y - hill(X2Y,v,K,n), 0 --> Z - d, (X,Y,X2Y,Z) --> 0 - end - u0_27 = [] - p_27 = [] - - rn28 = @reaction_network rnname begin - @parameters p1=1.0 p2 k1=4.0 k2 v=8.0 K n=3 d - @species X(t)=4.0 Y(t) X2Y(t) Z(t)=1.0 - (p1,p2), 0 --> (X,Y) - (k1,k2), 2X + Y --> X2Y - hill(X2Y,v,K,n), 0 --> Z - d, (X,Y,X2Y,Z) --> 0 - end - u0_28 = symmap_to_varmap(rn28, [:p2=>2.0, :k2=>5.0, :K=>9.0, :d=>10.0]) - p_28 = symmap_to_varmap(rn28, [:Y=>3.0, :X2Y=>2.0]) - defs28 = Dict(Iterators.flatten((u0_28, p_28))) - - rn29 = @reaction_network rnname begin - @parameters p1 p2 k1 k2 v K n d - @species X(t) Y(t) X2Y(t) Z(t) - (p1,p2), 0 --> (X,Y) - (k1,k2), 2X + Y --> X2Y - hill(X2Y,v,K,n), 0 --> Z - d, (X,Y,X2Y,Z) --> 0 - end - u0_29 = symmap_to_varmap(rn29, [:p1=>1.0, :p2=>2.0, :k1=>4.0, :k2=>5.0, :v=>8.0, :K=>9.0, :n=>3, :d=>10.0]) - p_29 = symmap_to_varmap(rn29, [:X=>4.0, :Y=>3.0, :X2Y=>2.0, :Z=>1.0]) - defs29 = Dict(Iterators.flatten((u0_29, p_29))) - - @test ModelingToolkit.defaults(rn27) == defs29 - @test merge(ModelingToolkit.defaults(rn28), defs28) == ModelingToolkit.defaults(rn27) + # Declares network. + differently_written_5 = @reaction_network begin + q, ∅ → Y1 + (l1, l2), Y1 ⟷ Y2 + (l3, l4), Y2 ⟷ Y3 + (l5, l6), Y3 ⟷ Y4 + c, Y4 → ∅ + end + + # Computes initial conditions/parameter values. + u0_vals = rand(rng, length(species(differently_written_5))) + ps_vals = rand(rng, length(parameters(differently_written_5))) + u0_1 = [:X1 => u0_vals[1], :X2 => u0_vals[2], :X3 => u0_vals[3], :X4 => u0_vals[4]] + u0_2 = [:Y1 => u0_vals[1], :Y2 => u0_vals[2], :Y3 => u0_vals[3], :Y4 => u0_vals[4]] + ps_1 = [:p => ps_vals[1], :k1 => ps_vals[2], :k2 => ps_vals[3], :k3 => ps_vals[4], + :k4 => ps_vals[5], :k5 => ps_vals[6], :k6 => ps_vals[7], :d => ps_vals[8]] + ps_2 = [:q => ps_vals[1], :l1 => ps_vals[2], :l2 => ps_vals[3], :l3 => ps_vals[4], + :l4 => ps_vals[5], :l5 => ps_vals[6], :l6 => ps_vals[7], :c => ps_vals[8]] + t = rand(rng) + + # Checks equivalence. + rn_1 = reaction_networks_standard[5] + rn_2 = differently_written_5 + @test f_eval(rn_1, u0_1, ps_1, t) ≈ f_eval(rn_2, u0_2, ps_2, t) + @test jac_eval(rn_1, u0_1, ps_1, t) ≈ jac_eval(rn_2, u0_2, ps_2, t) + @test g_eval(rn_1, u0_1, ps_1, t) ≈ g_eval(rn_2, u0_2, ps_2, t) end -### Observables ### - -# Test basic functionality. -# Tests various types of indexing. -let - rn = @reaction_network begin - @observables begin - X ~ Xi + Xa - Y ~ Y1 + Y2 +# Compares networks to networks written in different ways. +let + networks_1 = [] + networks_2 = [] + + # Unfold reactions. + differently_written_6 = @reaction_network begin + p1, ∅ → X1 + p2, ∅ → X2 + k1, 2X1 → X3 + k2, X3 → 2X1 + k3, X2 + X3 → 4X4 + k4, 4X4 → X2 + X3 + k5, X4 + X1 → 2X3 + k6, 2X3 → X4 + X1 + d, X1 → ∅ + d, X2 → ∅ + d, X3 → ∅ + d, X4 → ∅ + end + push!(networks_1, reaction_networks_standard[6]) + push!(networks_2, differently_written_6) + + # Ignore mass action. + differently_written_7 = @reaction_network begin + @parameters p1 p2 p3 k1 k2 k3 v1 K1 d1 d2 d3 d4 d5 + (p1, p2, p3), ∅ ⇒ (X1, X2, X3) + (k1 * X1 * X2^2 / 2, k2 * X4), X1 + 2X2 ⟺ X4 + (mm(X3, v1, K1) * X4, k3 * X5), X4 ⇔ X5 + (d1 * X1, d2 * X2, d3 * X3, d4 * X4, d5 * X5), ∅ ⟽ (X1, X2, X3, X4, X5) + end + push!(networks_1, reaction_networks_standard[7]) + push!(networks_2, differently_written_7) + + # Ignore mass action new arrows. + differently_written_8 = @reaction_network begin + @parameters p1 p2 p3 k1 k2 k3 v1 K1 d1 d2 d3 d4 d5 + (p1, p2, p3), ∅ => (X1, X2, X3) + (k1 * X1 * X2^2 / 2, k2 * X4), X1 + 2X2 ⟺ X4 + (mm(X3, v1, K1) * X4, k3 * X5), X4 ⇔ X5 + (d1 * X1, d2 * X2, d3 * X3, d4 * X4, d5 * X5), ∅ <= (X1, X2, X3, X4, X5) + end + push!(networks_1, reaction_networks_standard[7]) + push!(networks_2, differently_written_8) + + for (rn_1, rn_2) in zip(networks_1, networks_2) + for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] + u0 = rnd_u0(rn_1, rng; factor) + p = rnd_ps(rn_1, rng; factor) + t = rand(rng) + + @test f_eval(rn_1, u0, p, t) ≈ f_eval(rn_2, u0, p, t) + @test jac_eval(rn_1, u0, p, t) ≈ jac_eval(rn_2, u0, p, t) + @test g_eval(rn_1, u0, p, t) ≈ g_eval(rn_2, u0, p, t) end - (p,d), 0 <--> Xi - (k1,k2), Xi <--> Xa - (k3,k4), Y1 <--> Y2 end - @unpack X, Xi, Xa, Y, Y1, Y2, p, d, k1, k2, k3, k4 = rn - - # Test that ReactionSystem have the correct properties. - @test length(species(rn)) == 4 - @test length(unknowns(rn)) == 4 - @test length(observed(rn)) == 2 - @test length(equations(rn)) == 6 - - @test isequal(observed(rn)[1], X ~ Xi + Xa) - @test isequal(observed(rn)[2], Y ~ Y1 + Y2) - - # Tests correct indexing of solution. - u0 = [Xi => 0.0, Xa => 0.0, Y1 => 1.0, Y2 => 2.0] - ps = [p => 1.0, d => 0.2, k1 => 1.5, k2 => 1.5, k3 => 5.0, k4 => 5.0] - - oprob = ODEProblem(rn, u0, (0.0, 1000.0), ps) - sol = solve(oprob, Tsit5()) - @test sol[X][end] ≈ 10.0 - @test sol[Y][end] ≈ 3.0 - @test sol[rn.X][end] ≈ 10.0 - @test sol[rn.Y][end] ≈ 3.0 - @test sol[:X][end] ≈ 10.0 - @test sol[:Y][end] ≈ 3.0 - - # Tests that observables can be used for plot indexing. - @test plot(sol; idxs=X).series_list[1].plotattributes[:y][end] ≈ 10.0 - @test plot(sol; idxs=rn.X).series_list[1].plotattributes[:y][end] ≈ 10.0 - @test plot(sol; idxs=:X).series_list[1].plotattributes[:y][end] ≈ 10.0 - @test plot(sol; idxs=[X, Y]).series_list[2].plotattributes[:y][end] ≈ 3.0 - @test plot(sol; idxs=[rn.X, rn.Y]).series_list[2].plotattributes[:y][end] ≈ 3.0 - @test plot(sol; idxs=[:X, :Y]).series_list[2].plotattributes[:y][end] ≈ 3.0 end -# Compares programmatic and DSL systems with observables. +# Compares networks to networks written without parameters, let - # Model declarations. - rn_dsl = @reaction_network begin - @observables begin - X ~ x + 2x2y - Y ~ y + x2y + networks_1 = [] + networks_2 = [] + parameter_sets = [] + + # Different parameter and variable names. + no_parameters_9 = @reaction_network begin + (1.5, 1, 2), ∅ ⟶ (X1, X2, X3) + (0.01, 2.3, 1001), (X1, X2, X3) ⟶ ∅ + (π, 42), X1 + X2 ⟷ X3 + (19.9, 999.99), X3 ⟷ X4 + (sqrt(3.7), exp(1.9)), X4 ⟷ X1 + X2 + end + push!(networks_1, reaction_networks_standard[9]) + push!(networks_2, no_parameters_9) + push!(parameter_sets, [:p1 => 1.5, :p2 => 1, :p3 => 2, :d1 => 0.01, :d2 => 2.3, :d3 => 1001, + :k1 => π, :k2 => 42, :k3 => 19.9, :k4 => 999.99, :k5 => sqrt(3.7), :k6 => exp(1.9)]) + + no_parameters_10 = @reaction_network begin + 0.01, ∅ ⟶ X1 + (3.1, 3.2), X1 → X2 + (0.0, 2.1), X2 → X3 + (901.0, 63.5), X3 → X4 + (7, 8), X4 → X5 + 1.0, X5 ⟶ ∅ + end + push!(networks_1, reaction_networks_standard[10]) + push!(networks_2, no_parameters_10) + push!(parameter_sets, [:p => 0.01, :k1 => 3.1, :k2 => 3.2, :k3 => 0.0, :k4 => 2.1, :k5 => 901.0, + :k6 => 63.5, :k7 => 7, :k8 => 8, :d => 1.0]) + + + for (rn_1, rn_2, p_1) in zip(networks_1, networks_2, parameter_sets) + for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] + u0 = rnd_u0(rn_1, rng; factor) + t = rand(rng) + + @test f_eval(rn_1, u0, p_1, t) ≈ f_eval(rn_2, u0, [], t) + @test jac_eval(rn_1, u0, p_1, t) ≈ jac_eval(rn_2, u0, [], t) + @test g_eval(rn_1, u0, p_1, t) ≈ g_eval(rn_2, u0, [], t) end - k, 0 --> (x, y) - (kB, kD), 2x + y <--> x2y - d, (x,y,x2y) --> 0 - end - - @variables X(t) Y(t) - @species x(t), y(t), x2y(t) - @parameters k kB kD d - r1 = Reaction(k, nothing, [x], nothing, [1]) - r2 = Reaction(k, nothing, [y], nothing, [1]) - r3 = Reaction(kB, [x, y], [x2y], [2, 1], [1]) - r4 = Reaction(kD, [x2y], [x, y], [1], [2, 1]) - r5 = Reaction(d, [x], nothing, [1], nothing) - r6 = Reaction(d, [y], nothing, [1], nothing) - r7 = Reaction(d, [x2y], nothing, [1], nothing) - obs_eqs = [X ~ x + 2x2y, Y ~ y + x2y] - @named rn_prog = ReactionSystem([r1, r2, r3, r4, r5, r6, r7], t, [x, y, x2y], [k, kB, kD, d]; observed = obs_eqs) - - # Make simulations. - u0 = [x => 1.0, y => 0.5, x2y => 0.0] - tspan = (0.0, 15.0) - ps = [k => 1.0, kD => 0.1, kB => 0.5, d => 5.0] - oprob_dsl = ODEProblem(rn_dsl, u0, tspan, ps) - oprob_prog = ODEProblem(rn_prog, u0, tspan, ps) - - sol_dsl = solve(oprob_dsl, Tsit5(); saveat=0.1) - sol_prog = solve(oprob_prog, Tsit5(); saveat=0.1) - - # Tests observables equal in both cases. - @test oprob_dsl[:X] == oprob_prog[:X] - @test oprob_dsl[:Y] == oprob_prog[:Y] - @test sol_dsl[:X] == sol_prog[:X] - @test sol_dsl[:Y] == sol_prog[:Y] -end - -# Tests for complicated observable formula. -# Tests using a single observable (without begin/end statement). -# Tests using observable component not part of reaction. -# Tests using parameters in observables formula. -let - rn = @reaction_network begin - @parameters op_1 op_2 - @species X4(t) - @observables X ~ X1^2 + op_1*(X2 + 2X3) + X1*X4/op_2 + p - (p,d), 0 <--> X1 - (k1,k2), X1 <--> X2 - (k3,k4), X2 <--> X3 end - - u0 = Dict([:X1 => 1.0, :X2 => 2.0, :X3 => 3.0, :X4 => 4.0]) - ps = Dict([:p => 1.0, :d => 0.2, :k1 => 1.5, :k2 => 1.5, :k3 => 5.0, :k4 => 5.0, :op_1 => 1.5, :op_2 => 1.5]) - - oprob = ODEProblem(rn, u0, (0.0, 1000.0), ps) - sol = solve(oprob, Tsit5()) - - @test sol[:X][1] == u0[:X1]^2 + ps[:op_1]*(u0[:X2] + 2*u0[:X3]) + u0[:X1]*u0[:X4]/ps[:op_2] + ps[:p] end -# Checks that ivs are correctly found. +# Tests that reaction systems created manually and through macro are identical. let - rn = @reaction_network begin - @ivs t x y - @species V1(t) V2(t,x) V3(t, y) W1(t) W2(t, y) - @observables begin - V ~ V1 + 2V2 + 3V3 - W ~ W1 + W2 + identical_networks_4 = Vector{Pair}() + @parameters v1 K1 v2 K2 k1 k2 k3 k4 k5 p d t + @species X1(t) X2(t) X3(t) X4(t) X5(t) + + rxs_1 = [Reaction(p, nothing, [X1], nothing, [2]), + Reaction(k1, [X1], [X2], [1], [1]), + Reaction(k2, [X2], [X3], [1], [1]), + Reaction(k3, [X2], [X3], [1], [1]), + Reaction(d, [X3], nothing, [1], nothing)] + @named rs_1 = ReactionSystem(rxs_1, t, [X1, X2, X3], [p, k1, k2, k3, d]) + push!(identical_networks_4, reaction_networks_standard[8] => rs_1) + + rxs_2 = [Reaction(k1, [X1], [X2], [1], [1]), + Reaction(k2 * X5, [X2], [X1], [1], [1]), + Reaction(k3 * X5, [X3], [X4], [1], [1]), + Reaction(k4, [X4], [X3], [1], [1]), + Reaction(p + k5 * X2 * X3, nothing, [X5], nothing, [1]), + Reaction(d, [X5], nothing, [1], nothing)] + @named rs_2 = ReactionSystem(rxs_2, t, [X1, X2, X3, X4, X5], [k1, k2, k3, k4, p, k5, d]) + push!(identical_networks_4, reaction_networks_constraint[3] => rs_2) + + rxs_3 = [Reaction(k1, [X1], [X2], [1], [1]), + Reaction(0, [X2], [X3], [1], [1]), + Reaction(k2, [X3], [X4], [1], [1]), + Reaction(k3, [X4], [X5], [1], [1])] + @named rs_3 = ReactionSystem(rxs_3, t, [X1, X2, X3, X4, X5], [k1, k2, k3]) + push!(identical_networks_4, reaction_networks_weird[7] => rs_3) + + for networks in identical_networks_4 + @test isequal(get_iv(networks[1]), get_iv(networks[2])) + @test alleq(get_unknowns(networks[1]), get_unknowns(networks[2])) + @test alleq(get_ps(networks[1]), get_ps(networks[2])) + @test ModelingToolkit.get_systems(networks[1]) == + ModelingToolkit.get_systems(networks[2]) + @test length(get_eqs(networks[1])) == length(get_eqs(networks[2])) + for (e1, e2) in zip(get_eqs(networks[1]), get_eqs(networks[2])) + @test isequal(e1.rate, e2.rate) + @test isequal(e1.substrates, e2.substrates) + @test isequal(e1.products, e2.products) + @test isequal(e1.substoich, e2.substoich) + @test isequal(e1.prodstoich, e2.prodstoich) + @test isequal(e1.netstoich, e2.netstoich) + @test isequal(e1.only_use_rate, e2.only_use_rate) end end - V,W = getfield.(observed(rn), :lhs) - @test isequal(arguments(ModelingToolkit.unwrap(V)), Any[rn.iv, rn.sivs[1], rn.sivs[2]]) - @test isequal(arguments(ModelingToolkit.unwrap(W)), Any[rn.iv, rn.sivs[2]]) end -# Checks that metadata is written properly. +### Tests Usage of Various Symbols ### + +# Tests that time is handled properly. let - rn = @reaction_network rn_observed begin - @observables (X, [description="my_description"]) ~ X1 + X2 - k, 0 --> X1 + X2 + time_network = @reaction_network begin + (t, k2), X1 ↔ X2 + (k3, t), X2 ↔ X3 + (t, k6), X3 ↔ X1 end - @test getdescription(observed(rn)[1].lhs) == "my_description" -end -# Declares observables implicitly/explicitly. -# Cannot test `isequal(rn1, rn2)` because the two sets of observables have some obscure Symbolics -# substructure that are different. -let - # Basic case. - rn1 = @reaction_network rn_observed begin - @observables X ~ X1 + X2 - k, 0 --> X1 + X2 - end - rn2 = @reaction_network rn_observed begin - @variables X(t) - @observables X ~ X1 + X2 - k, 0 --> X1 + X2 - end - @test isequal(observed(rn1)[1].rhs, observed(rn2)[1].rhs) - @test isequal(observed(rn1)[1].lhs.metadata, observed(rn2)[1].lhs.metadata) - @test isequal(unknowns(rn1), unknowns(rn2)) - - # Case with metadata. - rn3 = @reaction_network rn_observed begin - @observables (X, [description="description"]) ~ X1 + X2 - k, 0 --> X1 + X2 - end - rn4 = @reaction_network rn_observed begin - @variables X(t) [description="description"] - @observables X ~ X1 + X2 - k, 0 --> X1 + X2 + for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] + τ = rand(rng) + u = rnd_u0(reaction_networks_constraint[1], rng; factor) + p_2 = rnd_ps(time_network, rng; factor) + p_1 = [p_2; reaction_networks_constraint[1].k1 => τ; + reaction_networks_constraint[1].k4 => τ; reaction_networks_constraint[1].k5 => τ] + + @test f_eval(reaction_networks_constraint[1], u, p_1, τ) ≈ f_eval(time_network, u, p_2, τ) + @test jac_eval(reaction_networks_constraint[1], u, p_1, τ) ≈ jac_eval(time_network, u, p_2, τ) + @test g_eval(reaction_networks_constraint[1], u, p_1, τ) ≈ g_eval(time_network, u, p_2, τ) end - @test isequal(observed(rn3)[1].rhs, observed(rn4)[1].rhs) - @test isequal(observed(rn3)[1].lhs.metadata, observed(rn4)[1].lhs.metadata) - @test isequal(unknowns(rn3), unknowns(rn4)) end -# Tests for interpolation into the observables option. +# Check that various symbols can be used as species/parameter names. let - # Interpolation into lhs. - @species X [description="An observable"] - rn1 = @reaction_network begin - @observables $X ~ X1 + X2 - (k1, k2), X1 <--> X2 - end - @test isequal(observed(rn1)[1].lhs, X) - @test getdescription(rn1.X) == "An observable" - @test isspecies(rn1.X) - @test length(unknowns(rn1)) == 2 - - # Interpolation into rhs. - @parameters n [description="A parameter"] - @species S(t) - rn2 = @reaction_network begin - @observables Stot ~ $S + $n*Sn - (kB, kD), $n*S <--> Sn - end - @unpack Stot, Sn, kD, kB = rn2 + @reaction_network begin + (a, A), n ⟷ N + (b, B), o ⟷ O + (c, C), p ⟷ P + (d, D), q ⟷ Q + (e, E), r ⟷ R + (f, F), s ⟷ S + (g, G), u ⟷ U + (h, H), v ⟷ V + (j, J), w ⟷ W + (k, K), x ⟷ X + (l, L), y ⟷ Y + (m, M), z ⟷ Z + end + + @reaction_network begin (1.0, 1.0), i ⟷ T end - u0 = Dict([S => 5.0, Sn => 1.0]) - ps = Dict([n => 2, kB => 1.0, kD => 1.0]) - oprob = ODEProblem(rn2, u0, (0.0, 1.0), ps) + @reaction_network begin + (å, Å), ü ⟷ Ü + (ä, Ä), ñ ⟷ Ñ + (ö, Ö), æ ⟷ Æ + end - @test issetequal(Symbolics.get_variables(observed(rn2)[1].rhs), [S, n, Sn]) - @test oprob[Stot] == u0[S] + ps[n]*u0[Sn] - @test length(unknowns(rn2)) == 2 + @reaction_network begin + (α, Α), ν ⟷ Ν + (β, Β), ξ ⟷ Ξ + (γ, γ), ο ⟷ Ο + (δ, Δ), Π ⟷ Π + (ϵ, Ε), ρ ⟷ Ρ + (ζ, Ζ), σ ⟷ Σ + (η, Η), τ ⟷ Τ + (θ, Θ), υ ⟷ Υ + (ι, Ι), ϕ ⟷ Φ + (κ, κ), χ ⟷ Χ + (λ, Λ), ψ ↔ Ψ + (μ, Μ), ω ⟷ Ω + end end -# Tests specific declaration of Observables as species/variables +# Test that I works as a name. let rn = @reaction_network begin - @species X(t) - @variables Y(t) - @observables begin - X ~ X + 2X2 - Y ~ Y1 + Y2 - Z ~ X + Y - end - (kB,kD), 2X <--> X2 - (k1,k2), Y1 <--> Y2 + k1, S + I --> 2I + k2, I --> R end - - @test isspecies(rn.X) - @test !isspecies(rn.Y) - @test !isspecies(rn.Z) + @species I(t) + @test any(isequal(I), species(rn)) + @test any(isequal(I), unknowns(rn)) end -# Tests various erroneous declarations throw errors. -let - # Independent variable in @observables. - @test_throws Exception @eval @reaction_network begin - @observables X(t) ~ X1 + X2 - k, 0 --> X1 + X2 - end - - # System with observable in observable formula. - @test_throws Exception @eval @reaction_network begin - @observables begin - X ~ X1 + X2 - X2 ~ 2X - end - (p,d), 0 <--> X1 + X2 - end - - # Multiple @observables options - @test_throws Exception @eval @reaction_network begin - @observables X ~ X1 + X2 - @observables Y ~ Y1 + Y2 - k, 0 --> X1 + X2 - k, 0 --> Y1 + Y2 - end - @test_throws Exception @eval @reaction_network begin - @observables begin - X ~ X1 + X2 - end - @observables begin - X ~ 2(X1 + X2) - end - (p,d), 0 <--> X1 + X2 - end - - # Default value for compound. - @test_throws Exception @eval @reaction_network begin - @observables (X = 1.0) ~ X1 + X2 - k, 0 --> X1 + X2 - end - - # Forbidden symbols as observable names. - @test_throws Exception @eval @reaction_network begin - @observables t ~ t1 + t2 - k, 0 --> t1 + t2 - end - @test_throws Exception @eval @reaction_network begin - @observables im ~ i + m - k, 0 --> i + m - end - - # Non-trivial observables expression. - @test_throws Exception @eval @reaction_network begin - @observables X - X1 ~ X2 - k, 0 --> X1 + X2 +# Tests backwards and double arrows. +let + rn1 = @reaction_network arrowtest begin + (a1, a2), C <--> 0 + (k1, k2), A + B <--> C + b1, 0 <-- B end - # Occurrence of undeclared dependants. - @test_throws Exception @eval @reaction_network begin - @observables X ~ X1 + X2 - k, 0 --> X1 + rn2 = @reaction_network arrowtest begin + a1, C --> 0 + a2, 0 --> C + k1, A + B --> C + k2, C --> A + B + b1, B --> 0 end - # Interpolation and explicit declaration of an observable. - @variables X(t) - @test_throws Exception @eval @reaction_network begin - @variables X(t) - @observables $X ~ X1 + X2 - (k1,k2), X1 <--> X2 - end + @test rn1 == rn2 end -### Tests Completeness Designations ### - -let - # Creates models with/without the `@incomplete` option. - rn1 = @reaction_network begin - (p,d), 0 <--> X - end - rn2 = @reaction_network begin - @incomplete - (p,d), 0 <--> X - end - - # Check that the correct compeltness is achived. - @test ModelingToolkit.iscomplete(rn1) - @test !ModelingToolkit.iscomplete(rn2) - rn2 = complete(rn2) - @test ModelingToolkit.iscomplete(rn2) +# Tests arrow variants in "@reaction" macro . +let + @test isequal((@reaction k, 0 --> X), (@reaction k, X <-- 0)) + @test isequal((@reaction k, 0 --> X), (@reaction k, X ⟻ 0)) + @test isequal((@reaction k, 0 --> X), (@reaction k, 0 → X)) + @test isequal((@reaction k, 0 --> X), (@reaction k, 0 ⥟ X)) end - \ No newline at end of file +# Test that symbols with special mean, or that are forbidden, are handled properly. +let + test_network = @reaction_network begin t * k, X --> ∅ end + @test length(species(test_network)) == 1 + @test length(parameters(test_network)) == 1 + + test_network = @reaction_network begin π, X --> ∅ end + @test length(species(test_network)) == 1 + @test length(parameters(test_network)) == 0 + @test reactions(test_network)[1].rate == π + + test_network = @reaction_network begin pi, X --> ∅ end + @test length(species(test_network)) == 1 + @test length(parameters(test_network)) == 0 + @test reactions(test_network)[1].rate == pi + + test_network = @reaction_network begin ℯ, X --> ∅ end + @test length(species(test_network)) == 1 + @test length(parameters(test_network)) == 0 + @test reactions(test_network)[1].rate == ℯ + + @test_throws LoadError @eval @reaction im, 0 --> B + @test_throws LoadError @eval @reaction nothing, 0 --> B + @test_throws LoadError @eval @reaction k, 0 --> im + @test_throws LoadError @eval @reaction k, 0 --> nothing +end diff --git a/test/programmatic_model_creation/programmatic_model_expansion.jl b/test/programmatic_model_creation/programmatic_model_expansion.jl index 11a3e0dcdf..917e50d836 100644 --- a/test/programmatic_model_creation/programmatic_model_expansion.jl +++ b/test/programmatic_model_creation/programmatic_model_expansion.jl @@ -10,7 +10,7 @@ using ModelingToolkit: get_ps, get_unknowns, get_eqs, get_systems, get_iv, getna using StableRNGs rng = StableRNG(12345) -# Fetch test networks. +# Sets the default `t` to use. t = default_t() # Fetch test networks. From f4deb3ae7321d9581fa368fbf3d78f2f85f8acba Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 26 Mar 2024 10:27:00 -0400 Subject: [PATCH 20/51] updates with working conservation laws --- Project.toml | 2 +- test/extensions/homotopy_continuation.jl | 13 ++++++++----- test/miscellaneous_tests/api.jl | 1 - test/miscellaneous_tests/nonlinear_solve.jl | 6 ++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Project.toml b/Project.toml index 994631aa99..6d94040ef2 100644 --- a/Project.toml +++ b/Project.toml @@ -44,7 +44,7 @@ HomotopyContinuation = "2.9" LaTeXStrings = "1.3.0" Latexify = "0.14, 0.15, 0.16" MacroTools = "0.5.5" -ModelingToolkit = "9.6" +ModelingToolkit = "9.7.1" Parameters = "0.12" Reexport = "0.2, 1.0" Requires = "1.0" diff --git a/test/extensions/homotopy_continuation.jl b/test/extensions/homotopy_continuation.jl index d366c42f46..b920bee7d4 100644 --- a/test/extensions/homotopy_continuation.jl +++ b/test/extensions/homotopy_continuation.jl @@ -14,7 +14,8 @@ include("../test_functions.jl") # Tests for Symbolics initial condition input. # Tests for different types (Symbol/Symbolics) for parameters and initial conditions. # Tests that attempts to find steady states of system with conservation laws, while u0 is not provided, gives an error. -@test_broken let # Requires /~https://github.com/SciML/ModelingToolkit.jl/issues/2566 to be fixed +let + # Creates the model. rs = @reaction_network begin (k1,k2), X1 <--> X2 (k3,k4), 2X2 + X3 <--> X2_2X3 @@ -23,10 +24,12 @@ include("../test_functions.jl") ps = [k1 => 1.0, k2 => 2.0, k3 => 2.0, k4 => 2.0] u0 = [:X1 => 2.0, :X2 => 2.0, :X3 => 2.0, :X2_2X3 => 2.0] - hc_ss = hc_steady_states(rs, ps; u0=u0, show_progress=false)[1] - f = ODEFunction(convert(ODESystem, rs)) - @test f(hc_ss, last.(ps), 0.0)[1] == 0.0 + # Computes the single steady state, checks that when given to the ODE rhs, all are evaluated to 0. + hc_ss = hc_steady_states(rs, ps; u0=u0, show_progress=false) + hc_ss = Pair.(unknowns(rs), hc_ss[1]) + @test maximum(abs.(f_eval(rs, hc_ss, ps, 0.0))) ≈ 0.0 atol=1e-12 + # Checks that not giving a `u0` argument yields an error for systems with conservation laws. @test_throws Exception hc_steady_states(rs, ps; show_progress=false) end @@ -55,7 +58,7 @@ end # Tests correctness in presence of default values. # Tests where some default values are overwritten with other values. # Tests where input ps/u0 are tuples with mixed types. -@test_broken let # Requires /~https://github.com/SciML/ModelingToolkit.jl/issues/2566 to be fixed +let rs_1 = @reaction_network begin @parameters kX1=1.0 kX2=2.0 kY1=12345.0 @species X1(t)=0.1 X2(t)=0.2 Y1(t)=12345.0 diff --git a/test/miscellaneous_tests/api.jl b/test/miscellaneous_tests/api.jl index 55dddabfc6..d42a55d7c1 100644 --- a/test/miscellaneous_tests/api.jl +++ b/test/miscellaneous_tests/api.jl @@ -533,7 +533,6 @@ end # Test conservation law elimination. # Conservation laws currently broken (you get stuck in an infinite loop in MTK or something). let - return (@test_broken false) rn = @reaction_network begin (k1, k2), A + B <--> C (m1, m2), D <--> E diff --git a/test/miscellaneous_tests/nonlinear_solve.jl b/test/miscellaneous_tests/nonlinear_solve.jl index c2040bc1c4..4bba00612a 100644 --- a/test/miscellaneous_tests/nonlinear_solve.jl +++ b/test/miscellaneous_tests/nonlinear_solve.jl @@ -77,8 +77,6 @@ end # Checks for system with conservation laws. # Checks using interfacing with output solution. let - # Conservation laws currently broken (you get stuck in an infinite loop in MTK or something). - return (@test_broken false) # Creates steady state network, unpack the parameter values. steady_state_network_3 = @reaction_network begin (p,d), 0 <--> X @@ -98,6 +96,6 @@ let sol2 = solve(nl_prob_2, DynamicSS(Rosenbrock23()); abstol=1e-12, reltol=1e-12) # Checks output using the ODE's drift function - @test f_eval([:X => sol1[X], :Y => sol1[Y], :Y2 => sol1[Y2], :XY2 => sol1[XY2]], p, 0.0) ≈ [0.0, 0.0, 0.0, 0.0] atol=1e-10 - @test f_eval([:X => sol2[X], :Y => sol2[Y], :Y2 => sol2[Y2], :XY2 => sol2[XY2]], p, 0.0) ≈ [0.0, 0.0, 0.0, 0.0] atol=1e-10 + @test f_eval(steady_state_network_3, [:X => sol1[X], :Y => sol1[Y], :Y2 => sol1[Y2], :XY2 => sol1[XY2]], p, 0.0) ≈ [0.0, 0.0, 0.0, 0.0] atol=1e-10 + @test f_eval(steady_state_network_3, [:X => sol2[X], :Y => sol2[Y], :Y2 => sol2[Y2], :XY2 => sol2[XY2]], p, 0.0) ≈ [0.0, 0.0, 0.0, 0.0] atol=1e-10 end \ No newline at end of file From 68c22c1cc87bf9eccfac959f691cefb2ac4908fd Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 29 Mar 2024 10:18:54 -0400 Subject: [PATCH 21/51] update with new completeness handling --- .../compositional_modeling.md | 28 +++--- src/Catalyst.jl | 2 +- src/reaction_network.jl | 87 +++++++++---------- src/reactionsystem.jl | 37 +++++--- test/extensions/bifurcation_kit.jl | 18 ++-- test/extensions/structural_identifiability.jl | 6 +- test/miscellaneous_tests/api.jl | 7 +- .../component_based_model_creation.jl | 6 +- .../programmatic_model_expansion.jl | 3 +- 9 files changed, 88 insertions(+), 106 deletions(-) diff --git a/docs/src/catalyst_functionality/compositional_modeling.md b/docs/src/catalyst_functionality/compositional_modeling.md index 862c1d9d19..bd5d552a94 100644 --- a/docs/src/catalyst_functionality/compositional_modeling.md +++ b/docs/src/catalyst_functionality/compositional_modeling.md @@ -6,13 +6,12 @@ identically repressed genes, and how to use compositional modeling to create compartments. ## A note on *completeness* -Catalyst `ReactionSystem` can either be *complete* or *incomplete*. *By default they are created as complete*. Here, only complete `ReactionSystem`s can be used to create the various problem types (e.g. `ODEProblem`). However, only incomplete `ReactionSystem`s can be composed using the features described below. Hence, for compositional modeling, `ReactionSystem` must be created as incomplete, and later set to complete before simulation. +Catalyst `ReactionSystem` can either be *complete* or *incomplete*. When created using the `@reaction_network` DSL they are *created as complete*. Here, only complete `ReactionSystem`s can be used to create the various problem types (e.g. `ODEProblem`). However, only incomplete `ReactionSystem`s can be composed using the features described below. Hence, for compositional modeling, `ReactionSystem` must be created as incomplete, and later set to complete before simulation. -To set a `ReactionSystem` created via the DSL as complete, use the `@incomplete` option: +To create incomplete `ReactionSystem`s using the DSL as complete, use the `@network_component` instead of `@reaction_network`: ```@example ex0 using Catalyst -degradation_component = @reaction_network begin - @incomplete +degradation_component = @network_component begin d, X --> 0 end ``` @@ -40,12 +39,10 @@ Catalyst supports two ModelingToolkit interfaces for composing multiple extending a system is the `extend` command ```@example ex1 using Catalyst -basern = @reaction_network rn1 begin - @incomplete +basern = @network_component rn1 begin k, A + B --> C end -newrn = @reaction_network rn2 begin - @incomplete +newrn = @network_component rn2 begin r, C --> A + B end @named rn = extend(newrn, basern) @@ -58,8 +55,7 @@ The second main compositional modeling tool is the use of subsystems. Suppose we now add to `basern` two subsystems, `newrn` and `newestrn`, we get a different result: ```@example ex1 -newestrn = @reaction_network rn3 begin - @incomplete +newestrn = @network_component rn3 begin v, A + D --> 2D end @named rn = compose(basern, [newrn, newestrn]) @@ -131,8 +127,7 @@ modular fashion. We start by defining a function that creates a negatively repressed gene, taking the repressor as input ```@example ex1 function repressed_gene(; R, name) - @reaction_network $name begin - @incomplete + @network_component $name begin hillr($R,α,K,n), ∅ --> m (δ,γ), m <--> ∅ β, m --> m + P @@ -189,15 +184,13 @@ In our model we'll therefore add the conversions of the last column to properly account for compartment volumes: ```@example ex1 # transcription and regulation -nuc = @reaction_network nuc begin - @incomplete +nuc = @network_component nuc begin α, G --> G + M (κ₊/V,κ₋), D + G <--> DG end # translation and dimerization -cyto = @reaction_network cyto begin - @incomplete +cyto = @network_component cyto begin β, M --> M + P (k₊/V,k₋), 2P <--> D σ, P --> 0 @@ -206,8 +199,7 @@ end # export reactions, # γ,δ=probability per time to be exported/imported -model = @reaction_network model begin - @incomplete +model = @network_component model begin γ, $(nuc.M) --> $(cyto.M) δ, $(cyto.D) --> $(nuc.D) end diff --git a/src/Catalyst.jl b/src/Catalyst.jl index 1fa198dcd2..cf50cfccc0 100644 --- a/src/Catalyst.jl +++ b/src/Catalyst.jl @@ -77,7 +77,7 @@ export ODEProblem, const ExprValues = Union{Expr, Symbol, Float64, Int, Bool} include("expression_utils.jl") include("reaction_network.jl") -export @reaction_network, @add_reactions, @reaction, @species +export @reaction_network, @network_component, @add_reactions, @reaction, @species # registers CRN specific functions using Symbolics.jl include("registered_functions.jl") diff --git a/src/reaction_network.jl b/src/reaction_network.jl index 2d838ece0c..bf6968b92c 100644 --- a/src/reaction_network.jl +++ b/src/reaction_network.jl @@ -1,45 +1,4 @@ ### Temporary deprecation warning - Eventually to be removed. ### -deprication_message = """ -@reaction_network notation where parameters are declared after "end", e.g. like: - -```julia -@reaction_network begin - p, 0 --> X - d, X --> 0 -end p d -``` - -has been deprecated in favor of a notation where the parameters are inferred, e.g: - -```julia -@reaction_network begin - p, 0 --> X - d, X --> 0 -end -``` - -Parameters and species can be explicitly indicated using the @parameters and @species -macros, e.g: - -```julia -@reaction_network begin - @parameters p d - @species X(t) - p, 0 --> X - d, X --> 0 -end -``` -""" -macro reaction_network(name::Symbol, ex::Expr, parameters...) - error(deprication_message) -end -macro reaction_network(name::Expr, ex::Expr, parameters...) - error(deprication_message) -end -macro reaction_network(ex::Expr, parameters...) - error(deprication_message) -end - """ Macro that inputs an expression corresponding to a reaction network and outputs a `ReactionNetwork` that can be used as input to generation of ODE, SDE, and @@ -123,7 +82,7 @@ const forbidden_variables_error = let end # Declares the keys used for various options. -const option_keys = (:species, :parameters, :variables, :ivs, :compounds, :observables, :default_noise_scaling, :incomplete) +const option_keys = (:species, :parameters, :variables, :ivs, :compounds, :observables, :default_noise_scaling) ### The @species macro, basically a copy of the @variables macro. ### macro species(ex...) @@ -186,19 +145,55 @@ emptyrn = @reaction_network empty # an empty network with random generated name emptyrn = @reaction_network ``` + +ReactionSystems generated through `@reaction_network` are compelte. """ macro reaction_network(name::Symbol, ex::Expr) - make_reaction_system(MacroTools.striplines(ex); name = :($(QuoteNode(name)))) + :(complete($(make_reaction_system(MacroTools.striplines(ex); name = :($(QuoteNode(name))))))) end # allows @reaction_network $name begin ... to interpolate variables storing a name macro reaction_network(name::Expr, ex::Expr) - make_reaction_system(MacroTools.striplines(ex); name = :($(esc(name.args[1])))) + :(complete($(make_reaction_system(MacroTools.striplines(ex); name = :($(esc(name.args[1]))))))) end macro reaction_network(ex::Expr) ex = MacroTools.striplines(ex) + # no name but equations: @reaction_network begin ... end ... + if ex.head == :block + :(complete($(make_reaction_system(ex)))) + else # empty but has interpolated name: @reaction_network $name + networkname = :($(esc(ex.args[1]))) + return Expr(:block, :(@parameters t), + :(complete(ReactionSystem(Reaction[], t, [], []; name = $networkname)))) + end +end + +#Returns a empty network (with, or without, a declared name) +macro reaction_network(name::Symbol = gensym(:ReactionSystem)) + return Expr(:block, :(@parameters t), + :(complete(ReactionSystem(Reaction[], t, [], []; name = $(QuoteNode(name)))))) +end + + +""" + @network_component + +As @reaction_network, but the output system is not complete. +""" +macro network_component(name::Symbol, ex::Expr) + make_reaction_system(MacroTools.striplines(ex); name = :($(QuoteNode(name)))) +end + +# allows @reaction_network $name begin ... to interpolate variables storing a name +macro network_component(name::Expr, ex::Expr) + make_reaction_system(MacroTools.striplines(ex); name = :($(esc(name.args[1])))) +end + +macro network_component(ex::Expr) + ex = MacroTools.striplines(ex) + # no name but equations: @reaction_network begin ... end ... if ex.head == :block make_reaction_system(ex) @@ -210,12 +205,12 @@ macro reaction_network(ex::Expr) end #Returns a empty network (with, or without, a declared name) -# @reaction_network name -macro reaction_network(name::Symbol = gensym(:ReactionSystem)) +macro network_component(name::Symbol = gensym(:ReactionSystem)) return Expr(:block, :(@parameters t), :(ReactionSystem(Reaction[], t, [], []; name = $(QuoteNode(name))))) end + ### Macros used for manipulating, and successively builing up, reaction systems. ### @doc raw""" @reaction diff --git a/src/reactionsystem.jl b/src/reactionsystem.jl index d27fbb394a..4c6b2b26ca 100644 --- a/src/reactionsystem.jl +++ b/src/reactionsystem.jl @@ -544,7 +544,7 @@ struct ReactionSystem{V <: NetworkProperties} <: # inner constructor is considered private and may change between non-breaking releases. function ReactionSystem(eqs, rxs, iv, sivs, unknowns, spcs, ps, var_to_name, observed, name, systems, defaults, connection_type, nps, cls, cevs, devs, - metadata = nothing, complete::Bool = false; checks::Bool = true) + metadata, complete; checks::Bool = true) # unit checks are for ODEs and Reactions only currently nonrx_eqs = Equation[eq for eq in eqs if eq isa Equation] @@ -610,8 +610,7 @@ function ReactionSystem(eqs, iv, unknowns, ps; spatial_ivs = nothing, continuous_events = nothing, discrete_events = nothing, - metadata = nothing, - complete = true) + metadata = nothing) name === nothing && throw(ArgumentError("The `name` keyword must be provided. Please consider using the `@named` macro")) @@ -681,7 +680,7 @@ function ReactionSystem(eqs, iv, unknowns, ps; ReactionSystem(eqs′, rxs, iv′, sivs′, unknowns′, spcs, ps′, var_to_name, observed, name, systems, defaults, connection_type, nps, combinatoric_ratelaws, - ccallbacks, dcallbacks, metadata, complete; checks = checks) + ccallbacks, dcallbacks, metadata, false; checks = checks) end function ReactionSystem(rxs::Vector, iv = Catalyst.DEFAULT_IV; kwargs...) @@ -1472,6 +1471,8 @@ function spatial_convert_err(rs::ReactionSystem, systype) isspatial(rs) && error("Conversion to $systype is not supported for spatial networks.") end +COMPLETENESS_ERROR = "A ReactionSystem must be complete before it can be converted to other system types. A ReactionSystem can be marked as complete using the `compelte` function." + """ ```julia Base.convert(::Type{<:ODESystem},rs::ReactionSystem) @@ -1493,8 +1494,9 @@ function Base.convert(::Type{<:ODESystem}, rs::ReactionSystem; name = nameof(rs) include_zero_odes = true, remove_conserved = false, checks = false, default_u0 = Dict(), default_p = Dict(), defaults = _merge(Dict(default_u0), Dict(default_p)), kwargs...) + iscomplete(rs) || error(COMPLETENESS_ERROR) spatial_convert_err(rs::ReactionSystem, ODESystem) - fullrs = Catalyst.flatten(rs; complete = true) + fullrs = Catalyst.flatten(rs) remove_conserved && conservationlaws(fullrs) ists, ispcs = get_indep_sts(fullrs, remove_conserved) eqs = assemble_drift(fullrs, ispcs; combinatoric_ratelaws, remove_conserved, @@ -1509,7 +1511,7 @@ function Base.convert(::Type{<:ODESystem}, rs::ReactionSystem; name = nameof(rs) continuous_events = MT.get_continuous_events(fullrs), discrete_events = MT.get_discrete_events(fullrs), kwargs...) - return iscomplete(rs) ? complete(osys) : osys + return osys end """ @@ -1534,6 +1536,7 @@ function Base.convert(::Type{<:NonlinearSystem}, rs::ReactionSystem; name = name include_zero_odes = true, remove_conserved = false, checks = false, default_u0 = Dict(), default_p = Dict(), defaults = _merge(Dict(default_u0), Dict(default_p)), kwargs...) + iscomplete(rs) || error(COMPLETENESS_ERROR) spatial_convert_err(rs::ReactionSystem, NonlinearSystem) fullrs = Catalyst.flatten(rs; complete = true) remove_conserved && conservationlaws(fullrs) @@ -1549,7 +1552,7 @@ function Base.convert(::Type{<:NonlinearSystem}, rs::ReactionSystem; name = name defaults = _merge(defaults,defs), checks, kwargs...) - return iscomplete(rs) ? complete(nlsys) : nlsys + return nlsys end """ @@ -1576,6 +1579,7 @@ function Base.convert(::Type{<:SDESystem}, rs::ReactionSystem; include_zero_odes = true, checks = false, remove_conserved = false, default_u0 = Dict(), default_p = Dict(), defaults = _merge(Dict(default_u0), Dict(default_p)), kwargs...) + iscomplete(rs) || error(COMPLETENESS_ERROR) spatial_convert_err(rs::ReactionSystem, SDESystem) flatrs = Catalyst.flatten(rs; complete = true) @@ -1600,7 +1604,7 @@ function Base.convert(::Type{<:SDESystem}, rs::ReactionSystem; continuous_events = MT.get_continuous_events(flatrs), discrete_events = MT.get_discrete_events(flatrs), kwargs...) - return iscomplete(rs) ? complete(ssys) : ssys + return ssys end """ @@ -1626,6 +1630,7 @@ function Base.convert(::Type{<:JumpSystem}, rs::ReactionSystem; name = nameof(rs remove_conserved = nothing, checks = false, default_u0 = Dict(), default_p = Dict(), defaults = _merge(Dict(default_u0), Dict(default_p)), kwargs...) + iscomplete(rs) || error(COMPLETENESS_ERROR) spatial_convert_err(rs::ReactionSystem, JumpSystem) (remove_conserved !== nothing) && @@ -1651,7 +1656,7 @@ function Base.convert(::Type{<:JumpSystem}, rs::ReactionSystem; name = nameof(rs checks, discrete_events = MT.discrete_events(flatrs), kwargs...) - return iscomplete(rs) ? complete(jsys) : jsys + return jsys end ### Converts a reaction system to ODE or SDE problems ### @@ -1667,6 +1672,7 @@ function DiffEqBase.ODEProblem(rs::ReactionSystem, u0, tspan, pmap = symmap_to_varmap(rs, p) osys = convert(ODESystem, rs; name, combinatoric_ratelaws, include_zero_odes, checks, remove_conserved) + osys = complete(osys) return ODEProblem(osys, u0map, tspan, pmap, args...; check_length, kwargs...) end @@ -1681,6 +1687,7 @@ function DiffEqBase.NonlinearProblem(rs::ReactionSystem, u0, pmap = symmap_to_varmap(rs, p) nlsys = convert(NonlinearSystem, rs; name, combinatoric_ratelaws, include_zero_odes, checks, remove_conserved) + nlsys = complete(nlsys) return NonlinearProblem(nlsys, u0map, pmap, args...; check_length, kwargs...) end @@ -1695,6 +1702,7 @@ function DiffEqBase.SDEProblem(rs::ReactionSystem, u0, tspan, pmap = symmap_to_varmap(rs, p) sde_sys = convert(SDESystem, rs; name, combinatoric_ratelaws, include_zero_odes, checks, remove_conserved) + sde_sys = complete(sde_sys) p_matrix = zeros(length(get_unknowns(sde_sys)), numreactions(rs)) return SDEProblem(sde_sys, u0map, tspan, pmap, args...; check_length, noise_rate_prototype = p_matrix, kwargs...) @@ -1709,6 +1717,7 @@ function DiffEqBase.DiscreteProblem(rs::ReactionSystem, u0, tspan::Tuple, u0map = symmap_to_varmap(rs, u0) pmap = symmap_to_varmap(rs, p) jsys = convert(JumpSystem, rs; name, combinatoric_ratelaws, checks) + jsys = complete(jsys) return DiscreteProblem(jsys, u0map, tspan, pmap, args...; kwargs...) end @@ -1718,6 +1727,7 @@ function JumpProcesses.JumpProblem(rs::ReactionSystem, prob, aggregator, args... combinatoric_ratelaws = get_combinatoric_ratelaws(rs), checks = false, kwargs...) jsys = convert(JumpSystem, rs; name, combinatoric_ratelaws, checks) + jsys = complete(jsys) return JumpProblem(jsys, prob, aggregator, args...; kwargs...) end @@ -1732,6 +1742,7 @@ function DiffEqBase.SteadyStateProblem(rs::ReactionSystem, u0, pmap = symmap_to_varmap(rs, p) osys = convert(ODESystem, rs; name, combinatoric_ratelaws, include_zero_odes, checks, remove_conserved) + osys = complete(osys) return SteadyStateProblem(osys, u0map, pmap, args...; check_length, kwargs...) end @@ -1792,7 +1803,7 @@ Notes: - The default value of `combinatoric_ratelaws` will be the logical or of all `ReactionSystem`s. """ -function MT.flatten(rs::ReactionSystem; name = nameof(rs), complete = false) +function MT.flatten(rs::ReactionSystem; name = nameof(rs)) isempty(get_systems(rs)) && return rs # right now only NonlinearSystems and ODESystems can be handled as subsystems @@ -1810,8 +1821,7 @@ function MT.flatten(rs::ReactionSystem; name = nameof(rs), complete = false) balanced_bc_check = false, spatial_ivs = get_sivs(rs), continuous_events = MT.continuous_events(rs), - discrete_events = MT.discrete_events(rs), - complete = complete) + discrete_events = MT.discre) end """ @@ -1868,6 +1878,5 @@ function ModelingToolkit.extend(sys::MT.AbstractSystem, rs::ReactionSystem; balanced_bc_check = false, spatial_ivs = sivs, continuous_events, - discrete_events, - complete = false) + discrete_events) end diff --git a/test/extensions/bifurcation_kit.jl b/test/extensions/bifurcation_kit.jl index 79443682fb..c5805ee7cb 100644 --- a/test/extensions/bifurcation_kit.jl +++ b/test/extensions/bifurcation_kit.jl @@ -87,23 +87,19 @@ end # Creates a system where rn is composed of 4, somewhat nested, networks. # Tests with defaults within nested networks. let - rn1 = @reaction_network rn1 begin - @incomplete + rn1 = @network_component rn1 begin @parameters p=1.0 (p, d), 0 <--> X end - rn2 = @reaction_network rn2 begin - @incomplete + rn2 = @network_component rn2 begin @parameters p=2.0 (p, d), 0 <--> X end - rn3 = @reaction_network rn3 begin - @incomplete + rn3 = @network_component rn3 begin @parameters p=3.0 (p, d), 0 <--> X end - rn4 = @reaction_network rn4 begin - @incomplete + rn4 = @network_component rn4 begin @parameters p=4.0 (p, d), 0 <--> X end @@ -150,12 +146,10 @@ end # Tests for nested model with conservation laws. let # Creates model. - rn1 = @reaction_network rn1 begin - @incomplete + rn1 = @network_component rn1 begin (k1, k2), X1 <--> X2 end - rn2 = @reaction_network rn2 begin - @incomplete + rn2 = @network_component rn2 begin (l1, l2), Y1 <--> Y2 end @named rn = compose(rn1, [rn2]) diff --git a/test/extensions/structural_identifiability.jl b/test/extensions/structural_identifiability.jl index 9473001c33..f6aabea82e 100644 --- a/test/extensions/structural_identifiability.jl +++ b/test/extensions/structural_identifiability.jl @@ -204,12 +204,10 @@ end # Tests for hierarchical model with conservation laws at both top and internal levels. let # Identifiability analysis for Catalyst model. - rs1 = @reaction_network rs1 begin - @incomplete + rs1 = @network_component rs1 begin (k1, k2), X1 <--> X2 end - rs2 = @reaction_network rs2 begin - @incomplete + rs2 = @network_component rs2 begin (k3, k4), X3 <--> X4 end @named rs_catalyst = compose(rs1, [rs2]) diff --git a/test/miscellaneous_tests/api.jl b/test/miscellaneous_tests/api.jl index d42a55d7c1..063d061219 100644 --- a/test/miscellaneous_tests/api.jl +++ b/test/miscellaneous_tests/api.jl @@ -497,14 +497,11 @@ let @test norm(sol.u - sol3.u) ≈ 0 # Test symmap_to_varmap. - # Test symmap_to_varmap. - sir = @reaction_network sir begin - @incomplete + sir = @network_component sir begin β, S + I --> 2I ν, I --> R end - subsys = @reaction_network subsys begin - @incomplete + subsys = @network_component subsys begin k, A --> B end @named sys = compose(sir, [subsys]) diff --git a/test/programmatic_model_creation/component_based_model_creation.jl b/test/programmatic_model_creation/component_based_model_creation.jl index 095f347875..f50eac9ebe 100644 --- a/test/programmatic_model_creation/component_based_model_creation.jl +++ b/test/programmatic_model_creation/component_based_model_creation.jl @@ -458,15 +458,13 @@ end # Tests that conversion with defaults works for a composed model. let - rn1 = @reaction_network rn1 begin - @incomplete + rn1 = @network_component rn1 begin @parameters p=1.0 r=2.0 @species X(t) = 3.0 Y(t) = 4.0 (p1, d), 0 <--> X (p2, r), 0 <--> Z end - rn2 = @reaction_network rn1 begin - @incomplete + rn2 = @network_component rn1 begin @parameters p=10. q=20.0 @species X(t) = 30.0 Z(t) = 40.0 (p1, d), 0 <--> X diff --git a/test/programmatic_model_creation/programmatic_model_expansion.jl b/test/programmatic_model_creation/programmatic_model_expansion.jl index 917e50d836..7e5e8943b5 100644 --- a/test/programmatic_model_creation/programmatic_model_expansion.jl +++ b/test/programmatic_model_creation/programmatic_model_expansion.jl @@ -51,8 +51,7 @@ end # Tests accessing parameters and species added with network API. # Should probably be removed if we remove mutating stuff? let - empty_network_3 = @reaction_network begin - @incomplete + empty_network_3 = @network_component begin end @parameters p @species x(t) From 45ad1d8df1a432cdfde7ac5c4f6f2ca70f9c0b7b Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 29 Mar 2024 12:00:40 -0400 Subject: [PATCH 22/51] updates test --- src/expression_utils.jl | 12 --- src/reaction_network.jl | 3 +- src/reactionsystem.jl | 8 +- test/dsl/custom_functions.jl | 10 +-- test/dsl/dsl_basics.jl | 85 ++++++++++--------- test/dsl/dsl_model_construction.jl | 2 +- test/miscellaneous_tests/api.jl | 6 +- test/miscellaneous_tests/events.jl | 4 +- test/model_simulation/simulate_SDEs.jl | 5 +- .../model_simulation/u0_n_parameter_inputs.jl | 2 +- .../component_based_model_creation.jl | 67 +++++++++------ .../programmatic_model_expansion.jl | 8 +- .../reactionsystem.jl | 82 +++++++++--------- 13 files changed, 148 insertions(+), 146 deletions(-) diff --git a/src/expression_utils.jl b/src/expression_utils.jl index d5fafe64a2..b13f78b512 100644 --- a/src/expression_utils.jl +++ b/src/expression_utils.jl @@ -94,18 +94,6 @@ function is_escaped_expr(expr) end -### Generic Expression Handling ### - -# Convert an expression that is a vector with symbols that have values assigned using `=` -# (e.g. :([a=1.0, b=2.0])) to a vector where the assignment instead uses symbols and pairs -# (e.g. :([a=>1.0, b=>2.0])). Used to e.g. convert default reaction metadata to a form that can be -# evaluated as actual code. -function expr_equal_vector_to_pairs(expr_vec) - pair_vector = :([]) - foreach(arg -> push!(pair_vector.args, arg.args[1] => arg.args[2]), expr_vec.args) - return pair_vector -end - ### Old Stuff ### #This will be called whenever a function stored in funcdict is called. diff --git a/src/reaction_network.jl b/src/reaction_network.jl index bf6968b92c..bf885b4d22 100644 --- a/src/reaction_network.jl +++ b/src/reaction_network.jl @@ -360,7 +360,6 @@ function make_reaction_system(ex::Expr; name = :(gensym(:ReactionSystem))) check_default_noise_scaling!(default_reaction_metadata, options) compound_expr, compound_species = read_compound_options(options) check_default_noise_scaling!(default_reaction_metadata, options) - default_reaction_metadata = expr_equal_vector_to_pairs(default_reaction_metadata) # Parses reactions, species, and parameters. reactions = get_reactions(reaction_lines) @@ -806,7 +805,7 @@ function check_default_noise_scaling!(default_reaction_metadata, options) if (length(options[:default_noise_scaling].args) != 3) # Becasue of how expressions are, 3 is used. error("@default_noise_scaling should only have a single input, this appears not to be the case: \"$(options[:default_noise_scaling])\"") end - push!(default_reaction_metadata.args, :(noise_scaling=$(options[:default_noise_scaling].args[3]))) + push!(default_reaction_metadata.args, :(:noise_scaling => $(options[:default_noise_scaling].args[3]))) end end diff --git a/src/reactionsystem.jl b/src/reactionsystem.jl index 4c6b2b26ca..3de292035d 100644 --- a/src/reactionsystem.jl +++ b/src/reactionsystem.jl @@ -1538,7 +1538,7 @@ function Base.convert(::Type{<:NonlinearSystem}, rs::ReactionSystem; name = name kwargs...) iscomplete(rs) || error(COMPLETENESS_ERROR) spatial_convert_err(rs::ReactionSystem, NonlinearSystem) - fullrs = Catalyst.flatten(rs; complete = true) + fullrs = Catalyst.flatten(rs) remove_conserved && conservationlaws(fullrs) ists, ispcs = get_indep_sts(fullrs, remove_conserved) eqs = assemble_drift(fullrs, ispcs; combinatoric_ratelaws, remove_conserved, @@ -1582,7 +1582,7 @@ function Base.convert(::Type{<:SDESystem}, rs::ReactionSystem; iscomplete(rs) || error(COMPLETENESS_ERROR) spatial_convert_err(rs::ReactionSystem, SDESystem) - flatrs = Catalyst.flatten(rs; complete = true) + flatrs = Catalyst.flatten(rs) error_if_constraints(SDESystem, flatrs) remove_conserved && conservationlaws(flatrs) @@ -1636,7 +1636,7 @@ function Base.convert(::Type{<:JumpSystem}, rs::ReactionSystem; name = nameof(rs (remove_conserved !== nothing) && error("Catalyst does not support removing conserved species when converting to JumpSystems.") - flatrs = Catalyst.flatten(rs; complete = true) + flatrs = Catalyst.flatten(rs) error_if_constraints(JumpSystem, flatrs) (length(MT.continuous_events(flatrs)) > 0) && @@ -1821,7 +1821,7 @@ function MT.flatten(rs::ReactionSystem; name = nameof(rs)) balanced_bc_check = false, spatial_ivs = get_sivs(rs), continuous_events = MT.continuous_events(rs), - discrete_events = MT.discre) + discrete_events = MT.continuous_events(rs)) end """ diff --git a/test/dsl/custom_functions.jl b/test/dsl/custom_functions.jl index 0f3d408ffb..f254d081ac 100644 --- a/test/dsl/custom_functions.jl +++ b/test/dsl/custom_functions.jl @@ -55,7 +55,7 @@ let mm(X, v, K), 0 --> X2 mm(X, v, K), 0 --> X3 end - f_mm = ODEFunction(convert(ODESystem, mm_network), jac = true) + f_mm = ODEFunction(complete(convert(ODESystem, mm_network)), jac = true) u0 = 10 * rand(rng, length(get_unknowns(mm_network))) p = 10 * rand(rng, length(get_ps(mm_network))) @@ -75,7 +75,7 @@ let mmr(X, v, K), 0 --> X2 mmr(X, v, K), 0 --> X3 end - f_mmr = ODEFunction(convert(ODESystem, mmr_network), jac = true) + f_mmr = ODEFunction(complete(convert(ODESystem, mmr_network)), jac = true) u0 = 10 * rand(rng, length(get_unknowns(mmr_network))) p = 10 * rand(rng, length(get_ps(mmr_network))) @@ -94,7 +94,7 @@ let hill(X, v, K, 2), 0 --> X1 hill(X, v, K, 2), 0 --> X2 end - f_hill = ODEFunction(convert(ODESystem, hill_network), jac = true) + f_hill = ODEFunction(complete(convert(ODESystem, hill_network)), jac = true) u0 = 10 * rand(rng, length(get_unknowns(hill_network))) p = 10 * rand(rng, length(get_ps(hill_network))) @@ -113,7 +113,7 @@ let hillr(X, v, K, 2), 0 --> X1 hillr(X, v, K, 2), 0 --> X2 end - f_hillr = ODEFunction(convert(ODESystem, hillr_network), jac = true) + f_hillr = ODEFunction(complete(convert(ODESystem, hillr_network)), jac = true) u0 = 10 * rand(rng, length(get_unknowns(hillr_network))) p = 10 * rand(rng, length(get_ps(hillr_network))) @@ -134,7 +134,7 @@ let hillar(X, Y, v, K, 2), 0 --> X3 hillar(X, Y, v, K, 2), 0 --> X4 end - f_hillar = ODEFunction(convert(ODESystem, hillar_network), jac = true) + f_hillar = ODEFunction(complete(convert(ODESystem, hillar_network)), jac = true) u0 = 10 * rand(rng, length(get_unknowns(hillar_network))) p = 10 * rand(rng, length(get_ps(hillar_network))) diff --git a/test/dsl/dsl_basics.jl b/test/dsl/dsl_basics.jl index 20b9254090..a2c5992911 100644 --- a/test/dsl/dsl_basics.jl +++ b/test/dsl/dsl_basics.jl @@ -68,54 +68,53 @@ end ### Test Interpolation Within the DSL ### # Tests basic interpolation cases. -let - # Declares parameters and species used across the test. - @parameters α k k1 k2 - @species A(t) B(t) C(t) D(t) - let - AA = A - AAA = A^2 + B - rn = @reaction_network rn begin - @parameters k - @species A(t) B(t) C(t) D(t) - k*$AAA, C --> D - end - rn2 = ReactionSystem([Reaction(k*AAA, [C], [D])], t; name=:rn) - @test rn == rn2 +# Declares parameters and species used across the test. +@parameters α k k1 k2 +@species A(t) B(t) C(t) D(t) - rn = @reaction_network rn begin - @parameters k - @species A(t) C(t) D(t) - k, $AA + C --> D - end - rn2 = ReactionSystem([Reaction(k, [AA,C], [D])], t; name=:rn) - @test rn == rn2 +let + AA = A + AAA = A^2 + B + rn = @reaction_network rn begin + @parameters k + @species A(t) B(t) C(t) D(t) + k*$AAA, C --> D end + rn2 = ReactionSystem([Reaction(k*AAA, [C], [D])], t; name=:rn) + @test rn == rn2 - let - BB = B; A2 = A - rn = @reaction_network rn begin - @parameters k1 k2 - (k1,k2), C + $A2 + $BB + $A2 <--> $BB + $BB - end - rn2 = ReactionSystem([Reaction(k1, [C, A, B], [B], [1,2,1],[2]), - Reaction(k2, [B], [C, A, B], [2], [1,2,1])], - t; name=:rn) - @test rn == rn2 + rn = @reaction_network rn begin + @parameters k + @species A(t) C(t) D(t) + k, $AA + C --> D end + rn2 = ReactionSystem([Reaction(k, [AA,C], [D])], t; name=:rn) + @test rn == rn2 +end - let - AA = A - kk1 = k^2*A - kk2 = k1+k2 - rn = @reaction_network rn begin - @parameters α k k1 k2 - α+$kk1*$kk2*$AA, 2*$AA + B --> $AA - end - rn2 = ReactionSystem([Reaction(α+kk1*kk2*AA, [A, B], [A], [2, 1], [1])], t; name=:rn) - @test rn == rn2 +let + BB = B; A2 = A + rn = @reaction_network rn begin + @parameters k1 k2 + (k1,k2), C + $A2 + $BB + $A2 <--> $BB + $BB + end + rn2 = ReactionSystem([Reaction(k1, [C, A, B], [B], [1,2,1],[2]), + Reaction(k2, [B], [C, A, B], [2], [1,2,1])], + t; name=:rn) + @test rn == rn2 +end + +let + AA = A + kk1 = k^2*A + kk2 = k1+k2 + rn = @reaction_network rn begin + @parameters α k k1 k2 + α+$kk1*$kk2*$AA, 2*$AA + B --> $AA end + rn2 = ReactionSystem([Reaction(α+kk1*kk2*AA, [A, B], [A], [2, 1], [1])], t; name=:rn) + @test rn == rn2 end # Miscellaneous interpolation tests. Unsure what they do here (not related to DSL). @@ -272,6 +271,7 @@ let rx2 = Reaction(2*k, [B], [D], [1], [2.5]) rx3 = Reaction(2*k, [B], [D], [2.5], [2]) @named mixedsys = ReactionSystem([rx1,rx2,rx3],t,[B,C,D],[k]) + mixedsys = complete(mixedsys) osys = convert(ODESystem, mixedsys; combinatoric_ratelaws=false) rn = @reaction_network mixedsys begin @parameters k @@ -313,11 +313,12 @@ let eq = D(B) ~ -B @named osys = ODESystem([eq], t) @named rn2 = extend(osys, rn2) + rn2 = complete(rn2) @test issetequal(unknowns(rn2), species(rn2)) @test all(isspecies, species(rn)) @test Catalyst.isbc(ModelingToolkit.value(B)) @test Catalyst.isbc(ModelingToolkit.value(A)) == false - osys2 = convert(ODESystem, rn2) + osys2 = complete(convert(ODESystem, rn2)) @test issetequal(unknowns(osys2), unknowns(rn2)) @test length(equations(osys2)) == 2 end diff --git a/test/dsl/dsl_model_construction.jl b/test/dsl/dsl_model_construction.jl index 9d3587cb35..8c4d7b3586 100644 --- a/test/dsl/dsl_model_construction.jl +++ b/test/dsl/dsl_model_construction.jl @@ -316,7 +316,7 @@ let end end -# CHeck that various symbols can be used as species/parameter names. +# Check that various symbols can be used as species/parameter names. let @reaction_network begin (a, A), n ⟷ N diff --git a/test/miscellaneous_tests/api.jl b/test/miscellaneous_tests/api.jl index 063d061219..e4ee34ea54 100644 --- a/test/miscellaneous_tests/api.jl +++ b/test/miscellaneous_tests/api.jl @@ -537,7 +537,7 @@ let b23, F2 --> F3 b31, F3 --> F1 end - osys = convert(ODESystem, rn; remove_conserved = true) + osys = complete(convert(ODESystem, rn; remove_conserved = true)) @unpack A, B, C, D, E, F1, F2, F3, k1, k2, m1, m2, b12, b23, b31 = osys u0 = [A => 10.0, B => 10.0, C => 0.0, D => 10.0, E => 0.0, F1 => 8.0, F2 => 0.0, F3 => 0.0] @@ -556,7 +556,7 @@ let @test isapprox(sol2(tv, idxs = s), sol2(tv, idxs = s)) end - nsys = convert(NonlinearSystem, rn; remove_conserved = true) + nsys = complete(convert(NonlinearSystem, rn; remove_conserved = true)) nprob = NonlinearProblem{true}(nsys, u0, p) nsol = solve(nprob, NewtonRaphson(); abstol = 1e-10) nprob2 = ODEProblem(rn, u0, (0.0, 100.0 * tspan[2]), p) @@ -570,7 +570,7 @@ let u0 = [A => 100.0, B => 20.0, C => 5.0, D => 10.0, E => 3.0, F1 => 8.0, F2 => 2.0, F3 => 20.0] - ssys = convert(SDESystem, rn; remove_conserved = true) + ssys = complete(convert(SDESystem, rn; remove_conserved = true)) sprob = SDEProblem(ssys, u0, tspan, p) sprob2 = SDEProblem(rn, u0, tspan, p) sprob3 = SDEProblem(rn, u0, tspan, p; remove_conserved = true) diff --git a/test/miscellaneous_tests/events.jl b/test/miscellaneous_tests/events.jl index 776e2aa13b..76e51d3fc3 100644 --- a/test/miscellaneous_tests/events.jl +++ b/test/miscellaneous_tests/events.jl @@ -17,12 +17,13 @@ let discrete_events = [1.0 => [V ~ 1.0]] rxs = [(@reaction $V, 0 --> A), (@reaction 1.0, A --> 0)] @named rs = ReactionSystem([rxs; eqs], t; discrete_events) + rs = complete(rs) @test length(ModelingToolkit.discrete_events(rs)) == 1 @test length(ModelingToolkit.continuous_events(rs)) == 0 # Tests in simulation. setdefaults!(rs, [:A => 0.0]) - osys = convert(ODESystem, rs) + osys = complete(convert(ODESystem, rs)) @test length(ModelingToolkit.discrete_events(osys)) == 1 oprob = ODEProblem(osys, [], (0.0, 20.0)) sol = solve(oprob, Tsit5()) @@ -37,6 +38,7 @@ let rxs = [Reaction(α, nothing, [V]), Reaction(β, [V], nothing)] continuous_events = [V ~ 2.5] => [α ~ 0, β ~ 0] @named rs = ReactionSystem(rxs, t; continuous_events) + rs = complete(rs) @test length(ModelingToolkit.discrete_events(rs)) == 0 @test length(ModelingToolkit.continuous_events(rs)) == 1 diff --git a/test/model_simulation/simulate_SDEs.jl b/test/model_simulation/simulate_SDEs.jl index 58a614a0de..f3f22fc755 100644 --- a/test/model_simulation/simulate_SDEs.jl +++ b/test/model_simulation/simulate_SDEs.jl @@ -255,7 +255,6 @@ let (p,d), 0 <--> X2, ([description="Y"],[description="Y"]) (p,d), 0 <--> X3 end - noise_scaling_network = complete(noise_scaling_network) u0 = [:X1 => 1000.0, :X2 => 1000.0, :X3 => 1000.0] ps = [noise_scaling_network.p => 1000.0, noise_scaling_network.d => 1.0, noise_scaling_network.η1 => 1.0] @@ -316,8 +315,8 @@ let # Checks that systems have the correct noise scaling terms. rn = set_default_noise_scaling(rn, 0.5) - rn1_noise_scaling = [get_noise_scaling(rx) for rx in get_rxs(rn)] - rn2_noise_scaling = [get_noise_scaling(rx) for rx in get_rxs(Catalyst.get_systems(rn)[1])] + rn1_noise_scaling = [get_noise_scaling(rx) for rx in Catalyst.get_rxs(rn)] + rn2_noise_scaling = [get_noise_scaling(rx) for rx in Catalyst.get_rxs(Catalyst.get_systems(rn)[1])] rn_noise_scaling = [get_noise_scaling(rx) for rx in reactions(rn)] @test issetequal(rn1_noise_scaling, [2.0, 0.5]) @test issetequal(rn2_noise_scaling, [5.0, 0.5]) diff --git a/test/model_simulation/u0_n_parameter_inputs.jl b/test/model_simulation/u0_n_parameter_inputs.jl index e2e5a92e3d..3d5f03dc02 100644 --- a/test/model_simulation/u0_n_parameter_inputs.jl +++ b/test/model_simulation/u0_n_parameter_inputs.jl @@ -19,7 +19,7 @@ include("../test_networks.jl") # Tests various ways to input u0 and p for various functions. let test_network = reaction_networks_standard[7] - test_osys = convert(ODESystem, test_network) + test_osys = complete(convert(ODESystem, test_network)) @parameters p1 p2 p3 k1 k2 k3 v1 K1 d1 d2 d3 d4 d5 @species X1(t) X2(t) X3(t) X4(t) X5(t) X6(t) X(t) diff --git a/test/programmatic_model_creation/component_based_model_creation.jl b/test/programmatic_model_creation/component_based_model_creation.jl index f50eac9ebe..f3d4f75a28 100644 --- a/test/programmatic_model_creation/component_based_model_creation.jl +++ b/test/programmatic_model_creation/component_based_model_creation.jl @@ -26,7 +26,8 @@ let specs = [m, P, R] pars = [α₀, α, K, n, δ, β, μ] - @named rs = ReactionSystem(rxs, t, specs, pars; complete = false) + @named rs = ReactionSystem(rxs, t, specs, pars) + rs = complete(rs) # Using ODESystem components. @named sys₁ = convert(ODESystem, rs; include_zero_odes = false) @@ -77,14 +78,15 @@ let @test all(isapprox.(sol(tvs, idxs = sys₁.P), sol2(tvs, idxs = 4), atol = 1e-4)) # Using ReactionSystem components. - @named sys₁ = ReactionSystem(rxs, t, specs, pars; complete = false) - @named sys₂ = ReactionSystem(rxs, t, specs, pars; complete = false) - @named sys₃ = ReactionSystem(rxs, t, specs, pars; complete = false) + @named sys₁ = ReactionSystem(rxs, t, specs, pars) + @named sys₂ = ReactionSystem(rxs, t, specs, pars) + @named sys₃ = ReactionSystem(rxs, t, specs, pars) connections = [ParentScope(sys₁.R) ~ ParentScope(sys₃.P), ParentScope(sys₂.R) ~ ParentScope(sys₁.P), ParentScope(sys₃.R) ~ ParentScope(sys₂.P)] @named csys = ODESystem(connections, t, [], []) - @named repressilator = ReactionSystem(t; systems = [csys, sys₁, sys₂, sys₃], complete = false) + @named repressilator = ReactionSystem(t; systems = [csys, sys₁, sys₂, sys₃]) + repressilator = complete(repressilator) @named oderepressilator2 = convert(ODESystem, repressilator, include_zero_odes = false) sys2 = structural_simplify(oderepressilator2) # FAILS currently oprob = ODEProblem(sys2, u₀, tspan, pvals) @@ -93,9 +95,9 @@ let # Test conversion to nonlinear system. @named nsys = NonlinearSystem(connections, [], []) - @named ssrepressilator = ReactionSystem(t; systems = [nsys, sys₁, sys₂, sys₃], complete = false) - @named nlrepressilator = convert(NonlinearSystem, ssrepressilator, - include_zero_odes = false) + @named ssrepressilator = ReactionSystem(t; systems = [nsys, sys₁, sys₂, sys₃]) + ssrepressilator = complete(ssrepressilator) + @named nlrepressilator = convert(NonlinearSystem, ssrepressilator, include_zero_odes = false) sys2 = structural_simplify(nlrepressilator) @test length(equations(sys2)) <= 6 nlprob = NonlinearProblem(sys2, u₀, pvals) @@ -107,6 +109,7 @@ let # Flattening. fsys = Catalyst.flatten(ssrepressilator) + fsys = complete(fsys) @named nlrepressilator = convert(NonlinearSystem, fsys, include_zero_odes = false) sys2 = structural_simplify(nlrepressilator) @test length(equations(sys2)) <= 6 @@ -123,7 +126,8 @@ let sys₃.R ~ sys₂.P] @named csys = NonlinearSystem(connections, [sys₁.R, sys₃.P, sys₂.R, sys₁.P, sys₃.R, sys₂.P], []) - @named repressilator2 = ReactionSystem(connections, t; systems = [sys₁, sys₂, sys₃], complete = false) + @named repressilator2 = ReactionSystem(connections, t; systems = [sys₁, sys₂, sys₃]) + repressilator2 = complete(repressilator2) @named nlrepressilator = convert(NonlinearSystem, repressilator2, include_zero_odes = false) sys2 = structural_simplify(nlrepressilator) @test length(equations(sys2)) <= 6 @@ -144,6 +148,7 @@ let @test isequal(extended.a, ModelingToolkit.namespace_expr(a, extended)) @test isequal(extended.x, ModelingToolkit.namespace_expr(x, extended)) # and after conversion to an AbstractSystem + extended = complete(extended) system = convert(NonlinearSystem, extended) @test isequal(system.a, ModelingToolkit.namespace_expr(a, system)) @test isequal(system.x, ModelingToolkit.namespace_expr(x, system; ivs = independent_variables(extended))) @@ -156,6 +161,7 @@ let @test isequal(extended.a, ModelingToolkit.namespace_expr(a, extended)) @test isequal(extended.x, ModelingToolkit.namespace_expr(x, extended)) # and after conversion to an AbstractSystem. + extended = complete(extended) system = convert(NonlinearSystem, extended) @test isequal(system.a, ModelingToolkit.namespace_expr(a, system)) @test isequal(system.x, ModelingToolkit.namespace_expr(x, system; ivs = independent_variables(extended))) @@ -204,8 +210,9 @@ let extended = compose(extended, subextended) @test isequal(extended.a, ModelingToolkit.namespace_expr(a, extended)) @test isequal(extended.x, ModelingToolkit.namespace_expr(x, extended)) - odesystem = convert(ODESystem, extended) - nlsystem = convert(NonlinearSystem, extended) + extended = complete(extended) + odesystem = complete(convert(ODESystem, extended)) + nlsystem = complete(convert(NonlinearSystem, extended)) obs = Set([ModelingToolkit.observed(constraints); [ModelingToolkit.namespace_equation(o, subextended) @@ -219,8 +226,9 @@ let extended = compose(extended, subextended) @test isequal(extended.a, ModelingToolkit.namespace_expr(a, extended)) @test isequal(extended.x, ModelingToolkit.namespace_expr(x, extended)) - odesystem = convert(ODESystem, extended) - nlsystem = convert(NonlinearSystem, extended) + extended = complete(extended) + odesystem = complete(convert(ODESystem, extended)) + nlsystem = complete(convert(NonlinearSystem, extended)) obs = Set([ModelingToolkit.observed(constraints); [ModelingToolkit.namespace_equation(o, subextended) @@ -237,9 +245,10 @@ let @test all(isapprox.(sol(tvs, idxs = sys₁.P), sol2(tvs, idxs = 4), atol = 1e-4)) # Test extending with NonlinearSystem. - @named repressilator2 = ReactionSystem(t; systems = [sys₁, sys₂, sys₃], complete = false) + @named repressilator2 = ReactionSystem(t; systems = [sys₁, sys₂, sys₃]) repressilator2 = Catalyst.flatten(repressilator2) repressilator2 = extend(csys, repressilator2) + repressilator2 = complete(repressilator2) @named nlrepressilator = convert(NonlinearSystem, repressilator2, include_zero_odes = false) sys2 = structural_simplify(nlrepressilator) @test length(equations(sys2)) <= 6 @@ -259,8 +268,8 @@ let @species A(t), B(t), C(t), D(t) rxs1 = [Reaction(r₊, [A, B], [C])] rxs2 = [Reaction(r₋, [C], [A, B])] - @named rs1 = ReactionSystem(rxs1, t, [A, B, C], [r₊]; complete = false) - @named rs2 = ReactionSystem(rxs2, t, [A, B, C], [r₋]; complete = false) + @named rs1 = ReactionSystem(rxs1, t, [A, B, C], [r₊]) + @named rs2 = ReactionSystem(rxs2, t, [A, B, C], [r₋]) @named rs = extend(rs1, rs2) @test issetequal(unknowns(rs), [A, B, C]) @test issetequal(parameters(rs), [r₊, r₋]) @@ -270,7 +279,8 @@ let nseqs = [D ~ 2 * A2 + β * B2] @named ns = ODESystem(nseqs, t, [A2, B2, D], [β]) rs = compose(rs, [ns]) - osys = convert(ODESystem, rs; include_zero_odes = false) + rs = complete(rs) + osys = complete(convert(ODESystem, rs; include_zero_odes = false)) p = [r₊ => 1.0, r₋ => 2.0, ns.β => 3.0] u₀ = [A => 1.0, B => 2.0, C => 0.0] oprob = ODEProblem(structural_simplify(osys), u₀, (0.0, 10.0), p) @@ -307,13 +317,13 @@ let eqs3 = [ParentScope(A2a) ~ p3b * A3b] @named rs3 = ReactionSystem(rxs3, t, [A3a, ParentScope(A2a)], [p3a, ParentScope(p2a)]; - combinatoric_ratelaws = false, complete = false) + combinatoric_ratelaws = false) @named ns3 = NonlinearSystem(eqs3, [ParentScope(A2a), A3b], [p3b]) @named rs2 = ReactionSystem(rxs2, t, [A2a, ParentScope(A1)], [p2a, p2b], - systems = [rs3, ns3]; combinatoric_ratelaws = true, complete = false) + systems = [rs3, ns3]; combinatoric_ratelaws = true) @named ns2 = NonlinearSystem(eqs2, [ParentScope(A1), A2b], [ParentScope(p1)]) @named rs1 = ReactionSystem(rxs1, t, [A1], [p1], systems = [rs2, ns2]; - combinatoric_ratelaws = false, complete = false) + combinatoric_ratelaws = false) # Namespaced reactions. nrxs1 = [Reaction(p1, [A1], nothing)] @@ -350,7 +360,8 @@ let eqs = [D(C) ~ -b * C + a * A] @named osys = ODESystem(eqs, t, [A, C], [a, b]) rn2 = extend(osys, rn) - rnodes = convert(ODESystem, rn2) + rn2 = complete(rn2) + rnodes = complete(convert(ODESystem, rn2)) @test_throws ErrorException convert(NonlinearSystem, rn2) # Ensure right number of equations are generated. @@ -364,10 +375,12 @@ let eqs = [0 ~ -a * A + C, 0 ~ -b * C + a * A] @named nlsys = NonlinearSystem(eqs, [A, C], [a, b]) rn2 = extend(nlsys, rn) - rnodes = convert(ODESystem, rn2) - rnnlsys = convert(NonlinearSystem, rn2) + rn2 = complete(rn2) + rnodes = complete(convert(ODESystem, rn2)) + rnnlsys = complete(convert(NonlinearSystem, rn2)) @named nlsys = ODESystem(eqs, t, [A, C], [a, b]) rn2 = extend(nlsys, rn) + rn2 = complete(rn2) rnodes = convert(ODESystem, rn2) rnnlsys = convert(NonlinearSystem, rn2) end @@ -381,6 +394,7 @@ let @named rs1 = ReactionSystem(rxs1, t) @named rs2 = ReactionSystem(rxs2, t) rsc = compose(rs1, [rs2]) + rsc = complete(rsc) orsc = convert(ODESystem, rsc) @test length(equations(orsc)) == 1 end @@ -433,13 +447,13 @@ let D = default_time_deriv() rx1 = Reaction(k1*V1, [A1], [B1]) eq1 = D(V1) ~ -V1 - @named rs1 = ReactionSystem([rx1, eq1], t; complete = false) + @named rs1 = ReactionSystem([rx1, eq1], t) rx2 = Reaction(k2*V2, [A2], [B2]) eq2 = D(V2) ~ -V2 - @named rs2 = ReactionSystem([rx2, eq2], t; complete = false) + @named rs2 = ReactionSystem([rx2, eq2], t) rx3 = Reaction(k3*V3, [A3], [B3]) eq3 = D(V3) ~ -V3 - @named rs3 = ReactionSystem([rx3, eq3], t; complete = false) + @named rs3 = ReactionSystem([rx3, eq3], t) @named rs23 = compose(rs2, [rs3]) @test length(unknowns(rs23)) == 6 @test all(p -> isequal(p[1], p[2]), zip(unknowns(rs23)[1:4], species(rs23))) @@ -471,6 +485,7 @@ let (p2, q), 0 <--> Z end composed_reaction_system = compose(rn1, [rn2]) + composed_reaction_system = complete(composed_reaction_system) osys = convert(ODESystem, composed_reaction_system) parameters(osys)[1].metadata diff --git a/test/programmatic_model_creation/programmatic_model_expansion.jl b/test/programmatic_model_creation/programmatic_model_expansion.jl index 7e5e8943b5..6215d7fdcc 100644 --- a/test/programmatic_model_creation/programmatic_model_expansion.jl +++ b/test/programmatic_model_creation/programmatic_model_expansion.jl @@ -229,10 +229,10 @@ let end for networks in identical_networks - f1 = ODEFunction(convert(ODESystem, networks[1]), jac = true) - f2 = ODEFunction(convert(ODESystem, networks[2]), jac = true) - g1 = SDEFunction(convert(SDESystem, networks[1])) - g2 = SDEFunction(convert(SDESystem, networks[2])) + f1 = ODEFunction(complete(convert(ODESystem, networks[1])), jac = true) + f2 = ODEFunction(complete(convert(ODESystem, networks[2])), jac = true) + g1 = SDEFunction(complete(convert(SDESystem, networks[1]))) + g2 = SDEFunction(complete(convert(SDESystem, networks[2]))) @test networks[1] == networks[2] for factor in [1e-2, 1e-1, 1e0, 1e1] u0 = factor * rand(rng, length(get_unknowns(networks[1]))) diff --git a/test/reactionsystem_structure/reactionsystem.jl b/test/reactionsystem_structure/reactionsystem.jl index 6c4d12496a..b9b467d5d0 100644 --- a/test/reactionsystem_structure/reactionsystem.jl +++ b/test/reactionsystem_structure/reactionsystem.jl @@ -43,8 +43,9 @@ rxs = [Reaction(k[1], nothing, [A]), # 0 -> A Reaction(k[20] * t * A, [B, C], [D], [2, 1], [2]), # 2A +B -> 2C with non constant rate. ] @named rs = ReactionSystem(rxs, t, [A, B, C, D], k) -odesys = convert(ODESystem, rs) -sdesys = convert(SDESystem, rs) +rs = complete(rs) +odesys = complete(convert(ODESystem, rs)) +sdesys = complete(convert(SDESystem, rs)) # Hard coded ODE rhs. function oderhs(u, k, t) @@ -123,10 +124,11 @@ let defs = merge(Dict(def_p), Dict(def_u0)) @named rs = ReactionSystem(rxs, t, [A, B, C, D], k; defaults = defs) - odesys = convert(ODESystem, rs) - sdesys = convert(SDESystem, rs) - js = convert(JumpSystem, rs) - nlsys = convert(NonlinearSystem, rs) + rs = complete(rs) + odesys = complete(convert(ODESystem, rs)) + sdesys = complete(convert(SDESystem, rs)) + js = complete(convert(JumpSystem, rs)) + nlsys = complete(convert(NonlinearSystem, rs)) @test ModelingToolkit.get_defaults(rs) == ModelingToolkit.get_defaults(odesys) == @@ -156,7 +158,7 @@ let p = rnd_ps(rs, rng) du = oderhs(last.(u), last.(p), 0.0) G = sdenoise(last.(u), last.(p), 0.0) - sdesys = convert(SDESystem, rs) + sdesys = complete(convert(SDESystem, rs)) sf = SDEFunction{false}(sdesys, unknowns(rs), parameters(rs)) sprob = SDEProblem(rs, u, (0.0, 0.0), p) du2 = sf.f(sprob.u0, sprob.p, 0.0) @@ -203,7 +205,8 @@ let Reaction(k[20] * t * A, [D, E], [F], [2, 1], [2]), # 2D + E -> 2F with non constant rate. ] @named rs = ReactionSystem(rxs, t, [A, B, C, D, E, F], k) - js = convert(JumpSystem, rs) + rs = complete(rs) + js = complete(convert(JumpSystem, rs)) midxs = 1:14 cidxs = 15:18 @@ -286,7 +289,8 @@ let @species S(t) I(t) rxs = [Reaction(1, [S], [I]), Reaction(1.1, [S], [I])] @named rs = ReactionSystem(rxs, t, [S, I], []) - js = convert(JumpSystem, rs) + rs = complete(rs) + js = complete(convert(JumpSystem, rs)) dprob = DiscreteProblem(js, [S => 1, I => 1], (0.0, 10.0)) jprob = JumpProblem(js, dprob, Direct()) sol = solve(jprob, SSAStepper()) @@ -299,6 +303,7 @@ let rxs = [Reaction(k1 * S, [S, I], [I], [2, 3], [2]), Reaction(k2 * R, [I], [R])] @named rs = ReactionSystem(rxs, t, [S, I, R], [k1, k2]) + rs = complete(rs) @test isequal(oderatelaw(equations(rs)[1]), k1 * S * S^2 * I^3 / (factorial(2) * factorial(3))) @test_skip isequal(jumpratelaw(equations(eqs)[1]), @@ -320,26 +325,27 @@ let @test isequal2(oderatelaw(rxs[1]; combinatoric_ratelaw = false), k1 * S * S^2 * I^3) @named rs2 = ReactionSystem(rxs, t, [S, I, R], [k1, k2]; combinatoric_ratelaws = false) + rs2 = complete(rs2) # Test ODE scaling: - os = convert(ODESystem, rs) + os = complete(convert(ODESystem, rs)) @test isequal2(equations(os)[1].rhs, -2 * k1 * S * S^2 * I^3 / 12) os = convert(ODESystem, rs; combinatoric_ratelaws = false) @test isequal2(equations(os)[1].rhs, -2 * k1 * S * S^2 * I^3) - os2 = convert(ODESystem, rs2) + os2 = complete(convert(ODESystem, rs2)) @test isequal2(equations(os2)[1].rhs, -2 * k1 * S * S^2 * I^3) - os3 = convert(ODESystem, rs2; combinatoric_ratelaws = true) + os3 = complete(convert(ODESystem, rs2; combinatoric_ratelaws = true)) @test isequal2(equations(os3)[1].rhs, -2 * k1 * S * S^2 * I^3 / 12) # Test ConstantRateJump rate scaling. - js = convert(JumpSystem, rs) + js = complete(convert(JumpSystem, rs)) @test isequal2(equations(js)[1].rate, k1 * S * S * (S - 1) * I * (I - 1) * (I - 2) / 12) - js = convert(JumpSystem, rs; combinatoric_ratelaws = false) + js = complete(convert(JumpSystem, rs; combinatoric_ratelaws = false)) @test isequal2(equations(js)[1].rate, k1 * S * S * (S - 1) * I * (I - 1) * (I - 2)) - js2 = convert(JumpSystem, rs2) + js2 = complete(convert(JumpSystem, rs2)) @test isequal2(equations(js2)[1].rate, k1 * S * S * (S - 1) * I * (I - 1) * (I - 2)) - js3 = convert(JumpSystem, rs2; combinatoric_ratelaws = true) + js3 = complete(convert(JumpSystem, rs2; combinatoric_ratelaws = true)) @test isequal2(equations(js3)[1].rate, k1 * S * S * (S - 1) * I * (I - 1) * (I - 2) / 12) @@ -347,9 +353,10 @@ let rxs = [Reaction(k1, [S, I], [I], [2, 3], [2]), Reaction(k2, [I], [R])] @named rs = ReactionSystem(rxs, t, [S, I, R], [k1, k2]) - js = convert(JumpSystem, rs) + rs = complete(rs) + js = complete(convert(JumpSystem, rs)) @test isequal2(equations(js)[1].scaled_rates, k1 / 12) - js = convert(JumpSystem, rs; combinatoric_ratelaws = false) + js = complete(convert(JumpSystem, rs; combinatoric_ratelaws = false)) @test isequal2(equations(js)[1].scaled_rates, k1) # test building directly from rxs @@ -375,6 +382,7 @@ let rx2 = Reaction(2 * k, [B], [D], [1], [2.5]) rx3 = Reaction(2 * k, [B], [D], [2.5], [2]) @named mixedsys = ReactionSystem([rx1, rx2, rx3], t, [A, B, C, D], [k, b]) + mixedsys = complete(mixedsys) osys = convert(ODESystem, mixedsys; combinatoric_ratelaws = false) end @@ -435,8 +443,9 @@ let Dt(C) ~ -C, (@reaction k2, E + $C --> $C + D)] @named rs = ReactionSystem(eqs, t) + rs = complete(rs) @test all(eq -> eq isa Reaction, ModelingToolkit.get_eqs(rs)[1:4]) - osys = convert(ODESystem, rs) + osys = complete(convert(ODESystem, rs)) @test issetequal(MT.get_unknowns(osys), [B, C, D, E]) @test issetequal(MT.get_ps(osys), [k1, k2, A]) @@ -466,7 +475,8 @@ let (@reaction k1, $C + D --> E + $C), (@reaction k2, E + $C --> $C + D)] @named rs = ReactionSystem(rxs, t) # add constraint csys when supported! - ssys = convert(SDESystem, rs) + rs = complete(rs) + ssys = complete(convert(SDESystem, rs)) @test issetequal(MT.get_unknowns(ssys), [B, C, D, E]) @test issetequal(MT.get_ps(ssys), [A, k1, k2]) du1 = zeros(4) @@ -489,7 +499,8 @@ let (@reaction k1 * t, $A + $C --> B + $C), (@reaction k1 * B, 2 * $A + $C --> $C + B)] @named rs = ReactionSystem(rxs, t) - jsys = convert(JumpSystem, rs) + rs = complete(rs) + jsys = complete(convert(JumpSystem, rs)) @test issetequal(unknowns(jsys), [B, C, D, E]) @test issetequal(parameters(jsys), [k1, k2, A]) majrates = [k1 * A, k1, k2] @@ -519,6 +530,7 @@ let @named rn = ReactionSystem([(@reaction k1, $C --> B1 + $C), (@reaction k1, $A --> B2), (@reaction 10 * k1, ∅ --> B3)], t) + rn = complete(rn) dprob = DiscreteProblem(rn, [A => 10, C => 10, B1 => 0, B2 => 0, B3 => 0], (0.0, 10.0), [k1 => 1.0]) jprob = JumpProblem(rn, dprob, Direct(), save_positions = (false, false)) @@ -541,8 +553,9 @@ let rx = Reaction(k2, [S1], nothing) ∂ₜ = default_time_deriv() eq = ∂ₜ(S3) ~ k1 * S2 - @named osys = ODESystem([eq], t) + @mtkbuild osys = ODESystem([eq], t) @named rs = ReactionSystem([rx, eq], t) + rs = complete(rs) @test issetequal(unknowns(rs), [S1, S3]) @test issetequal(parameters(rs), [S2, k1, k2]) osys = convert(ODESystem, rs) @@ -556,6 +569,7 @@ let ∂ₜ = default_time_deriv() eq = S3 ~ k1 * S2 @named rs = ReactionSystem([rx, eq], t) + rs = complete(rs) @test issetequal(unknowns(rs), [S1, S3]) @test issetequal(parameters(rs), [S2, k1, k2]) osys = convert(ODESystem, rs) @@ -574,6 +588,7 @@ let ∂ₜ = default_time_deriv() eq = S3 ~ k1 * S2 @named rs = ReactionSystem([rx, eq], t) + rs = complete(rs) @test issetequal(unknowns(rs), [S1, S3]) @test issetequal(parameters(rs), [S2, k1, k2]) osys = convert(ODESystem, rs) @@ -603,6 +618,7 @@ let @species X(t)=X0 rx = Reaction(d, [X], nothing, [1], nothing) @named rs = ReactionSystem([rx], t) + rs = complete(rs) prob = ODEProblem(rs, [], (0.0, 1.0), [d => 1.0, X0 => 7.6]) @test prob[X] == 7.6 end @@ -669,6 +685,7 @@ let D = default_time_deriv() eq = D(V) ~ -k1 * k2 * V + A @named rs = ReactionSystem([eq, rx], t) + rs = complete(rs) @test length(unknowns(rs)) == 3 @test issetequal(unknowns(rs), [A, B, V]) @test length(parameters(rs)) == 2 @@ -723,23 +740,4 @@ end # Tests metadata. let @test isnothing(ModelingToolkit.get_metadata(rs)) -end - - -### Tests Completeness Designations ### -let - # Creates models with/without the `complete` input. - @parameters p d - @variables t - @species X(t) - r1 = Reaction(p, [X], nothing) - r2 = Reaction(d, nothing, [X]) - @named rs1 = ReactionSystem([r1, r2], t) - @named rs2 = ReactionSystem([r1, r2], t; complete = false) - - # Check that the correct compeltness is achived. - @test ModelingToolkit.iscomplete(rs1) - @test !ModelingToolkit.iscomplete(rs2) - rs2 = complete(rs2) - @test ModelingToolkit.iscomplete(rs2) -end +end \ No newline at end of file From bedc46388d2a5a6260a6e405a66a7cd3738da0d3 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 29 Mar 2024 12:11:10 -0400 Subject: [PATCH 23/51] make @reaction_network go through @network_component --- src/reaction_network.jl | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/src/reaction_network.jl b/src/reaction_network.jl index bf885b4d22..e3c9ca9ef0 100644 --- a/src/reaction_network.jl +++ b/src/reaction_network.jl @@ -148,35 +148,10 @@ emptyrn = @reaction_network ReactionSystems generated through `@reaction_network` are compelte. """ -macro reaction_network(name::Symbol, ex::Expr) - :(complete($(make_reaction_system(MacroTools.striplines(ex); name = :($(QuoteNode(name))))))) +macro reaction_network(args...) + return :(complete(@network_component $(args... ))) end -# allows @reaction_network $name begin ... to interpolate variables storing a name -macro reaction_network(name::Expr, ex::Expr) - :(complete($(make_reaction_system(MacroTools.striplines(ex); name = :($(esc(name.args[1]))))))) -end - -macro reaction_network(ex::Expr) - ex = MacroTools.striplines(ex) - - # no name but equations: @reaction_network begin ... end ... - if ex.head == :block - :(complete($(make_reaction_system(ex)))) - else # empty but has interpolated name: @reaction_network $name - networkname = :($(esc(ex.args[1]))) - return Expr(:block, :(@parameters t), - :(complete(ReactionSystem(Reaction[], t, [], []; name = $networkname)))) - end -end - -#Returns a empty network (with, or without, a declared name) -macro reaction_network(name::Symbol = gensym(:ReactionSystem)) - return Expr(:block, :(@parameters t), - :(complete(ReactionSystem(Reaction[], t, [], []; name = $(QuoteNode(name)))))) -end - - """ @network_component From c077cd62fe8e2e7098561bcd365de46b66a5642c Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 29 Mar 2024 12:12:04 -0400 Subject: [PATCH 24/51] format fix --- src/reaction_network.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reaction_network.jl b/src/reaction_network.jl index e3c9ca9ef0..b5b3424a60 100644 --- a/src/reaction_network.jl +++ b/src/reaction_network.jl @@ -146,7 +146,7 @@ emptyrn = @reaction_network empty emptyrn = @reaction_network ``` -ReactionSystems generated through `@reaction_network` are compelte. +ReactionSystems generated through `@reaction_network` are complete. """ macro reaction_network(args...) return :(complete(@network_component $(args... ))) From 7fcc82bc63fbf36df3c9fd3486adb03069499b81 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 29 Mar 2024 12:22:02 -0400 Subject: [PATCH 25/51] revert reaction_network reroute through network_component --- src/reaction_network.jl | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/reaction_network.jl b/src/reaction_network.jl index b5b3424a60..21686edb14 100644 --- a/src/reaction_network.jl +++ b/src/reaction_network.jl @@ -148,8 +148,35 @@ emptyrn = @reaction_network ReactionSystems generated through `@reaction_network` are complete. """ -macro reaction_network(args...) - return :(complete(@network_component $(args... ))) +# macro reaction_network(args...) +# return :(complete(@network_component $(args... ))) +# end +macro reaction_network(name::Symbol, ex::Expr) + complete(make_reaction_system(MacroTools.striplines(ex); name = :($(QuoteNode(name))))) +end + +# allows @reaction_network $name begin ... to interpolate variables storing a name +macro reaction_network(name::Expr, ex::Expr) + complete(make_reaction_system(MacroTools.striplines(ex); name = :($(esc(name.args[1]))))) +end + +macro reaction_network(ex::Expr) + ex = MacroTools.striplines(ex) + + # no name but equations: @reaction_network begin ... end ... + if ex.head == :block + complete(make_reaction_system(ex)) + else # empty but has interpolated name: @reaction_network $name + networkname = :($(esc(ex.args[1]))) + return Expr(:block, :(@parameters t), + :(complete(ReactionSystem(Reaction[], t, [], []; name = $networkname)))) + end +end + +#Returns a empty network (with, or without, a declared name) +macro reaction_network(name::Symbol = gensym(:ReactionSystem)) + return Expr(:block, :(@parameters t), + :(complete(ReactionSystem(Reaction[], t, [], []; name = $(QuoteNode(name)))))) end """ @@ -161,7 +188,7 @@ macro network_component(name::Symbol, ex::Expr) make_reaction_system(MacroTools.striplines(ex); name = :($(QuoteNode(name)))) end -# allows @reaction_network $name begin ... to interpolate variables storing a name +# allows @network_component $name begin ... to interpolate variables storing a name macro network_component(name::Expr, ex::Expr) make_reaction_system(MacroTools.striplines(ex); name = :($(esc(name.args[1])))) end @@ -169,17 +196,17 @@ end macro network_component(ex::Expr) ex = MacroTools.striplines(ex) - # no name but equations: @reaction_network begin ... end ... + # no name but equations: @network_component begin ... end ... if ex.head == :block make_reaction_system(ex) - else # empty but has interpolated name: @reaction_network $name + else # empty but has interpolated name: @network_component $name networkname = :($(esc(ex.args[1]))) return Expr(:block, :(@parameters t), :(ReactionSystem(Reaction[], t, [], []; name = $networkname))) end end -#Returns a empty network (with, or without, a declared name) +# Returns a empty network (with, or without, a declared name) macro network_component(name::Symbol = gensym(:ReactionSystem)) return Expr(:block, :(@parameters t), :(ReactionSystem(Reaction[], t, [], []; name = $(QuoteNode(name))))) From d62424bf5659fa05ada758cb2e18e21c3117969a Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 29 Mar 2024 12:30:26 -0400 Subject: [PATCH 26/51] format fix --- src/reaction_network.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/reaction_network.jl b/src/reaction_network.jl index 21686edb14..c4fd3324ee 100644 --- a/src/reaction_network.jl +++ b/src/reaction_network.jl @@ -152,12 +152,12 @@ ReactionSystems generated through `@reaction_network` are complete. # return :(complete(@network_component $(args... ))) # end macro reaction_network(name::Symbol, ex::Expr) - complete(make_reaction_system(MacroTools.striplines(ex); name = :($(QuoteNode(name))))) + :(complete($(make_reaction_system(MacroTools.striplines(ex); name = :($(QuoteNode(name))))))) end -# allows @reaction_network $name begin ... to interpolate variables storing a name +# Allows @reaction_network $name begin ... to interpolate variables storing a name. macro reaction_network(name::Expr, ex::Expr) - complete(make_reaction_system(MacroTools.striplines(ex); name = :($(esc(name.args[1]))))) + :(complete($(make_reaction_system(MacroTools.striplines(ex); name = :($(esc(name.args[1]))))))) end macro reaction_network(ex::Expr) @@ -165,7 +165,7 @@ macro reaction_network(ex::Expr) # no name but equations: @reaction_network begin ... end ... if ex.head == :block - complete(make_reaction_system(ex)) + :(complete($(make_reaction_system(ex)))) else # empty but has interpolated name: @reaction_network $name networkname = :($(esc(ex.args[1]))) return Expr(:block, :(@parameters t), @@ -173,7 +173,7 @@ macro reaction_network(ex::Expr) end end -#Returns a empty network (with, or without, a declared name) +# Returns a empty network (with, or without, a declared name). macro reaction_network(name::Symbol = gensym(:ReactionSystem)) return Expr(:block, :(@parameters t), :(complete(ReactionSystem(Reaction[], t, [], []; name = $(QuoteNode(name)))))) @@ -188,7 +188,7 @@ macro network_component(name::Symbol, ex::Expr) make_reaction_system(MacroTools.striplines(ex); name = :($(QuoteNode(name)))) end -# allows @network_component $name begin ... to interpolate variables storing a name +# Allows @network_component $name begin ... to interpolate variables storing a name. macro network_component(name::Expr, ex::Expr) make_reaction_system(MacroTools.striplines(ex); name = :($(esc(name.args[1])))) end @@ -206,7 +206,7 @@ macro network_component(ex::Expr) end end -# Returns a empty network (with, or without, a declared name) +# Returns a empty network (with, or without, a declared name). macro network_component(name::Symbol = gensym(:ReactionSystem)) return Expr(:block, :(@parameters t), :(ReactionSystem(Reaction[], t, [], []; name = $(QuoteNode(name))))) From 4772d9699f4454f901f68f9d71f6ff9c21294f46 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 29 Mar 2024 12:57:55 -0400 Subject: [PATCH 27/51] update --- .../bifurcation_kit_extension.jl | 2 +- .../homotopy_continuation_extension.jl | 2 +- src/spatial_reaction_systems/spatial_ODE_systems.jl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/CatalystBifurcationKitExtension/bifurcation_kit_extension.jl b/ext/CatalystBifurcationKitExtension/bifurcation_kit_extension.jl index 07453151fd..f99389c6df 100644 --- a/ext/CatalystBifurcationKitExtension/bifurcation_kit_extension.jl +++ b/ext/CatalystBifurcationKitExtension/bifurcation_kit_extension.jl @@ -13,7 +13,7 @@ function BK.BifurcationProblem(rs::ReactionSystem, u0_bif, ps, bif_par, args...; # Creates NonlinearSystem. Catalyst.conservationlaw_errorcheck(rs, vcat(ps, u0)) - nsys = convert(NonlinearSystem, rs; remove_conserved=true, defaults=Dict(u0)) + nsys = complete(convert(NonlinearSystem, rs; remove_conserved=true, defaults=Dict(u0))) # Makes BifurcationProblem (this call goes through the ModelingToolkit-based BifurcationKit extension). return BK.BifurcationProblem(nsys, u0_bif, ps, bif_par, args...; plot_var=plot_var, diff --git a/ext/CatalystHomotopyContinuationExtension/homotopy_continuation_extension.jl b/ext/CatalystHomotopyContinuationExtension/homotopy_continuation_extension.jl index f9c1a5c19b..fb5c6ae782 100644 --- a/ext/CatalystHomotopyContinuationExtension/homotopy_continuation_extension.jl +++ b/ext/CatalystHomotopyContinuationExtension/homotopy_continuation_extension.jl @@ -45,7 +45,7 @@ end # For a given reaction system, parameter values, and initial conditions, find the polynomial that HC solves to find steady states. function steady_state_polynomial(rs::ReactionSystem, ps, u0) rs = Catalyst.expand_registered_functions(rs) - ns = convert(NonlinearSystem, rs; remove_conserved = true) + ns = complete(convert(NonlinearSystem, rs; remove_conserved = true)) pre_varmap = [symmap_to_varmap(rs,u0)..., symmap_to_varmap(rs,ps)...] Catalyst.conservationlaw_errorcheck(rs, pre_varmap) p_vals = ModelingToolkit.varmap_to_vars(pre_varmap, parameters(ns); defaults = ModelingToolkit.defaults(ns)) diff --git a/src/spatial_reaction_systems/spatial_ODE_systems.jl b/src/spatial_reaction_systems/spatial_ODE_systems.jl index f0d71383ba..b967dd29fb 100644 --- a/src/spatial_reaction_systems/spatial_ODE_systems.jl +++ b/src/spatial_reaction_systems/spatial_ODE_systems.jl @@ -156,7 +156,7 @@ function build_odefunction(lrs::LatticeReactionSystem, vert_ps::Vector{Vector{T} transport_rates = make_sidxs_to_transrate_map(vert_ps, edge_ps, lrs) # Prepares the Jacobian and forcing functions (depending on jacobian and sparsity selection). - osys = convert(ODESystem, lrs.rs; name, combinatoric_ratelaws, include_zero_odes, checks) + osys = complete(convert(ODESystem, lrs.rs; name, combinatoric_ratelaws, include_zero_odes, checks)) if jac # `build_jac_prototype` currently assumes a sparse (non-spatial) Jacobian. Hence compute this. # `LatticeTransportODEjac` currently assumes a dense (non-spatial) Jacobian. Hence compute this. From a9371d573ec068944437616e493ecfc47232a039 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 29 Mar 2024 14:03:37 -0400 Subject: [PATCH 28/51] up --- .../structural_identifiability_extension.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl b/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl index ce62b6550d..6556436387 100644 --- a/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl +++ b/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl @@ -157,8 +157,8 @@ end function make_osys(rs::ReactionSystem; remove_conserved=true) # Creates the ODESystem corresponding to the ReactionSystem (expanding functions and flattening it). # Creates a list of the systems all symbols (unknowns and parameters). - rs = Catalyst.expand_registered_functions(flatten(rs)) - osys = convert(ODESystem, rs; remove_conserved) + rs = complete(Catalyst.expand_registered_functions(flatten(rs))) + osys = complete(convert(ODESystem, rs; remove_conserved)) vars = [unknowns(rs); parameters(rs)] # Computes equations for system conservation laws. From b42bb62445ace3a017140645cf128bc94356a289 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 29 Mar 2024 15:29:48 -0400 Subject: [PATCH 29/51] Improve documentation on completeness. --- .../catalyst_functionality/compositional_modeling.md | 10 +++++----- .../programmatic_CRN_construction.md | 3 +++ .../introduction_to_catalyst.md | 6 ++++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/docs/src/catalyst_functionality/compositional_modeling.md b/docs/src/catalyst_functionality/compositional_modeling.md index bd5d552a94..66d327474c 100644 --- a/docs/src/catalyst_functionality/compositional_modeling.md +++ b/docs/src/catalyst_functionality/compositional_modeling.md @@ -5,7 +5,7 @@ can construct the earlier repressilator model by composing together three identically repressed genes, and how to use compositional modeling to create compartments. -## A note on *completeness* +## [A note on *completeness*](@id completeness_note) Catalyst `ReactionSystem` can either be *complete* or *incomplete*. When created using the `@reaction_network` DSL they are *created as complete*. Here, only complete `ReactionSystem`s can be used to create the various problem types (e.g. `ODEProblem`). However, only incomplete `ReactionSystem`s can be composed using the features described below. Hence, for compositional modeling, `ReactionSystem` must be created as incomplete, and later set to complete before simulation. To create incomplete `ReactionSystem`s using the DSL as complete, use the `@network_component` instead of `@reaction_network`: @@ -15,13 +15,13 @@ degradation_component = @network_component begin d, X --> 0 end ``` -Or when created directly, use the `complete = false` argument: +Alternatively all `ReactionSystem`s created programmatically are incomplete: ```@example ex0 @parameters d @variable t @species X(t) rx = Reaction(d, [X], nothing) -@named degradation_component = ReactionSystem([rs], t; complete = false) +@named degradation_component = ReactionSystem([rs], t) ``` We can test whether a system is complete using the `ModelingToolkit.iscomplete` function: ```@example ex0 @@ -83,7 +83,7 @@ t = default_t() @parameters k @species A(t), B(t), C(t) rxs = [Reaction(k, [A,B], [C])] -@named rn = ReactionSystem(rxs, t; systems = [newrn, newestrn], complete = false) +@named rn = ReactionSystem(rxs, t; systems = [newrn, newestrn]) ``` Catalyst provides several different accessors for getting information from a @@ -147,7 +147,7 @@ t = default_t() @named G1 = repressed_gene(; R=ParentScope(G3₊P)) @named G2 = repressed_gene(; R=ParentScope(G1.P)) @named G3 = repressed_gene(; R=ParentScope(G2.P)) -@named repressilator = ReactionSystem(t; systems=[G1,G2,G3], complete = false) +@named repressilator = ReactionSystem(t; systems=[G1,G2,G3]) ``` Notice, in this system each gene is a child node in the system graph of the repressilator ```julia diff --git a/docs/src/catalyst_functionality/programmatic_CRN_construction.md b/docs/src/catalyst_functionality/programmatic_CRN_construction.md index c3a5a2b724..507ef458a7 100644 --- a/docs/src/catalyst_functionality/programmatic_CRN_construction.md +++ b/docs/src/catalyst_functionality/programmatic_CRN_construction.md @@ -61,6 +61,9 @@ system to be the same as the name of the variable storing the system. Alternatively, one can use the `name = :repressilator` keyword argument to the `ReactionSystem` constructor. +!!! warn + All `ReactionSystem`s created programmatically (i.e. by calling `ReactionSystem` with some input, rather than using `@reaction_network`) are created as *incomplete*. To simulate them, they must first be made *complete*. This can be done using the `complete` function, i.e. by calling `repressilator = complete(repressilator)`. An expanded description on *completeness* can be found [here](@ref completeness_note). + We can check that this is the same model as the one we defined via the DSL as follows (this requires that we use the same names for rates, species and the system) diff --git a/docs/src/introduction_to_catalyst/introduction_to_catalyst.md b/docs/src/introduction_to_catalyst/introduction_to_catalyst.md index ecda7a8561..f922611160 100644 --- a/docs/src/introduction_to_catalyst/introduction_to_catalyst.md +++ b/docs/src/introduction_to_catalyst/introduction_to_catalyst.md @@ -96,6 +96,7 @@ Let's now use our `ReactionSystem` to generate and solve a corresponding mass action ODE model. We first convert the system to a `ModelingToolkit.ODESystem` by ```@example tut1 +repressilator = complete(repressilator) odesys = convert(ODESystem, repressilator) ``` (Here Latexify is used automatically to display `odesys` in Latex within Markdown @@ -138,6 +139,7 @@ By passing `repressilator` directly to the `ODEProblem`, Catalyst has to (internally) call `convert(ODESystem, repressilator)` again to generate the symbolic ODEs. We could instead pass `odesys` directly like ```@example tut1 +odesys = complete(odesys) oprob2 = ODEProblem(odesys, u₀symmap, tspan, psymmap) nothing # hide ``` @@ -152,6 +154,10 @@ underlying problem. always be converted to `symbolic` mappings using [`symmap_to_varmap`](@ref), see the [Basic Syntax](@ref basic_examples) section for more details. + +!!! note + Above we have used `repressilator = complete(repressilator)` and `odesys = complete(odesys)` to mark these systems as *complete*. This must be done before any system is given as input to a `convert` call or some problem type. Models created through the @reaction_network` DSL (which is introduced elsewhere, and primarily used throughout these documentation) are created as complete. Hence `complete` does not need to be called on these models. An expanded description on *completeness* can be found [here](@ref completeness_note). + At this point we are all set to solve the ODEs. We can now use any ODE solver from within the [DifferentialEquations.jl](https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/) From adb2d292e7e97e6a44526a55d3e164ec40e2203b Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 29 Mar 2024 17:23:41 -0400 Subject: [PATCH 30/51] up --- src/reaction_network.jl | 1 - src/reactionsystem.jl | 16 +- test/dsl/dsl_model_construction.jl | 16 +- test/dsl/dsl_options.jl | 962 +++++++++++------- test/miscellaneous_tests/api.jl | 1 - .../symbolic_stoichiometry.jl | 8 +- test/model_simulation/simulate_SDEs.jl | 77 +- test/model_simulation/simulate_jumps.jl | 8 +- .../higher_order_reactions.jl | 4 +- .../reactionsystem.jl | 8 +- 10 files changed, 653 insertions(+), 448 deletions(-) diff --git a/src/reaction_network.jl b/src/reaction_network.jl index c4fd3324ee..3ca59d4c6a 100644 --- a/src/reaction_network.jl +++ b/src/reaction_network.jl @@ -361,7 +361,6 @@ function make_reaction_system(ex::Expr; name = :(gensym(:ReactionSystem))) default_reaction_metadata = :([]) check_default_noise_scaling!(default_reaction_metadata, options) compound_expr, compound_species = read_compound_options(options) - check_default_noise_scaling!(default_reaction_metadata, options) # Parses reactions, species, and parameters. reactions = get_reactions(reaction_lines) diff --git a/src/reactionsystem.jl b/src/reactionsystem.jl index 3de292035d..6256fa305d 100644 --- a/src/reactionsystem.jl +++ b/src/reactionsystem.jl @@ -1503,7 +1503,7 @@ function Base.convert(::Type{<:ODESystem}, rs::ReactionSystem; name = nameof(rs) include_zero_odes) eqs, sts, ps, obs, defs = addconstraints!(eqs, fullrs, ists, ispcs; remove_conserved) - osys = ODESystem(eqs, get_iv(fullrs), sts, ps; + ODESystem(eqs, get_iv(fullrs), sts, ps; observed = obs, name, defaults = _merge(defaults,defs), @@ -1511,7 +1511,6 @@ function Base.convert(::Type{<:ODESystem}, rs::ReactionSystem; name = nameof(rs) continuous_events = MT.get_continuous_events(fullrs), discrete_events = MT.get_discrete_events(fullrs), kwargs...) - return osys end """ @@ -1546,13 +1545,12 @@ function Base.convert(::Type{<:NonlinearSystem}, rs::ReactionSystem; name = name error_if_constraint_odes(NonlinearSystem, fullrs) eqs, sts, ps, obs, defs = addconstraints!(eqs, fullrs, ists, ispcs; remove_conserved) - nlsys = NonlinearSystem(eqs, sts, ps; + NonlinearSystem(eqs, sts, ps; name, observed = obs, defaults = _merge(defaults,defs), checks, kwargs...) - return nlsys end """ @@ -1596,7 +1594,7 @@ function Base.convert(::Type{<:SDESystem}, rs::ReactionSystem; @info "Boundary condition species detected. As constraint equations are not currently supported when converting to SDESystems, the resulting system will be undetermined. Consider using constant species instead." end - ssys = SDESystem(eqs, noiseeqs, get_iv(flatrs), sts, ps; + SDESystem(eqs, noiseeqs, get_iv(flatrs), sts, ps; observed = obs, name, defaults = defs, @@ -1604,7 +1602,6 @@ function Base.convert(::Type{<:SDESystem}, rs::ReactionSystem; continuous_events = MT.get_continuous_events(flatrs), discrete_events = MT.get_discrete_events(flatrs), kwargs...) - return ssys end """ @@ -1630,7 +1627,7 @@ function Base.convert(::Type{<:JumpSystem}, rs::ReactionSystem; name = nameof(rs remove_conserved = nothing, checks = false, default_u0 = Dict(), default_p = Dict(), defaults = _merge(Dict(default_u0), Dict(default_p)), kwargs...) - iscomplete(rs) || error(COMPLETENESS_ERROR) + iscomplete(rs) || error(COMPLETENESS_ERROR) spatial_convert_err(rs::ReactionSystem, JumpSystem) (remove_conserved !== nothing) && @@ -1649,14 +1646,13 @@ function Base.convert(::Type{<:JumpSystem}, rs::ReactionSystem; name = nameof(rs any(isbc, get_unknowns(flatrs)) && (sts = vcat(sts, filter(isbc, get_unknowns(flatrs)))) ps = get_ps(flatrs) - jsys = JumpSystem(eqs, get_iv(flatrs), sts, ps; + JumpSystem(eqs, get_iv(flatrs), sts, ps; observed = MT.observed(flatrs), name, defaults = _merge(defaults,MT.defaults(flatrs)), checks, discrete_events = MT.discrete_events(flatrs), kwargs...) - return jsys end ### Converts a reaction system to ODE or SDE problems ### @@ -1821,7 +1817,7 @@ function MT.flatten(rs::ReactionSystem; name = nameof(rs)) balanced_bc_check = false, spatial_ivs = get_sivs(rs), continuous_events = MT.continuous_events(rs), - discrete_events = MT.continuous_events(rs)) + discrete_events = MT.discrete_events(rs)) end """ diff --git a/test/dsl/dsl_model_construction.jl b/test/dsl/dsl_model_construction.jl index 8c4d7b3586..e777a2b998 100644 --- a/test/dsl/dsl_model_construction.jl +++ b/test/dsl/dsl_model_construction.jl @@ -1,4 +1,4 @@ -### Fetch Packages and Reaction Networks ### +### Prepares Tests ### # Fetch packages. using DiffEqBase, Catalyst, Random, Test @@ -203,7 +203,7 @@ let end end -# Compares networks to networks written without parameters +# Compares networks to networks written without parameters. let identical_networks_3 = Vector{Pair}() parameter_sets = [] @@ -421,3 +421,15 @@ let @test_throws LoadError @eval @reaction k, 0 --> im @test_throws LoadError @eval @reaction k, 0 --> nothing end + + +### Other Tests ### + +# Test names work. +let + rn = @reaction_network SIR1 begin + k1, S + I --> 2I + k2, I --> R + end + @test nameof(rn) == :SIR1 +end \ No newline at end of file diff --git a/test/dsl/dsl_options.jl b/test/dsl/dsl_options.jl index a4c19467b8..ad791705cd 100644 --- a/test/dsl/dsl_options.jl +++ b/test/dsl/dsl_options.jl @@ -1,455 +1,637 @@ +#! format: off + ### Prepares Tests ### # Fetch packages. -using DiffEqBase, Catalyst, Random, Test -using ModelingToolkit: operation, istree, get_unknowns, get_ps, get_eqs, get_systems, - get_iv, nameof - -# Sets stable rng number. -using StableRNGs -rng = StableRNG(12345) +using Catalyst, ModelingToolkit, OrdinaryDiffEq, Plots, Test # Sets the default `t` to use. t = default_t() -# Fetch test networks and functions. -include("../test_networks.jl") -include("../test_functions.jl") +### Tests `@parameters` and `@species` Options ### -### Declares Testing Functions ### +# Test creating networks with/without options. +let + @reaction_network begin (k1, k2), A <--> B end + @reaction_network begin + @parameters k1 k2 + (k1, k2), A <--> B + end + @reaction_network begin + @parameters k1 k2 + @species A(t) B(t) + (k1, k2), A <--> B + end + @reaction_network begin + @species A(t) B(t) + (k1, k2), A <--> B + end + + @reaction_network begin + @parameters begin + k1 + k2 + end + (k1, k2), A <--> B + end + @reaction_network begin + @species begin + A(t) + B(t) + end + (k1, k2), A <--> B + end + @reaction_network begin + @parameters begin + k1 + k2 + end + @species begin + A(t) + B(t) + end + (k1, k2), A <--> B + end -function unpacksys(sys) - get_eqs(sys), get_iv(sys), get_unknowns(sys), get_ps(sys), nameof(sys), get_systems(sys) + n1 = @reaction_network rnname begin (k1, k2), A <--> B end + n2 = @reaction_network rnname begin + @parameters k1 k2 + (k1, k2), A <--> B + end + n3 = @reaction_network rnname begin + @species A(t) B(t) + (k1, k2), A <--> B + end + n4 = @reaction_network rnname begin + @parameters k1 k2 + @species A(t) B(t) + (k1, k2), A <--> B + end + n5 = @reaction_network rnname begin + (k1, k2), A <--> B + @parameters k1 k2 + end + n6 = @reaction_network rnname begin + (k1, k2), A <--> B + @species A(t) B(t) + end + n7 = @reaction_network rnname begin + (k1, k2), A <--> B + @parameters k1 k2 + @species A(t) B(t) + end + n8 = @reaction_network rnname begin + @parameters begin + k1 + k2 + end + (k1, k2), A <--> B + end + n9 = @reaction_network rnname begin + @species begin + A(t) + B(t) + end + (k1, k2), A <--> B + end + n10 = @reaction_network rnname begin + @parameters begin + k1 + k2 + end + @species begin + A(t) + B(t) + end + (k1, k2), A <--> B + end + @test all(==(n1), (n2, n3, n4, n5, n6, n7, n8, n9, n10)) end -opname(x) = istree(x) ? nameof(operation(x)) : nameof(x) -alleq(xs, ys) = all(isequal(x, y) for (x, y) in zip(xs, ys)) +# Tests that when either @species or @parameters is given, the other is inferred properly. +let + rn1 = @reaction_network begin + k*X, A + B --> 0 + end + @test issetequal(species(rn1), @species A(t) B(t)) + @test issetequal(parameters(rn1), @parameters k X) -# Gets all the reactants in a set of equations. -function all_reactants(eqs) - all_reactants = [] - for eq in eqs - append!(all_reactants, opname.(eq.substrates)) - append!(all_reactants, opname.(eq.products)) + rn2 = @reaction_network begin + @species A(t) B(t) X(t) + k*X, A + B --> 0 end - return Set{Symbol}(unique(all_reactants)) -end + @test issetequal(species(rn2), @species A(t) B(t) X(t)) + @test issetequal(parameters(rn2), @parameters k) -# Gets all parameters (where every reaction rate is constant) -function all_parameters(eqs) - return Set(unique(map(eq -> opname(eq.rate), eqs))) -end + rn3 = @reaction_network begin + @parameters k + k*X, A + B --> 0 + end + @test issetequal(species(rn3), @species A(t) B(t)) + @test issetequal(parameters(rn3), @parameters k X) -# Perform basic tests. -function basic_test(rn, N, unknowns_syms, p_syms) - eqs, iv, unknowns, ps, name, systems = unpacksys(rn) - @test length(eqs) == N - @test opname(iv) == :t - @test length(unknowns) == length(unknowns_syms) - @test issetequal(map(opname, unknowns), unknowns_syms) - @test all_reactants(eqs) == Set(unknowns_syms) - @test length(ps) == length(p_syms) - @test issetequal(map(opname, ps), p_syms) -end + rn4 = @reaction_network begin + @species A(t) B(t) X(t) + @parameters k + k*X, A + B --> 0 + end + @test issetequal(species(rn4), @species A(t) B(t) X(t)) + @test issetequal(parameters(rn4), @parameters k) -### Basic Tests ### + rn5 = @reaction_network begin + @parameters k B [isconstantspecies=true] + k*X, A + B --> 0 + end + @test issetequal(species(rn5), @species A(t)) + @test issetequal(parameters(rn5), @parameters k B X) +end -# Test basic properties of networks. +# Test inferring with stoichiometry symbols and interpolation. let - basic_test(reaction_networks_standard[1], 10, [:X1, :X2, :X3], - [:p1, :p2, :p3, :k1, :k2, :k3, :k4, :d1, :d2, :d3]) - @test all_parameters(get_eqs(reaction_networks_standard[1])) == - Set([:p1, :p2, :p3, :k1, :k2, :k3, :k4, :d1, :d2, :d3]) - basic_test(reaction_networks_standard[2], 3, [:X1, :X2], [:v1, :K1, :v2, :K2, :d]) - basic_test(reaction_networks_standard[3], 10, [:X1, :X2, :X3, :X4], - [:v1, :K1, :v2, :K2, :k1, :k2, :k3, :k4, :d]) - basic_test(reaction_networks_standard[4], 8, [:X1, :X2, :X3, :X4], - [:v1, :K1, :v2, :K2, :v3, :K3, :v4, :K4, :d1, :d2, :d3, :d4]) - basic_test(reaction_networks_standard[5], 8, [:X1, :X2, :X3, :X4], - [:p, :k1, :k2, :k3, :k4, :k5, :k6, :d]) - @test all_parameters(get_eqs(reaction_networks_standard[5])) == - Set([:p, :k1, :k2, :k3, :k4, :k5, :k6, :d]) - basic_test(reaction_networks_hill[1], 4, [:X1, :X2], - [:v1, :v2, :K1, :K2, :n1, :n2, :d1, :d2]) - basic_test(reaction_networks_constraint[1], 6, [:X1, :X2, :X3], - [:k1, :k2, :k3, :k4, :k5, :k6]) - basic_test(reaction_networks_real[1], 4, [:X, :Y], [:A, :B]) - basic_test(reaction_networks_weird[1], 2, [:X], [:p, :d]) - basic_test(reaction_networks_weird[2], 4, [:X, :Y, :Z], [:k1, :k2, :k3, :k4]) + @parameters k g h gg X y [isconstantspecies = true] + t = Catalyst.DEFAULT_IV + @species A(t) B(t) BB(t) C(t) + + rni = @reaction_network inferred begin + $k*X, $y + g*A + h*($gg)*B + $BB * C --> k*C + end + @test issetequal(species(rni), [A, B, BB, C]) + @test issetequal(parameters(rni), [k, g, h, gg, X, y]) + + rnii = @reaction_network inferred begin + @species BB(t) + @parameters y [isconstantspecies = true] + k*X, y + g*A + h*($gg)*B + BB * C --> k*C + end + @test rnii == rni end -# Compares networks to networks created using different arrow types. +# Tests that when some species or parameters are left out, the others are set properly. let - networks_1 = [] - networks_2 = [] - - different_arrow_1 = @reaction_network begin - (p1, p2, p3), ∅ > (X1, X2, X3) - (k1, k2), X2 ↔ X1 + 2X3 - (k3, k4), X1 ⟷ X3 - (d1, d2, d3), (X1, X2, X3) → ∅ - end - push!(networks_1, reaction_networks_standard[1]) - push!(networks_2, different_arrow_1) - - different_arrow_2 = @reaction_network begin - mmr(X2, v1, K1), ∅ → X1 - mm(X1, v2, K2), ∅ ↣ X2 - d, X1 + X2 ↦ ∅ - end - push!(networks_1, reaction_networks_standard[2]) - push!(networks_2, different_arrow_2) - - different_arrow_3 = @reaction_network begin - mm(X2, v1, K1), ∅ ⇾ X1 - mm(X3, v2, K2), ∅ ⟶ X2 - (k1, k2), X1 ⇄ X3 - (k3, k4), X3 + X2 ⇆ X4 + X1 - d, (X1, X2, X3, X4) ⟼ ∅ - end - push!(networks_1, reaction_networks_standard[3]) - push!(networks_2, different_arrow_3) - - different_arrow_4 = @reaction_network begin - mmr(X4, v1, K1), ∅ ⥟ X1 - mmr(X1, v2, K2), ∅ ⥟ X2 - mmr(X2, v3, K3), ∅ ⇀ X3 - mmr(X3, v4, K4), ∅ ⇁ X4 - (d1, d2, d3, d4), (X1, X2, X3, X4) --> ∅ - end - push!(networks_1, reaction_networks_standard[4]) - push!(networks_2, different_arrow_4) - - # Yes the name is different, I wanted one with several single direction arrows. - different_arrow_8 = @reaction_network begin - p, 2X1 < ∅ - k1, X2 ← X1 - (k2, k3), X3 ⟻ X2 - d, ∅ ↼ X3 - end - push!(networks_1, reaction_networks_standard[8]) - push!(networks_2, different_arrow_8) - - for (rn_1, rn_2) in zip(networks_1, networks_2) - for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] - u0 = rnd_u0(rn_1, rng; factor) - p = rnd_ps(rn_1, rng; factor) - t = rand(rng) - - @test f_eval(rn_1, u0, p, t) ≈ f_eval(rn_2, u0, p, t) - @test jac_eval(rn_1, u0, p, t) ≈ jac_eval(rn_2, u0, p, t) - @test g_eval(rn_1, u0, p, t) ≈ g_eval(rn_2, u0, p, t) - end + rn6 = @reaction_network begin + @species A(t) + k*X, A + B --> 0 + end + @test issetequal(species(rn6), @species A(t) B(t)) + @test issetequal(parameters(rn6), @parameters k X) + + rn7 = @reaction_network begin + @species A(t) X(t) + k*X, A + B --> 0 + end + @test issetequal(species(rn7), @species A(t) X(t) B(t)) + @test issetequal(parameters(rn7), @parameters k) + + rn7 = @reaction_network begin + @parameters B [isconstantspecies=true] + k*X, A + B --> 0 + end + @test issetequal(species(rn7), @species A(t)) + @test issetequal(parameters(rn7), @parameters B k X) + + rn8 = @reaction_network begin + @parameters B [isconstantspecies=true] k + k*X, A + B --> 0 + end + @test issetequal(species(rn8), @species A(t)) + @test issetequal(parameters(rn8), @parameters B k X) + + rn9 = @reaction_network begin + @parameters k1 X1 + @species A1(t) B1(t) + k1*X1, A1 + B1 --> 0 + k2*X2, A2 + B2 --> 0 + end + @test issetequal(species(rn9), @species A1(t) B1(t) A2(t) B2(t)) + @test issetequal(parameters(rn9), @parameters k1 X1 k2 X2) + + rn10 = @reaction_network begin + @parameters k1 X2 B2 [isconstantspecies=true] + @species A1(t) X1(t) + k1*X1, A1 + B1 --> 0 + k2*X2, A2 + B2 --> 0 + end + @test issetequal(species(rn10), @species A1(t) X1(t) B1(t) A2(t)) + @test issetequal(parameters(rn10), @parameters k1 X2 B2 k2) + + rn11 = @reaction_network begin + @parameters k1 k2 + @species X1(t) + k1*X1, A1 + B1 --> 0 + k2*X2, A2 + B2 --> 0 end + @test issetequal(species(rn11), @species X1(t) A1(t) A2(t) B1(t) B2(t)) + @test issetequal(parameters(rn11), @parameters k1 k2 X2) end -# Compares two networks with different parameter/species names. +# Checks that some created networks are identical. let - # Declares network. - differently_written_5 = @reaction_network begin - q, ∅ → Y1 - (l1, l2), Y1 ⟷ Y2 - (l3, l4), Y2 ⟷ Y3 - (l5, l6), Y3 ⟷ Y4 - c, Y4 → ∅ - end - - # Computes initial conditions/parameter values. - u0_vals = rand(rng, length(species(differently_written_5))) - ps_vals = rand(rng, length(parameters(differently_written_5))) - u0_1 = [:X1 => u0_vals[1], :X2 => u0_vals[2], :X3 => u0_vals[3], :X4 => u0_vals[4]] - u0_2 = [:Y1 => u0_vals[1], :Y2 => u0_vals[2], :Y3 => u0_vals[3], :Y4 => u0_vals[4]] - ps_1 = [:p => ps_vals[1], :k1 => ps_vals[2], :k2 => ps_vals[3], :k3 => ps_vals[4], - :k4 => ps_vals[5], :k5 => ps_vals[6], :k6 => ps_vals[7], :d => ps_vals[8]] - ps_2 = [:q => ps_vals[1], :l1 => ps_vals[2], :l2 => ps_vals[3], :l3 => ps_vals[4], - :l4 => ps_vals[5], :l5 => ps_vals[6], :l6 => ps_vals[7], :c => ps_vals[8]] - t = rand(rng) - - # Checks equivalence. - rn_1 = reaction_networks_standard[5] - rn_2 = differently_written_5 - @test f_eval(rn_1, u0_1, ps_1, t) ≈ f_eval(rn_2, u0_2, ps_2, t) - @test jac_eval(rn_1, u0_1, ps_1, t) ≈ jac_eval(rn_2, u0_2, ps_2, t) - @test g_eval(rn_1, u0_1, ps_1, t) ≈ g_eval(rn_2, u0_2, ps_2, t) + rn12 = @reaction_network rnname begin (k1, k2), A <--> B end + rn13 = @reaction_network rnname begin + @parameters k1 k2 + (k1, k2), A <--> B + end + rn14 = @reaction_network rnname begin + @species A(t) B(t) + (k1, k2), A <--> B + end + rn15 = @reaction_network rnname begin + @parameters k1 k2 + @species A(t) B(t) + (k1, k2), A <--> B + end + @test all(==(rn12), (rn13, rn14, rn15)) end -# Compares networks to networks written in different ways. +# Checks that the rights things are put in vectors. let - networks_1 = [] - networks_2 = [] - - # Unfold reactions. - differently_written_6 = @reaction_network begin - p1, ∅ → X1 - p2, ∅ → X2 - k1, 2X1 → X3 - k2, X3 → 2X1 - k3, X2 + X3 → 4X4 - k4, 4X4 → X2 + X3 - k5, X4 + X1 → 2X3 - k6, 2X3 → X4 + X1 - d, X1 → ∅ - d, X2 → ∅ - d, X3 → ∅ - d, X4 → ∅ - end - push!(networks_1, reaction_networks_standard[6]) - push!(networks_2, differently_written_6) - - # Ignore mass action. - differently_written_7 = @reaction_network begin - @parameters p1 p2 p3 k1 k2 k3 v1 K1 d1 d2 d3 d4 d5 - (p1, p2, p3), ∅ ⇒ (X1, X2, X3) - (k1 * X1 * X2^2 / 2, k2 * X4), X1 + 2X2 ⟺ X4 - (mm(X3, v1, K1) * X4, k3 * X5), X4 ⇔ X5 - (d1 * X1, d2 * X2, d3 * X3, d4 * X4, d5 * X5), ∅ ⟽ (X1, X2, X3, X4, X5) - end - push!(networks_1, reaction_networks_standard[7]) - push!(networks_2, differently_written_7) - - # Ignore mass action new arrows. - differently_written_8 = @reaction_network begin - @parameters p1 p2 p3 k1 k2 k3 v1 K1 d1 d2 d3 d4 d5 - (p1, p2, p3), ∅ => (X1, X2, X3) - (k1 * X1 * X2^2 / 2, k2 * X4), X1 + 2X2 ⟺ X4 - (mm(X3, v1, K1) * X4, k3 * X5), X4 ⇔ X5 - (d1 * X1, d2 * X2, d3 * X3, d4 * X4, d5 * X5), ∅ <= (X1, X2, X3, X4, X5) - end - push!(networks_1, reaction_networks_standard[7]) - push!(networks_2, differently_written_8) - - for (rn_1, rn_2) in zip(networks_1, networks_2) - for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] - u0 = rnd_u0(rn_1, rng; factor) - p = rnd_ps(rn_1, rng; factor) - t = rand(rng) - - @test f_eval(rn_1, u0, p, t) ≈ f_eval(rn_2, u0, p, t) - @test jac_eval(rn_1, u0, p, t) ≈ jac_eval(rn_2, u0, p, t) - @test g_eval(rn_1, u0, p, t) ≈ g_eval(rn_2, u0, p, t) - end + rn18 = @reaction_network rnname begin + @parameters p d1 d2 + @species A(t) B(t) + p, 0 --> A + 1, A --> B + (d1, d2), (A, B) --> 0 + end + rn19 = @reaction_network rnname begin + p, 0 --> A + 1, A --> B + (d1, d2), (A, B) --> 0 + end + @test rn18 == rn19 + + @parameters p d1 d2 + @species A(t) B(t) + @test isequal(parameters(rn18)[1], p) + @test isequal(parameters(rn18)[2], d1) + @test isequal(parameters(rn18)[3], d2) + @test isequal(species(rn18)[1], A) + @test isequal(species(rn18)[2], B) + + rn20 = @reaction_network rnname begin + @species X(t) + @parameters S + mm(X,v,K), 0 --> Y + (k1,k2), 2Y <--> Y2 + d*Y, S*(Y2+Y) --> 0 + end + rn21 = @reaction_network rnname begin + @species X(t) Y(t) Y2(t) + @parameters v K k1 k2 d S + mm(X,v,K), 0 --> Y + (k1,k2), 2Y <--> Y2 + d*Y, S*(Y2+Y) --> 0 end + rn22 = @reaction_network rnname begin + @species X(t) Y2(t) + @parameters d k1 + mm(X,v,K), 0 --> Y + (k1,k2), 2Y <--> Y2 + d*Y, S*(Y2+Y) --> 0 + end + @test all(==(rn20), (rn21, rn22)) + @parameters v K k1 k2 d S + @species X(t) Y(t) Y2(t) + @test issetequal(parameters(rn22),[v K k1 k2 d S]) + @test issetequal(species(rn22), [X Y Y2]) end -# Compares networks to networks written without parameters, +# Tests that defaults work. let - networks_1 = [] - networks_2 = [] - parameter_sets = [] - - # Different parameter and variable names. - no_parameters_9 = @reaction_network begin - (1.5, 1, 2), ∅ ⟶ (X1, X2, X3) - (0.01, 2.3, 1001), (X1, X2, X3) ⟶ ∅ - (π, 42), X1 + X2 ⟷ X3 - (19.9, 999.99), X3 ⟷ X4 - (sqrt(3.7), exp(1.9)), X4 ⟷ X1 + X2 - end - push!(networks_1, reaction_networks_standard[9]) - push!(networks_2, no_parameters_9) - push!(parameter_sets, [:p1 => 1.5, :p2 => 1, :p3 => 2, :d1 => 0.01, :d2 => 2.3, :d3 => 1001, - :k1 => π, :k2 => 42, :k3 => 19.9, :k4 => 999.99, :k5 => sqrt(3.7), :k6 => exp(1.9)]) - - no_parameters_10 = @reaction_network begin - 0.01, ∅ ⟶ X1 - (3.1, 3.2), X1 → X2 - (0.0, 2.1), X2 → X3 - (901.0, 63.5), X3 → X4 - (7, 8), X4 → X5 - 1.0, X5 ⟶ ∅ - end - push!(networks_1, reaction_networks_standard[10]) - push!(networks_2, no_parameters_10) - push!(parameter_sets, [:p => 0.01, :k1 => 3.1, :k2 => 3.2, :k3 => 0.0, :k4 => 2.1, :k5 => 901.0, - :k6 => 63.5, :k7 => 7, :k8 => 8, :d => 1.0]) - - - for (rn_1, rn_2, p_1) in zip(networks_1, networks_2, parameter_sets) - for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] - u0 = rnd_u0(rn_1, rng; factor) - t = rand(rng) - - @test f_eval(rn_1, u0, p_1, t) ≈ f_eval(rn_2, u0, [], t) - @test jac_eval(rn_1, u0, p_1, t) ≈ jac_eval(rn_2, u0, [], t) - @test g_eval(rn_1, u0, p_1, t) ≈ g_eval(rn_2, u0, [], t) + rn26 = @reaction_network rnname begin + @parameters p=1.0 d1 d2=5 + @species A(t) B(t)=4 + p, 0 --> A + 1, A --> B + (d1, d2), (A, B) --> 0 + end + + rn27 = @reaction_network rnname begin + @parameters p1=1.0 p2=2.0 k1=4.0 k2=5.0 v=8.0 K=9.0 n=3 d=10.0 + @species X(t)=4.0 Y(t)=3.0 X2Y(t)=2.0 Z(t)=1.0 + (p1,p2), 0 --> (X,Y) + (k1,k2), 2X + Y --> X2Y + hill(X2Y,v,K,n), 0 --> Z + d, (X,Y,X2Y,Z) --> 0 + end + u0_27 = [] + p_27 = [] + + rn28 = @reaction_network rnname begin + @parameters p1=1.0 p2 k1=4.0 k2 v=8.0 K n=3 d + @species X(t)=4.0 Y(t) X2Y(t) Z(t)=1.0 + (p1,p2), 0 --> (X,Y) + (k1,k2), 2X + Y --> X2Y + hill(X2Y,v,K,n), 0 --> Z + d, (X,Y,X2Y,Z) --> 0 + end + u0_28 = symmap_to_varmap(rn28, [:p2=>2.0, :k2=>5.0, :K=>9.0, :d=>10.0]) + p_28 = symmap_to_varmap(rn28, [:Y=>3.0, :X2Y=>2.0]) + defs28 = Dict(Iterators.flatten((u0_28, p_28))) + + rn29 = @reaction_network rnname begin + @parameters p1 p2 k1 k2 v K n d + @species X(t) Y(t) X2Y(t) Z(t) + (p1,p2), 0 --> (X,Y) + (k1,k2), 2X + Y --> X2Y + hill(X2Y,v,K,n), 0 --> Z + d, (X,Y,X2Y,Z) --> 0 + end + u0_29 = symmap_to_varmap(rn29, [:p1=>1.0, :p2=>2.0, :k1=>4.0, :k2=>5.0, :v=>8.0, :K=>9.0, :n=>3, :d=>10.0]) + p_29 = symmap_to_varmap(rn29, [:X=>4.0, :Y=>3.0, :X2Y=>2.0, :Z=>1.0]) + defs29 = Dict(Iterators.flatten((u0_29, p_29))) + + @test ModelingToolkit.defaults(rn27) == defs29 + @test merge(ModelingToolkit.defaults(rn28), defs28) == ModelingToolkit.defaults(rn27) +end + +### Observables ### + +# Test basic functionality. +# Tests various types of indexing. +let + rn = @reaction_network begin + @observables begin + X ~ Xi + Xa + Y ~ Y1 + Y2 end + (p,d), 0 <--> Xi + (k1,k2), Xi <--> Xa + (k3,k4), Y1 <--> Y2 end + @unpack X, Xi, Xa, Y, Y1, Y2, p, d, k1, k2, k3, k4 = rn + + # Test that ReactionSystem have the correct properties. + @test length(species(rn)) == 4 + @test length(unknowns(rn)) == 4 + @test length(observed(rn)) == 2 + @test length(equations(rn)) == 6 + + @test isequal(observed(rn)[1], X ~ Xi + Xa) + @test isequal(observed(rn)[2], Y ~ Y1 + Y2) + + # Tests correct indexing of solution. + u0 = [Xi => 0.0, Xa => 0.0, Y1 => 1.0, Y2 => 2.0] + ps = [p => 1.0, d => 0.2, k1 => 1.5, k2 => 1.5, k3 => 5.0, k4 => 5.0] + + oprob = ODEProblem(rn, u0, (0.0, 1000.0), ps) + sol = solve(oprob, Tsit5()) + @test sol[X][end] ≈ 10.0 + @test sol[Y][end] ≈ 3.0 + @test sol[rn.X][end] ≈ 10.0 + @test sol[rn.Y][end] ≈ 3.0 + @test sol[:X][end] ≈ 10.0 + @test sol[:Y][end] ≈ 3.0 + + # Tests that observables can be used for plot indexing. + @test plot(sol; idxs=X).series_list[1].plotattributes[:y][end] ≈ 10.0 + @test plot(sol; idxs=rn.X).series_list[1].plotattributes[:y][end] ≈ 10.0 + @test plot(sol; idxs=:X).series_list[1].plotattributes[:y][end] ≈ 10.0 + @test plot(sol; idxs=[X, Y]).series_list[2].plotattributes[:y][end] ≈ 3.0 + @test plot(sol; idxs=[rn.X, rn.Y]).series_list[2].plotattributes[:y][end] ≈ 3.0 + @test plot(sol; idxs=[:X, :Y]).series_list[2].plotattributes[:y][end] ≈ 3.0 end -# Tests that reaction systems created manually and through macro are identical. +# Compares programmatic and DSL system with observables. let - identical_networks_4 = Vector{Pair}() - @parameters v1 K1 v2 K2 k1 k2 k3 k4 k5 p d t - @species X1(t) X2(t) X3(t) X4(t) X5(t) - - rxs_1 = [Reaction(p, nothing, [X1], nothing, [2]), - Reaction(k1, [X1], [X2], [1], [1]), - Reaction(k2, [X2], [X3], [1], [1]), - Reaction(k3, [X2], [X3], [1], [1]), - Reaction(d, [X3], nothing, [1], nothing)] - @named rs_1 = ReactionSystem(rxs_1, t, [X1, X2, X3], [p, k1, k2, k3, d]) - push!(identical_networks_4, reaction_networks_standard[8] => rs_1) - - rxs_2 = [Reaction(k1, [X1], [X2], [1], [1]), - Reaction(k2 * X5, [X2], [X1], [1], [1]), - Reaction(k3 * X5, [X3], [X4], [1], [1]), - Reaction(k4, [X4], [X3], [1], [1]), - Reaction(p + k5 * X2 * X3, nothing, [X5], nothing, [1]), - Reaction(d, [X5], nothing, [1], nothing)] - @named rs_2 = ReactionSystem(rxs_2, t, [X1, X2, X3, X4, X5], [k1, k2, k3, k4, p, k5, d]) - push!(identical_networks_4, reaction_networks_constraint[3] => rs_2) - - rxs_3 = [Reaction(k1, [X1], [X2], [1], [1]), - Reaction(0, [X2], [X3], [1], [1]), - Reaction(k2, [X3], [X4], [1], [1]), - Reaction(k3, [X4], [X5], [1], [1])] - @named rs_3 = ReactionSystem(rxs_3, t, [X1, X2, X3, X4, X5], [k1, k2, k3]) - push!(identical_networks_4, reaction_networks_weird[7] => rs_3) - - for networks in identical_networks_4 - @test isequal(get_iv(networks[1]), get_iv(networks[2])) - @test alleq(get_unknowns(networks[1]), get_unknowns(networks[2])) - @test alleq(get_ps(networks[1]), get_ps(networks[2])) - @test ModelingToolkit.get_systems(networks[1]) == - ModelingToolkit.get_systems(networks[2]) - @test length(get_eqs(networks[1])) == length(get_eqs(networks[2])) - for (e1, e2) in zip(get_eqs(networks[1]), get_eqs(networks[2])) - @test isequal(e1.rate, e2.rate) - @test isequal(e1.substrates, e2.substrates) - @test isequal(e1.products, e2.products) - @test isequal(e1.substoich, e2.substoich) - @test isequal(e1.prodstoich, e2.prodstoich) - @test isequal(e1.netstoich, e2.netstoich) - @test isequal(e1.only_use_rate, e2.only_use_rate) + # Model declarations. + rn_dsl = @reaction_network begin + @observables begin + X ~ x + 2x2y + Y ~ y + x2y end + k, 0 --> (x, y) + (kB, kD), 2x + y <--> x2y + d, (x,y,x2y) --> 0 end -end -### Tests Usage of Various Symbols ### + @variables X(t) Y(t) + @species x(t), y(t), x2y(t) + @parameters k kB kD d + r1 = Reaction(k, nothing, [x], nothing, [1]) + r2 = Reaction(k, nothing, [y], nothing, [1]) + r3 = Reaction(kB, [x, y], [x2y], [2, 1], [1]) + r4 = Reaction(kD, [x2y], [x, y], [1], [2, 1]) + r5 = Reaction(d, [x], nothing, [1], nothing) + r6 = Reaction(d, [y], nothing, [1], nothing) + r7 = Reaction(d, [x2y], nothing, [1], nothing) + obs_eqs = [X ~ x + 2x2y, Y ~ y + x2y] + @named rn_prog = ReactionSystem([r1, r2, r3, r4, r5, r6, r7], t, [x, y, x2y], [k, kB, kD, d]; observed = obs_eqs) + rn_prog = complete(rn_prog) + + # Make simulations. + u0 = [x => 1.0, y => 0.5, x2y => 0.0] + tspan = (0.0, 15.0) + ps = [k => 1.0, kD => 0.1, kB => 0.5, d => 5.0] + oprob_dsl = ODEProblem(rn_dsl, u0, tspan, ps) + oprob_prog = ODEProblem(rn_prog, u0, tspan, ps) + + sol_dsl = solve(oprob_dsl, Tsit5(); saveat=0.1) + sol_prog = solve(oprob_prog, Tsit5(); saveat=0.1) + + # Tests observables equal in both cases. + @test oprob_dsl[:X] == oprob_prog[:X] + @test oprob_dsl[:Y] == oprob_prog[:Y] + @test sol_dsl[:X] == sol_prog[:X] + @test sol_dsl[:Y] == sol_prog[:Y] +end -# Tests that time is handled properly. -let - time_network = @reaction_network begin - (t, k2), X1 ↔ X2 - (k3, t), X2 ↔ X3 - (t, k6), X3 ↔ X1 +# Tests for complicated observable formula. +# Tests using a single observable (without begin/end statement). +# Tests using observable component not part of reaction. +# Tests using parameters in observables formula. +let + rn = @reaction_network begin + @parameters op_1 op_2 + @species X4(t) + @observables X ~ X1^2 + op_1*(X2 + 2X3) + X1*X4/op_2 + p + (p,d), 0 <--> X1 + (k1,k2), X1 <--> X2 + (k3,k4), X2 <--> X3 end + + u0 = Dict([:X1 => 1.0, :X2 => 2.0, :X3 => 3.0, :X4 => 4.0]) + ps = Dict([:p => 1.0, :d => 0.2, :k1 => 1.5, :k2 => 1.5, :k3 => 5.0, :k4 => 5.0, :op_1 => 1.5, :op_2 => 1.5]) - for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] - τ = rand(rng) - u = rnd_u0(reaction_networks_constraint[1], rng; factor) - p_2 = rnd_ps(time_network, rng; factor) - p_1 = [p_2; reaction_networks_constraint[1].k1 => τ; - reaction_networks_constraint[1].k4 => τ; reaction_networks_constraint[1].k5 => τ] + oprob = ODEProblem(rn, u0, (0.0, 1000.0), ps) + sol = solve(oprob, Tsit5()) - @test f_eval(reaction_networks_constraint[1], u, p_1, τ) ≈ f_eval(time_network, u, p_2, τ) - @test jac_eval(reaction_networks_constraint[1], u, p_1, τ) ≈ jac_eval(time_network, u, p_2, τ) - @test g_eval(reaction_networks_constraint[1], u, p_1, τ) ≈ g_eval(time_network, u, p_2, τ) + @test sol[:X][1] == u0[:X1]^2 + ps[:op_1]*(u0[:X2] + 2*u0[:X3]) + u0[:X1]*u0[:X4]/ps[:op_2] + ps[:p] +end + +# Checks that ivs are correctly found. +let + rn = @reaction_network begin + @ivs t x y + @species V1(t) V2(t,x) V3(t, y) W1(t) W2(t, y) + @observables begin + V ~ V1 + 2V2 + 3V3 + W ~ W1 + W2 + end end + V,W = getfield.(observed(rn), :lhs) + @test isequal(arguments(ModelingToolkit.unwrap(V)), Any[rn.iv, rn.sivs[1], rn.sivs[2]]) + @test isequal(arguments(ModelingToolkit.unwrap(W)), Any[rn.iv, rn.sivs[2]]) end -# Check that various symbols can be used as species/parameter names. +# Checks that metadata is written properly. let - @reaction_network begin - (a, A), n ⟷ N - (b, B), o ⟷ O - (c, C), p ⟷ P - (d, D), q ⟷ Q - (e, E), r ⟷ R - (f, F), s ⟷ S - (g, G), u ⟷ U - (h, H), v ⟷ V - (j, J), w ⟷ W - (k, K), x ⟷ X - (l, L), y ⟷ Y - (m, M), z ⟷ Z - end - - @reaction_network begin (1.0, 1.0), i ⟷ T end + rn = @reaction_network rn_observed begin + @observables (X, [description="my_description"]) ~ X1 + X2 + k, 0 --> X1 + X2 + end + @test getdescription(observed(rn)[1].lhs) == "my_description" +end - @reaction_network begin - (å, Å), ü ⟷ Ü - (ä, Ä), ñ ⟷ Ñ - (ö, Ö), æ ⟷ Æ +# Declares observables implicitly/explicitly. +# Cannot test `isequal(rn1, rn2)` because the two sets of observables have some obscure Symbolics +# substructure that is different. +let + # Basic case. + rn1 = @reaction_network rn_observed begin + @observables X ~ X1 + X2 + k, 0 --> X1 + X2 + end + rn2 = @reaction_network rn_observed begin + @variables X(t) + @observables X ~ X1 + X2 + k, 0 --> X1 + X2 end + @test isequal(observed(rn1)[1].rhs, observed(rn2)[1].rhs) + @test isequal(observed(rn1)[1].lhs.metadata, observed(rn2)[1].lhs.metadata) + @test isequal(unknowns(rn1), unknowns(rn2)) + + # Case with metadata. + rn3 = @reaction_network rn_observed begin + @observables (X, [description="description"]) ~ X1 + X2 + k, 0 --> X1 + X2 + end + rn4 = @reaction_network rn_observed begin + @variables X(t) [description="description"] + @observables X ~ X1 + X2 + k, 0 --> X1 + X2 + end + @test isequal(observed(rn3)[1].rhs, observed(rn4)[1].rhs) + @test isequal(observed(rn3)[1].lhs.metadata, observed(rn4)[1].lhs.metadata) + @test isequal(unknowns(rn3), unknowns(rn4)) +end - @reaction_network begin - (α, Α), ν ⟷ Ν - (β, Β), ξ ⟷ Ξ - (γ, γ), ο ⟷ Ο - (δ, Δ), Π ⟷ Π - (ϵ, Ε), ρ ⟷ Ρ - (ζ, Ζ), σ ⟷ Σ - (η, Η), τ ⟷ Τ - (θ, Θ), υ ⟷ Υ - (ι, Ι), ϕ ⟷ Φ - (κ, κ), χ ⟷ Χ - (λ, Λ), ψ ↔ Ψ - (μ, Μ), ω ⟷ Ω +# Tests for interpolation into the observables option. +let + # Interpolation into lhs. + @species X [description="An observable"] + rn1 = @reaction_network begin + @observables $X ~ X1 + X2 + (k1, k2), X1 <--> X2 + end + @test isequal(observed(rn1)[1].lhs, X) + @test getdescription(rn1.X) == "An observable" + @test isspecies(rn1.X) + @test length(unknowns(rn1)) == 2 + + # Interpolation into rhs. + @parameters n [description="A parameter"] + @species S(t) + rn2 = @reaction_network begin + @observables Stot ~ $S + $n*Sn + (kB, kD), $n*S <--> Sn end + @unpack Stot, Sn, kD, kB = rn2 + + u0 = Dict([S => 5.0, Sn => 1.0]) + ps = Dict([n => 2, kB => 1.0, kD => 1.0]) + oprob = ODEProblem(rn2, u0, (0.0, 1.0), ps) + + @test issetequal(Symbolics.get_variables(observed(rn2)[1].rhs), [S, n, Sn]) + @test oprob[Stot] == u0[S] + ps[n]*u0[Sn] + @test length(unknowns(rn2)) == 2 end -# Test that I works as a name. +# Tests specific declaration of Observables as species/variables let rn = @reaction_network begin - k1, S + I --> 2I - k2, I --> R + @species X(t) + @variables Y(t) + @observables begin + X ~ X + 2X2 + Y ~ Y1 + Y2 + Z ~ X + Y + end + (kB,kD), 2X <--> X2 + (k1,k2), Y1 <--> Y2 end - @species I(t) - @test any(isequal(I), species(rn)) - @test any(isequal(I), unknowns(rn)) + + @test isspecies(rn.X) + @test !isspecies(rn.Y) + @test !isspecies(rn.Z) end -# Tests backwards and double arrows. -let - rn1 = @reaction_network arrowtest begin - (a1, a2), C <--> 0 - (k1, k2), A + B <--> C - b1, 0 <-- B +# Tests various erroneous declarations throw errors. +let + # Independent variable in @observables. + @test_throws Exception @eval @reaction_network begin + @observables X(t) ~ X1 + X2 + k, 0 --> X1 + X2 end - rn2 = @reaction_network arrowtest begin - a1, C --> 0 - a2, 0 --> C - k1, A + B --> C - k2, C --> A + B - b1, B --> 0 + # System with observable in observable formula. + @test_throws Exception @eval @reaction_network begin + @observables begin + X ~ X1 + X2 + X2 ~ 2X + end + (p,d), 0 <--> X1 + X2 end - @test rn1 == rn2 -end + # Multiple @observables options + @test_throws Exception @eval @reaction_network begin + @observables X ~ X1 + X2 + @observables Y ~ Y1 + Y2 + k, 0 --> X1 + X2 + k, 0 --> Y1 + Y2 + end + @test_throws Exception @eval @reaction_network begin + @observables begin + X ~ X1 + X2 + end + @observables begin + X ~ 2(X1 + X2) + end + (p,d), 0 <--> X1 + X2 + end -# Tests arrow variants in "@reaction" macro . -let - @test isequal((@reaction k, 0 --> X), (@reaction k, X <-- 0)) - @test isequal((@reaction k, 0 --> X), (@reaction k, X ⟻ 0)) - @test isequal((@reaction k, 0 --> X), (@reaction k, 0 → X)) - @test isequal((@reaction k, 0 --> X), (@reaction k, 0 ⥟ X)) -end + # Default value for compound. + @test_throws Exception @eval @reaction_network begin + @observables (X = 1.0) ~ X1 + X2 + k, 0 --> X1 + X2 + end -# Test that symbols with special mean, or that are forbidden, are handled properly. -let - test_network = @reaction_network begin t * k, X --> ∅ end - @test length(species(test_network)) == 1 - @test length(parameters(test_network)) == 1 - - test_network = @reaction_network begin π, X --> ∅ end - @test length(species(test_network)) == 1 - @test length(parameters(test_network)) == 0 - @test reactions(test_network)[1].rate == π - - test_network = @reaction_network begin pi, X --> ∅ end - @test length(species(test_network)) == 1 - @test length(parameters(test_network)) == 0 - @test reactions(test_network)[1].rate == pi - - test_network = @reaction_network begin ℯ, X --> ∅ end - @test length(species(test_network)) == 1 - @test length(parameters(test_network)) == 0 - @test reactions(test_network)[1].rate == ℯ - - @test_throws LoadError @eval @reaction im, 0 --> B - @test_throws LoadError @eval @reaction nothing, 0 --> B - @test_throws LoadError @eval @reaction k, 0 --> im - @test_throws LoadError @eval @reaction k, 0 --> nothing -end + # Forbidden symbols as observable names. + @test_throws Exception @eval @reaction_network begin + @observables t ~ t1 + t2 + k, 0 --> t1 + t2 + end + @test_throws Exception @eval @reaction_network begin + @observables im ~ i + m + k, 0 --> i + m + end + + # Non-trivial observables expression. + @test_throws Exception @eval @reaction_network begin + @observables X - X1 ~ X2 + k, 0 --> X1 + X2 + end + + # Occurrence of undeclared dependants. + @test_throws Exception @eval @reaction_network begin + @observables X ~ X1 + X2 + k, 0 --> X1 + end + + # Interpolation and explicit declaration of an observable. + @variables X(t) + @test_throws Exception @eval @reaction_network begin + @variables X(t) + @observables $X ~ X1 + X2 + (k1,k2), X1 <--> X2 + end +end \ No newline at end of file diff --git a/test/miscellaneous_tests/api.jl b/test/miscellaneous_tests/api.jl index e4ee34ea54..0fa4e64a50 100644 --- a/test/miscellaneous_tests/api.jl +++ b/test/miscellaneous_tests/api.jl @@ -528,7 +528,6 @@ let end # Test conservation law elimination. -# Conservation laws currently broken (you get stuck in an infinite loop in MTK or something). let rn = @reaction_network begin (k1, k2), A + B <--> C diff --git a/test/miscellaneous_tests/symbolic_stoichiometry.jl b/test/miscellaneous_tests/symbolic_stoichiometry.jl index 60df395937..8e1ebe9c0d 100644 --- a/test/miscellaneous_tests/symbolic_stoichiometry.jl +++ b/test/miscellaneous_tests/symbolic_stoichiometry.jl @@ -3,6 +3,10 @@ # Fetch packages. using Catalyst, JumpProcesses, LinearAlgebra, OrdinaryDiffEq, Test +# Sets stable rng number. +using StableRNGs +rng = StableRNG(12345) + # Sets the default `t` to use. t = default_t() @@ -230,7 +234,7 @@ end u0 = [S => 999, I => 1, R => 0] jsys = convert(JumpSystem, sir_ref) dprob = DiscreteProblem(jsys, u0, tspan, p1) - jprob = JumpProblem(jsys, dprob, Direct(), save_positions = (false, false)) + jprob = JumpProblem(jsys, dprob, Direct(); rng, save_positions = (false, false)) function getmean(jprob, tf) m = zeros(3) for i in 1:Nsims @@ -246,7 +250,7 @@ end pvs = Tuple(p2dict[s] for s in parameters(sir)) @test all(isequal.(parameters(sir), parameters(jsys2))) dprob2 = DiscreteProblem(jsys2, u0, tspan, pvs) - jprob2 = JumpProblem(jsys2, dprob2, Direct(), save_positions = (false, false)) + jprob2 = JumpProblem(jsys2, dprob2, Direct(); rng, save_positions = (false, false)) m2 = getmean(jprob2, tspan[2]) @test maximum(abs.(m1[2:3] .- m2[2:3]) ./ m1[2:3]) < 0.05 end diff --git a/test/model_simulation/simulate_SDEs.jl b/test/model_simulation/simulate_SDEs.jl index f3f22fc755..3bd5abcdea 100644 --- a/test/model_simulation/simulate_SDEs.jl +++ b/test/model_simulation/simulate_SDEs.jl @@ -131,8 +131,8 @@ let if nameof(rn_catalyst) != :rnc9 sprob_1 = SDEProblem(rn_catalyst, u0_1, (0.0, 1.0), ps_1) sprob_2 = SDEProblem(rn_manual.f, rn_manual.g, u0_2, (0.0, 1.0), ps_2; noise_rate_prototype = rn_manual.nrp) - sol1 = solve(sprob_1, ImplicitEM(); seed = 1234) - sol2 = solve(sprob_2, ImplicitEM(); seed = 1234) + sol1 = solve(sprob_1, ImplicitEM(); seed = rand(rng, 100)) + sol2 = solve(sprob_2, ImplicitEM(); seed = rand(rng, 100)) @test sol1[u0_sym] ≈ sol2.u end end @@ -186,26 +186,39 @@ let end # Tests with multiple noise scaling parameters directly in the macro. +# If the correct noise scaling is applied, the variance in the individual species should be (widely) different. let + # Tries with normally declared parameters. noise_scaling_network_1 = @reaction_network begin @parameters η1 η2 (k1, k2), X1 ↔ X2, ([noise_scaling=η1],[noise_scaling=η2]) end u0 = [:X1 => 1000.0, :X2 => 3000.0] - sol_1_1 = solve(SDEProblem(noise_scaling_network_1, u0, (0.0, 1000.0), [:k1 => 2.0, :k2 => 0.66, :η1 => 2.0, :η2 => 2.0]), ImplicitEM(); saveat=1.0) - sol_1_2 = solve(SDEProblem(noise_scaling_network_1, u0, (0.0, 1000.0), [:k1 => 2.0, :k2 => 0.66, :η1 => 2.0, :η2 => 0.2]), ImplicitEM(); saveat=1.0) - sol_1_3 = solve(SDEProblem(noise_scaling_network_1, u0, (0.0, 1000.0), [:k1 => 2.0, :k2 => 0.66, :η1 => 0.2, :η2 => 0.2]), ImplicitEM(); saveat=1.0) - @test var(sol_1_1[:X1]) > var(sol_1_2[:X1]) > var(sol_1_3[:X1]) + sprob_1_1 = SDEProblem(noise_scaling_network_1, u0, (0.0, 1000.0), [:k1 => 2.0, :k2 => 0.66, :η1 => 2.0, :η2 => 2.0]) + sprob_1_2 = SDEProblem(noise_scaling_network_1, u0, (0.0, 1000.0), [:k1 => 2.0, :k2 => 0.66, :η1 => 2.0, :η2 => 0.2]) + sprob_1_3 = SDEProblem(noise_scaling_network_1, u0, (0.0, 1000.0), [:k1 => 2.0, :k2 => 0.66, :η1 => 0.2, :η2 => 0.2]) + for repeat in 1:5 + sol_1_1 = solve(sprob_1_1, ImplicitEM(); saveat=1.0, seed = rand(rng, 100)) + sol_1_2 = solve(sprob_1_2, ImplicitEM(); saveat=1.0, seed = rand(rng, 100)) + sol_1_3 = solve(sprob_1_3, ImplicitEM(); saveat=1.0, seed = rand(rng, 100)) + @test var(sol_1_1[:X1]) > var(sol_1_2[:X1]) > var(sol_1_3[:X1]) + end + # Tries with an array parameter. noise_scaling_network_2 = @reaction_network begin @parameters η[1:2] (k1, k2), X1 ↔ X2, ([noise_scaling=η[1]],[noise_scaling=η[2]]) end @unpack k1, k2, η = noise_scaling_network_2 - sol_2_1 = solve(SDEProblem(noise_scaling_network_2, u0, (0.0, 1000.0), [k1 => 2.0, k2 => 0.66, η[1] => 2.0, η[2] => 2.0]), ImplicitEM(); saveat=1.0) - sol_2_2 = solve(SDEProblem(noise_scaling_network_2, u0, (0.0, 1000.0), [k1 => 2.0, k2 => 0.66, η[1] => 2.0, η[2] => 0.2]), ImplicitEM(); saveat=1.0) - sol_2_3 = solve(SDEProblem(noise_scaling_network_2, u0, (0.0, 1000.0), [k1 => 2.0, k2 => 0.66, η[1] => 0.2, η[2] => 0.2]), ImplicitEM(); saveat=1.0) - @test var(sol_2_1[:X1]) > var(sol_2_2[:X1]) > var(sol_2_3[:X1]) + sprob_2_1 = SDEProblem(noise_scaling_network_2, u0, (0.0, 1000.0), [k1 => 2.0, k2 => 0.66, η[1] => 2.0, η[2] => 2.0]) + sprob_2_2 = SDEProblem(noise_scaling_network_2, u0, (0.0, 1000.0), [k1 => 2.0, k2 => 0.66, η[1] => 2.0, η[2] => 0.2]) + sprob_2_3 = SDEProblem(noise_scaling_network_2, u0, (0.0, 1000.0), [k1 => 2.0, k2 => 0.66, η[1] => 0.2, η[2] => 0.2]) + for repeat in 1:5 + sol_2_1 = solve(sprob_2_1, ImplicitEM(); saveat=1.0, seed = rand(rng, 100)) + sol_2_2 = solve(sprob_2_2, ImplicitEM(); saveat=1.0, seed = rand(rng, 100)) + sol_2_3 = solve(sprob_2_3, ImplicitEM(); saveat=1.0, seed = rand(rng, 100)) + @test var(sol_2_1[:X1]) > var(sol_2_2[:X1]) > var(sol_2_3[:X1]) + end end # Tests using default values for noise scaling. @@ -258,12 +271,16 @@ let u0 = [:X1 => 1000.0, :X2 => 1000.0, :X3 => 1000.0] ps = [noise_scaling_network.p => 1000.0, noise_scaling_network.d => 1.0, noise_scaling_network.η1 => 1.0] - sol = solve(SDEProblem(noise_scaling_network, u0, (0.0, 1000.0), ps), ImplicitEM(); saveat=1.0) - @test var(sol[:X1]) < var(sol[:X2]) - @test var(sol[:X1]) < var(sol[:X3]) + sprob = SDEProblem(noise_scaling_network, u0, (0.0, 1000.0), ps) + + for repeat in 1:5 + sol = solve(sprob, ImplicitEM(); saveat=1.0, seed = rand(rng, 100)) + @test var(sol[:X1]) < var(sol[:X2]) + @test var(sol[:X1]) < var(sol[:X3]) + end end -# Tests using complicated noise scaling expressions +# Tests using complicated noise scaling expressions. let noise_scaling_network = @reaction_network begin @parameters η1 η2 η3 η4 @@ -280,8 +297,12 @@ let u0 = [:X1 => 1000.0, :X2 => 1000.0, :X3 => 1000.0, :X4 => 1000.0, :X5 => 1000.0, :N1 => 3.0, :N3 => 0.33] ps = [:p => 1000.0, :d => 1.0, :η1 => 1.0, :η2 => 1.4, :η3 => 0.33, :η4 => 4.0] - sol = solve(SDEProblem(noise_scaling_network, u0, (0.0, 1000.0), ps), ImplicitEM(); saveat=1.0, adaptive=false, dt=0.1) - @test var(sol[:X1]) > var(sol[:X2]) > var(sol[:X3]) > var(sol[:X4]) > var(sol[:X5]) + sprob = SDEProblem(noise_scaling_network, u0, (0.0, 1000.0), ps) + + for repeat in 1:5 + sol = solve(sprob, ImplicitEM(); saveat=1.0, adaptive=false, dt=0.1, seed = rand(rng, 100)) + @test var(sol[:X1]) > var(sol[:X2]) > var(sol[:X3]) > var(sol[:X4]) > var(sol[:X5]) + end end # Tests the `remake_noise_scaling` function. @@ -323,28 +344,20 @@ let @test issetequal(rn_noise_scaling, [2.0, 0.5, 5.0, 0.5]) end -### Checks Simulations Don't Error ### - -#Tries to create a large number of problem, ensuring there are no errors (cannot solve as solution likely to go into negatives). -let - for reaction_network in reaction_networks_all - for factor in [1e-2, 1e-1, 1e0, 1e1] - u0 = factor * rand(rng, length(unknowns(reaction_network))) - p = factor * rand(rng, length(parameters(reaction_network))) - prob = SDEProblem(reaction_network, u0, (0.0, 1.0), p) - end - end -end ### Other Tests ### # Tests simulating a network without parameters. let - no_param_network = @reaction_network begin (1.2, 5), X1 ↔ X2 end + no_param_network = @reaction_network begin + (1.2, 5), X1 ↔ X2 + end for factor in [1e3, 1e4] u0 = rnd_u0(no_param_network, rng; factor) - prob = SDEProblem(no_param_network, u0, (0.0, 1000.0)) - sol = solve(prob, ImplicitEM()) - @test mean(sol[:X1]) > mean(sol[:X2]) + sprob = SDEProblem(no_param_network, u0, (0.0, 1000.0)) + for repeat in 1:5 + sol = solve(sprob, ImplicitEM(); seed = rand(rng, 100)) + @test mean(sol[:X1]) > mean(sol[:X2]) + end end end diff --git a/test/model_simulation/simulate_jumps.jl b/test/model_simulation/simulate_jumps.jl index 2f8adb66cd..5568961332 100644 --- a/test/model_simulation/simulate_jumps.jl +++ b/test/model_simulation/simulate_jumps.jl @@ -115,13 +115,13 @@ let ps_1 = rnd_ps(rn_catalyst, rng; factor = factor/100.0) dprob_1 = DiscreteProblem(rn_catalyst, u0_1, (0.0, 100.0), ps_1) jprob_1 = JumpProblem(rn_catalyst, dprob_1, Direct(); rng) - sol1 = solve(jprob_1, SSAStepper(); seed = 1234, saveat = 1.0) + sol1 = solve(jprob_1, SSAStepper(); saveat = 1.0) u0_2 = map_to_vec(u0_1, u0_sym) ps_2 = map_to_vec(ps_1, ps_sym) dprob_2 = DiscreteProblem(u0_2, (0.0, 100.0), ps_2) jprob_2 = JumpProblem(dprob_2, Direct(), rn_manual...; rng) - sol2 = solve(jprob_2, SSAStepper(); seed = 1234, saveat = 1.0) + sol2 = solve(jprob_2, SSAStepper(); saveat = 1.0) if nameof(rn_catalyst) == :rnh7 # Have spent a few hours figuring this one out. For certain seeds it actually works, @@ -143,7 +143,7 @@ let ps = rnd_ps(rn, rng) dprob = DiscreteProblem(rn, u0, (0.0, 1.0), ps) jprob = JumpProblem(rn, dprob, Direct(); rng) - @test SciMLBase.successful_retcode(solve(jprob, SSAStepper(); seed = 1234)) + @test SciMLBase.successful_retcode(solve(jprob, SSAStepper())) end end @@ -157,6 +157,6 @@ let u0 = rnd_u0_Int64(no_param_network, rng) dprob = DiscreteProblem(no_param_network, u0, (0.0, 1000.0)) jprob = JumpProblem(no_param_network, dprob, Direct(); rng) - sol = solve(jprob, SSAStepper(); seed = 1234) + sol = solve(jprob, SSAStepper()) @test mean(sol[:X1]) > mean(sol[:X2]) end diff --git a/test/reactionsystem_structure/higher_order_reactions.jl b/test/reactionsystem_structure/higher_order_reactions.jl index ca2eb6c7e8..e50e037137 100644 --- a/test/reactionsystem_structure/higher_order_reactions.jl +++ b/test/reactionsystem_structure/higher_order_reactions.jl @@ -73,11 +73,11 @@ end p = factor * rand(rng, length(get_ps(higher_order_network_3))) prob1 = JumpProblem(higher_order_network_1, DiscreteProblem(higher_order_network_1, u0, (0.0, 1000.0), p), - Direct()) + Direct(); rng) sol1 = solve(prob1, SSAStepper()) prob2 = JumpProblem(higher_order_network_3, DiscreteProblem(higher_order_network_3, u0, (0.0, 1000.0), p), - Direct()) + Direct(); rng) sol2 = solve(prob2, SSAStepper()) for i in 1:length(u0) vals1 = getindex.(sol1.u, i) diff --git a/test/reactionsystem_structure/reactionsystem.jl b/test/reactionsystem_structure/reactionsystem.jl index b9b467d5d0..5731589563 100644 --- a/test/reactionsystem_structure/reactionsystem.jl +++ b/test/reactionsystem_structure/reactionsystem.jl @@ -292,11 +292,11 @@ let rs = complete(rs) js = complete(convert(JumpSystem, rs)) dprob = DiscreteProblem(js, [S => 1, I => 1], (0.0, 10.0)) - jprob = JumpProblem(js, dprob, Direct()) + jprob = JumpProblem(js, dprob, Direct(); rng) sol = solve(jprob, SSAStepper()) # Test for /~https://github.com/SciML/ModelingToolkit.jl/issues/1042. - jprob = JumpProblem(rs, dprob, Direct(), save_positions = (false, false)) + jprob = JumpProblem(rs, dprob, Direct(); rng, save_positions = (false, false)) @parameters k1 k2 @species R(t) @@ -530,10 +530,10 @@ let @named rn = ReactionSystem([(@reaction k1, $C --> B1 + $C), (@reaction k1, $A --> B2), (@reaction 10 * k1, ∅ --> B3)], t) - rn = complete(rn) + rn = complete(rn) dprob = DiscreteProblem(rn, [A => 10, C => 10, B1 => 0, B2 => 0, B3 => 0], (0.0, 10.0), [k1 => 1.0]) - jprob = JumpProblem(rn, dprob, Direct(), save_positions = (false, false)) + jprob = JumpProblem(rn, dprob, Direct(); rng, save_positions = (false, false)) umean = zeros(4) Nsims = 40000 for i in 1:Nsims From 2f0b81cd30a6e569a1ef875d2e948e242d60d098 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 29 Mar 2024 17:35:31 -0400 Subject: [PATCH 31/51] Move thing that was unnecessarily moved, should make diff easier to read --- test/dsl/dsl_basics.jl | 22 +++++++++++----------- test/dsl/dsl_model_construction.jl | 2 +- test/model_simulation/simulate_SDEs.jl | 24 ++++++++++++------------ 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/test/dsl/dsl_basics.jl b/test/dsl/dsl_basics.jl index a2c5992911..e0b4872267 100644 --- a/test/dsl/dsl_basics.jl +++ b/test/dsl/dsl_basics.jl @@ -117,6 +117,17 @@ let @test rn == rn2 end +# Creates a reaction network using `eval` and internal function. +let + ex = quote + (Ka, Depot --> Central) + (CL / Vc, Central --> 0) + end + # Line number nodes aren't ignored so have to be manually removed + Base.remove_linenums!(ex) + @test eval(Catalyst.make_reaction_system(ex)) isa ReactionSystem +end + # Miscellaneous interpolation tests. Unsure what they do here (not related to DSL). let rx = @reaction k*h, A + 2*B --> 3*C + D @@ -131,17 +142,6 @@ let @test rx == Reaction(b+ex, [A,C], nothing, [2,1], nothing) end -# Creates a reaction network using `eval` and internal function. -let - ex = quote - (Ka, Depot --> Central) - (CL / Vc, Central --> 0) - end - # Line number nodes aren't ignored so have to be manually removed - Base.remove_linenums!(ex) - @test eval(Catalyst.make_reaction_system(ex)) isa ReactionSystem -end - ### Tests Reaction Metadata ### # Tests construction for various types of reaction metadata. diff --git a/test/dsl/dsl_model_construction.jl b/test/dsl/dsl_model_construction.jl index e777a2b998..6ac59d0d29 100644 --- a/test/dsl/dsl_model_construction.jl +++ b/test/dsl/dsl_model_construction.jl @@ -395,7 +395,7 @@ let @test isequal((@reaction k, 0 --> X), (@reaction k, 0 ⥟ X)) end -# Test that symbols with special mean, or that are forbidden, are handled properly. +# Test that symbols with special meanings, or that are forbidden, are handled properly. let test_network = @reaction_network begin t * k, X --> ∅ end @test length(species(test_network)) == 1 diff --git a/test/model_simulation/simulate_SDEs.jl b/test/model_simulation/simulate_SDEs.jl index 3bd5abcdea..6c04c75e85 100644 --- a/test/model_simulation/simulate_SDEs.jl +++ b/test/model_simulation/simulate_SDEs.jl @@ -131,8 +131,8 @@ let if nameof(rn_catalyst) != :rnc9 sprob_1 = SDEProblem(rn_catalyst, u0_1, (0.0, 1.0), ps_1) sprob_2 = SDEProblem(rn_manual.f, rn_manual.g, u0_2, (0.0, 1.0), ps_2; noise_rate_prototype = rn_manual.nrp) - sol1 = solve(sprob_1, ImplicitEM(); seed = rand(rng, 100)) - sol2 = solve(sprob_2, ImplicitEM(); seed = rand(rng, 100)) + sol1 = solve(sprob_1, ImplicitEM(); seed = rand(rng, 1:100)) + sol2 = solve(sprob_2, ImplicitEM(); seed = rand(rng, 1:100)) @test sol1[u0_sym] ≈ sol2.u end end @@ -198,9 +198,9 @@ let sprob_1_2 = SDEProblem(noise_scaling_network_1, u0, (0.0, 1000.0), [:k1 => 2.0, :k2 => 0.66, :η1 => 2.0, :η2 => 0.2]) sprob_1_3 = SDEProblem(noise_scaling_network_1, u0, (0.0, 1000.0), [:k1 => 2.0, :k2 => 0.66, :η1 => 0.2, :η2 => 0.2]) for repeat in 1:5 - sol_1_1 = solve(sprob_1_1, ImplicitEM(); saveat=1.0, seed = rand(rng, 100)) - sol_1_2 = solve(sprob_1_2, ImplicitEM(); saveat=1.0, seed = rand(rng, 100)) - sol_1_3 = solve(sprob_1_3, ImplicitEM(); saveat=1.0, seed = rand(rng, 100)) + sol_1_1 = solve(sprob_1_1, ImplicitEM(); saveat=1.0, seed = rand(rng, 1:100)) + sol_1_2 = solve(sprob_1_2, ImplicitEM(); saveat=1.0, seed = rand(rng, 1:100)) + sol_1_3 = solve(sprob_1_3, ImplicitEM(); saveat=1.0, seed = rand(rng, 1:100)) @test var(sol_1_1[:X1]) > var(sol_1_2[:X1]) > var(sol_1_3[:X1]) end @@ -214,9 +214,9 @@ let sprob_2_2 = SDEProblem(noise_scaling_network_2, u0, (0.0, 1000.0), [k1 => 2.0, k2 => 0.66, η[1] => 2.0, η[2] => 0.2]) sprob_2_3 = SDEProblem(noise_scaling_network_2, u0, (0.0, 1000.0), [k1 => 2.0, k2 => 0.66, η[1] => 0.2, η[2] => 0.2]) for repeat in 1:5 - sol_2_1 = solve(sprob_2_1, ImplicitEM(); saveat=1.0, seed = rand(rng, 100)) - sol_2_2 = solve(sprob_2_2, ImplicitEM(); saveat=1.0, seed = rand(rng, 100)) - sol_2_3 = solve(sprob_2_3, ImplicitEM(); saveat=1.0, seed = rand(rng, 100)) + sol_2_1 = solve(sprob_2_1, ImplicitEM(); saveat=1.0, seed = rand(rng, 1:100)) + sol_2_2 = solve(sprob_2_2, ImplicitEM(); saveat=1.0, seed = rand(rng, 1:100)) + sol_2_3 = solve(sprob_2_3, ImplicitEM(); saveat=1.0, seed = rand(rng, 1:100)) @test var(sol_2_1[:X1]) > var(sol_2_2[:X1]) > var(sol_2_3[:X1]) end end @@ -274,7 +274,7 @@ let sprob = SDEProblem(noise_scaling_network, u0, (0.0, 1000.0), ps) for repeat in 1:5 - sol = solve(sprob, ImplicitEM(); saveat=1.0, seed = rand(rng, 100)) + sol = solve(sprob, ImplicitEM(); saveat=1.0, seed = rand(rng, 1:100)) @test var(sol[:X1]) < var(sol[:X2]) @test var(sol[:X1]) < var(sol[:X3]) end @@ -298,9 +298,9 @@ let u0 = [:X1 => 1000.0, :X2 => 1000.0, :X3 => 1000.0, :X4 => 1000.0, :X5 => 1000.0, :N1 => 3.0, :N3 => 0.33] ps = [:p => 1000.0, :d => 1.0, :η1 => 1.0, :η2 => 1.4, :η3 => 0.33, :η4 => 4.0] sprob = SDEProblem(noise_scaling_network, u0, (0.0, 1000.0), ps) - + for repeat in 1:5 - sol = solve(sprob, ImplicitEM(); saveat=1.0, adaptive=false, dt=0.1, seed = rand(rng, 100)) + sol = solve(sprob, ImplicitEM(); saveat=1.0, adaptive=false, dt=0.1, seed = rand(rng, 1:100)) @test var(sol[:X1]) > var(sol[:X2]) > var(sol[:X3]) > var(sol[:X4]) > var(sol[:X5]) end end @@ -356,7 +356,7 @@ let u0 = rnd_u0(no_param_network, rng; factor) sprob = SDEProblem(no_param_network, u0, (0.0, 1000.0)) for repeat in 1:5 - sol = solve(sprob, ImplicitEM(); seed = rand(rng, 100)) + sol = solve(sprob, ImplicitEM(); seed = rand(rng, 1:100)) @test mean(sol[:X1]) > mean(sol[:X2]) end end From b52449a181655035527ae5d07d6b751a26537759 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 29 Mar 2024 18:13:57 -0400 Subject: [PATCH 32/51] fix more old sde sims without seeds --- test/dsl/dsl_model_construction.jl | 52 ++++++++++++++++--------- test/model_simulation/simulate_SDEs.jl | 5 ++- test/model_simulation/simulate_jumps.jl | 5 ++- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/test/dsl/dsl_model_construction.jl b/test/dsl/dsl_model_construction.jl index 6ac59d0d29..1e720e0798 100644 --- a/test/dsl/dsl_model_construction.jl +++ b/test/dsl/dsl_model_construction.jl @@ -124,32 +124,48 @@ let for networks in identical_networks_1 for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] - u0_1 = rnd_u0(networks[1], rng; factor) - p_1 = rnd_ps(networks[1], rng; factor) - u0_2 = Pair.(unknowns(networks[2]), last.(u0_1)) - p_2 = Pair.(parameters(networks[2]), last.(p_1)) + u0 = rnd_u0(networks[1], rng; factor) + p = rnd_ps(networks[1], rng; factor) t = rand(rng) - @test f_eval(networks[1], u0_1, p_1, t) ≈ f_eval(networks[2], u0_2, p_2, t) - @test jac_eval(networks[1], u0_1, p_1, t) ≈ jac_eval(networks[2], u0_2, p_2, t) - @test g_eval(networks[1], u0_1, p_1, t) ≈ g_eval(networks[2], u0_2, p_2, t) + @test f_eval(networks[1], u0, p, t) ≈ f_eval(networks[2], u0, p, t) + @test jac_eval(networks[1], u0, p, t) ≈ jac_eval(networks[2], u0, p, t) + @test g_eval(networks[1], u0, p, t) ≈ g_eval(networks[2], u0, p, t) end end end -# Compares networks to networks written in different ways. +# Compares simulations for network with different species and parameter names let - identical_networks_2 = Vector{Pair}() - - # Different parameter and variable names. + # Fetches the original network, and also declares it using alternative notation. + network = reaction_networks_standard[5] differently_written_5 = @reaction_network begin q, ∅ → Y1 (l1, l2), Y1 ⟷ Y2 (l3, l4), Y2 ⟷ Y3 (l5, l6), Y3 ⟷ Y4 c, Y4 → ∅ + end + + # Checks that the networks' functions evaluates equally for various randomised inputs. + @unpack X1, X2, X3, X4, p, d, k1, k2, k3, k4, k5, k6 = network + for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] + u0_1 = Dict(rnd_u0(network, rng; factor)) + p_1 = Dict(rnd_ps(network, rng; factor)) + u0_2 = [:Y1 => u0_1[X1], :Y2 => u0_1[X2], :Y3 => u0_1[X3], :Y4 => u0_1[X4]] + p_2 = [:q => p_1[p], :c => p_1[d], :l1 => p_1[k1], :l2 => p_1[k2], :l3 => p_1[k3], + :l4 => p_1[k4], :l5 => p_1[k5], :l6 => p_1[k6]] + t = rand(rng) + + @test f_eval(network, u0_1, p_1, t) ≈ f_eval(differently_written_5, u0_2, p_2, t) + @test jac_eval(network, u0_1, p_1, t) ≈ jac_eval(differently_written_5, u0_2, p_2, t) + @test g_eval(network, u0_1, p_1, t) ≈ g_eval(differently_written_5, u0_2, p_2, t) end - push!(identical_networks_2, reaction_networks_standard[5] => differently_written_5) +end + +# Compares networks to networks written in different ways. +let + identical_networks_2 = Vector{Pair}() # Unfold reactions. differently_written_6 = @reaction_network begin @@ -190,15 +206,13 @@ let for networks in identical_networks_2 for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] - u0_1 = rnd_u0(networks[1], rng; factor) - p_1 = rnd_ps(networks[1], rng; factor) - u0_2 = Pair.(unknowns(networks[2]), last.(u0_1)) - p_2 = Pair.(parameters(networks[2]), last.(p_1)) + u0 = rnd_u0(networks[1], rng; factor) + p = rnd_ps(networks[1], rng; factor) t = rand(rng) - @test f_eval(networks[1], u0_1, p_1, t) ≈ f_eval(networks[2], u0_2, p_2, t) - @test jac_eval(networks[1], u0_1, p_1, t) ≈ jac_eval(networks[2], u0_2, p_2, t) - @test g_eval(networks[1], u0_1, p_1, t) ≈ g_eval(networks[2], u0_2, p_2, t) + @test f_eval(networks[1], u0, p, t) ≈ f_eval(networks[2], u0, p, t) + @test jac_eval(networks[1], u0, p, t) ≈ jac_eval(networks[2], u0, p, t) + @test g_eval(networks[1], u0, p, t) ≈ g_eval(networks[2], u0, p, t) end end end diff --git a/test/model_simulation/simulate_SDEs.jl b/test/model_simulation/simulate_SDEs.jl index 6c04c75e85..ea7d6e4666 100644 --- a/test/model_simulation/simulate_SDEs.jl +++ b/test/model_simulation/simulate_SDEs.jl @@ -131,8 +131,9 @@ let if nameof(rn_catalyst) != :rnc9 sprob_1 = SDEProblem(rn_catalyst, u0_1, (0.0, 1.0), ps_1) sprob_2 = SDEProblem(rn_manual.f, rn_manual.g, u0_2, (0.0, 1.0), ps_2; noise_rate_prototype = rn_manual.nrp) - sol1 = solve(sprob_1, ImplicitEM(); seed = rand(rng, 1:100)) - sol2 = solve(sprob_2, ImplicitEM(); seed = rand(rng, 1:100)) + seed = seed = rand(rng, 1:100) + sol1 = solve(sprob_1, ImplicitEM(); seed) + sol2 = solve(sprob_2, ImplicitEM(); seed) @test sol1[u0_sym] ≈ sol2.u end end diff --git a/test/model_simulation/simulate_jumps.jl b/test/model_simulation/simulate_jumps.jl index 5568961332..6425758b89 100644 --- a/test/model_simulation/simulate_jumps.jl +++ b/test/model_simulation/simulate_jumps.jl @@ -111,17 +111,18 @@ let # Loops through all cases, checks that identical simulations are generated with/without Catalyst. for (rn_catalyst, rn_manual, u0_sym, ps_sym) in zip(catalyst_networks, manual_networks, u0_syms, ps_syms) for factor in [5, 50] + seed = rand(rng, 1:100) u0_1 = rnd_u0_Int64(rn_catalyst, rng; n = factor) ps_1 = rnd_ps(rn_catalyst, rng; factor = factor/100.0) dprob_1 = DiscreteProblem(rn_catalyst, u0_1, (0.0, 100.0), ps_1) jprob_1 = JumpProblem(rn_catalyst, dprob_1, Direct(); rng) - sol1 = solve(jprob_1, SSAStepper(); saveat = 1.0) + sol1 = solve(jprob_1, SSAStepper(); seed, saveat = 1.0) u0_2 = map_to_vec(u0_1, u0_sym) ps_2 = map_to_vec(ps_1, ps_sym) dprob_2 = DiscreteProblem(u0_2, (0.0, 100.0), ps_2) jprob_2 = JumpProblem(dprob_2, Direct(), rn_manual...; rng) - sol2 = solve(jprob_2, SSAStepper(); saveat = 1.0) + sol2 = solve(jprob_2, SSAStepper(); seed, saveat = 1.0) if nameof(rn_catalyst) == :rnh7 # Have spent a few hours figuring this one out. For certain seeds it actually works, From 211baf797d1acc4ccc0e78d3df13ebaf4585458b Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 29 Mar 2024 18:51:22 -0400 Subject: [PATCH 33/51] up --- test/model_simulation/simulate_SDEs.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/model_simulation/simulate_SDEs.jl b/test/model_simulation/simulate_SDEs.jl index ea7d6e4666..c33d62fa48 100644 --- a/test/model_simulation/simulate_SDEs.jl +++ b/test/model_simulation/simulate_SDEs.jl @@ -199,9 +199,9 @@ let sprob_1_2 = SDEProblem(noise_scaling_network_1, u0, (0.0, 1000.0), [:k1 => 2.0, :k2 => 0.66, :η1 => 2.0, :η2 => 0.2]) sprob_1_3 = SDEProblem(noise_scaling_network_1, u0, (0.0, 1000.0), [:k1 => 2.0, :k2 => 0.66, :η1 => 0.2, :η2 => 0.2]) for repeat in 1:5 - sol_1_1 = solve(sprob_1_1, ImplicitEM(); saveat=1.0, seed = rand(rng, 1:100)) - sol_1_2 = solve(sprob_1_2, ImplicitEM(); saveat=1.0, seed = rand(rng, 1:100)) - sol_1_3 = solve(sprob_1_3, ImplicitEM(); saveat=1.0, seed = rand(rng, 1:100)) + sol_1_1 = solve(sprob_1_1, ImplicitEM(); saveat = 1.0, seed = rand(rng, 1:100)) + sol_1_2 = solve(sprob_1_2, ImplicitEM(); saveat = 1.0, seed = rand(rng, 1:100)) + sol_1_3 = solve(sprob_1_3, ImplicitEM(); saveat = 1.0, seed = rand(rng, 1:100)) @test var(sol_1_1[:X1]) > var(sol_1_2[:X1]) > var(sol_1_3[:X1]) end @@ -215,9 +215,9 @@ let sprob_2_2 = SDEProblem(noise_scaling_network_2, u0, (0.0, 1000.0), [k1 => 2.0, k2 => 0.66, η[1] => 2.0, η[2] => 0.2]) sprob_2_3 = SDEProblem(noise_scaling_network_2, u0, (0.0, 1000.0), [k1 => 2.0, k2 => 0.66, η[1] => 0.2, η[2] => 0.2]) for repeat in 1:5 - sol_2_1 = solve(sprob_2_1, ImplicitEM(); saveat=1.0, seed = rand(rng, 1:100)) - sol_2_2 = solve(sprob_2_2, ImplicitEM(); saveat=1.0, seed = rand(rng, 1:100)) - sol_2_3 = solve(sprob_2_3, ImplicitEM(); saveat=1.0, seed = rand(rng, 1:100)) + sol_2_1 = solve(sprob_2_1, ImplicitEM(); saveat = 1.0, seed = rand(rng, 1:100)) + sol_2_2 = solve(sprob_2_2, ImplicitEM(); saveat = 1.0, seed = rand(rng, 1:100)) + sol_2_3 = solve(sprob_2_3, ImplicitEM(); saveat = 1.0, seed = rand(rng, 1:100)) @test var(sol_2_1[:X1]) > var(sol_2_2[:X1]) > var(sol_2_3[:X1]) end end @@ -275,14 +275,14 @@ let sprob = SDEProblem(noise_scaling_network, u0, (0.0, 1000.0), ps) for repeat in 1:5 - sol = solve(sprob, ImplicitEM(); saveat=1.0, seed = rand(rng, 1:100)) + sol = solve(sprob, ImplicitEM(); saveat = 1.0, seed = rand(rng, 1:100)) @test var(sol[:X1]) < var(sol[:X2]) @test var(sol[:X1]) < var(sol[:X3]) end end # Tests using complicated noise scaling expressions. -let +@time let noise_scaling_network = @reaction_network begin @parameters η1 η2 η3 η4 @species N1(t) N2(t)=0.5 @@ -301,7 +301,7 @@ let sprob = SDEProblem(noise_scaling_network, u0, (0.0, 1000.0), ps) for repeat in 1:5 - sol = solve(sprob, ImplicitEM(); saveat=1.0, adaptive=false, dt=0.1, seed = rand(rng, 1:100)) + sol = solve(sprob, ImplicitEM(); saveat = 1.0, adaptive = false, dt = 0.01, seed = rand(rng, 1:100)) @test var(sol[:X1]) > var(sol[:X2]) > var(sol[:X3]) > var(sol[:X4]) > var(sol[:X5]) end end From 89c5cf88868f8278a0969baff3871de403df1378 Mon Sep 17 00:00:00 2001 From: Torkel Date: Fri, 29 Mar 2024 21:59:48 -0400 Subject: [PATCH 34/51] rework higher order reactions test so that works with MTKv9 --- test/dsl/custom_functions.jl | 164 ++++-------------- test/miscellaneous_tests/api.jl | 4 +- .../component_based_model_creation.jl | 2 +- .../programmatic_model_expansion.jl | 2 +- .../higher_order_reactions.jl | 117 ++++++++----- .../reactionsystem.jl | 2 - 6 files changed, 110 insertions(+), 181 deletions(-) diff --git a/test/dsl/custom_functions.jl b/test/dsl/custom_functions.jl index f254d081ac..616d76e48f 100644 --- a/test/dsl/custom_functions.jl +++ b/test/dsl/custom_functions.jl @@ -1,15 +1,23 @@ -### Fetch Packages and Set Global Variables ### -using DiffEqBase, Catalyst, Random, Symbolics, Test -using ModelingToolkit: get_unknowns, get_ps -t = default_t() +### Prepares Tests ### + +# Fetch packages. +using Catalyst, Test +import Symbolics: derivative +# Sets stable rng number. using StableRNGs rng = StableRNG(12345) +# Sets the default `t` to use. +t = default_t() + # Fetch test functions. include("../test_functions.jl") -### Tests Custom Functions ### + +### Basic Tests ### + +# Compares network written with and without special functions. let new_hill(x, v, k, n) = v * x^n / (k^n + x^n) new_poly(x, p1, p2) = 0.5 * p1 * x^2 @@ -45,151 +53,49 @@ let end end -### Tests that the various notations gives identical results ### - -# Michaelis-Menten function. -let - mm_network = @reaction_network begin - (1.0, 1.0), 0 ↔ X - mm(X, v, K), 0 --> X1 - mm(X, v, K), 0 --> X2 - mm(X, v, K), 0 --> X3 - end - f_mm = ODEFunction(complete(convert(ODESystem, mm_network)), jac = true) - - u0 = 10 * rand(rng, length(get_unknowns(mm_network))) - p = 10 * rand(rng, length(get_ps(mm_network))) - t = 10 * rand(rng) - - f_mm_output = f_mm(u0, p, t)[2:end] - f_mm_jac_output = f_mm.jac(u0, p, t)[2:end, 1] - @test (maximum(f_mm_output) - minimum(f_mm_output)) .< 100 * eps() - @test (maximum(f_mm_jac_output) - minimum(f_mm_jac_output)) .< 100 * eps() -end - -# Repressing Michaelis-Menten function. -let - mmr_network = @reaction_network begin - (1.0, 1.0), 0 ↔ X - mmr(X, v, K), 0 --> X1 - mmr(X, v, K), 0 --> X2 - mmr(X, v, K), 0 --> X3 - end - f_mmr = ODEFunction(complete(convert(ODESystem, mmr_network)), jac = true) - - u0 = 10 * rand(rng, length(get_unknowns(mmr_network))) - p = 10 * rand(rng, length(get_ps(mmr_network))) - t = 10 * rand(rng) - - f_mmr_output = f_mmr(u0, p, t)[2:end] - f_mmr_jac_output = f_mmr.jac(u0, p, t)[2:end, 1] - @test (maximum(f_mmr_output) - minimum(f_mmr_output)) .< 100 * eps() - @test (maximum(f_mmr_jac_output) - minimum(f_mmr_jac_output)) .< 100 * eps() -end - -# Hill function. -let - hill_network = @reaction_network begin - (1.0, 1.0), 0 ↔ X - hill(X, v, K, 2), 0 --> X1 - hill(X, v, K, 2), 0 --> X2 - end - f_hill = ODEFunction(complete(convert(ODESystem, hill_network)), jac = true) - - u0 = 10 * rand(rng, length(get_unknowns(hill_network))) - p = 10 * rand(rng, length(get_ps(hill_network))) - t = 10 * rand(rng) - - f_hill_output = f_hill(u0, p, t)[2:end] - f_hill_jac_output = f_hill.jac(u0, p, t)[2:end, 1] - @test (maximum(f_hill_output) - minimum(f_hill_output)) .< 100 * eps() - @test (maximum(f_hill_jac_output) - minimum(f_hill_jac_output)) .< 100 * eps() -end - -# Repressing Hill function. -let - hillr_network = @reaction_network begin - (1.0, 1.0), 0 ↔ X - hillr(X, v, K, 2), 0 --> X1 - hillr(X, v, K, 2), 0 --> X2 - end - f_hillr = ODEFunction(complete(convert(ODESystem, hillr_network)), jac = true) - - u0 = 10 * rand(rng, length(get_unknowns(hillr_network))) - p = 10 * rand(rng, length(get_ps(hillr_network))) - t = 10 * rand(rng) - - f_hillr_output = f_hillr(u0, p, t)[2:end] - f_hillr_jac_output = f_hillr.jac(u0, p, t)[2:end, 1] - @test (maximum(f_hillr_output) - minimum(f_hillr_output)) .< 100 * eps() - @test (maximum(f_hillr_jac_output) - minimum(f_hillr_jac_output)) .< 100 * eps() -end - -# Activation/repressing Hill function. -let - hillar_network = @reaction_network begin - (1.0, 1.0), 0 ↔ (X, Y) - hillar(X, Y, v, K, 2), 0 --> X1 - hillar(X, Y, v, K, 2), 0 --> X2 - hillar(X, Y, v, K, 2), 0 --> X3 - hillar(X, Y, v, K, 2), 0 --> X4 - end - f_hillar = ODEFunction(complete(convert(ODESystem, hillar_network)), jac = true) - - u0 = 10 * rand(rng, length(get_unknowns(hillar_network))) - p = 10 * rand(rng, length(get_ps(hillar_network))) - t = 10 * rand(rng) - - f_hillar_output = f_hillar(u0, p, t)[3:end] - f_hillar_jac_output = f_hillar.jac(u0, p, t)[3:end, 1] - @test (maximum(f_hillar_output) - minimum(f_hillar_output)) .< 100 * eps() - @test (maximum(f_hillar_jac_output) - minimum(f_hillar_jac_output)) .< 100 * eps() -end - -### Test Symbolic Derivatives ### - +# Compares the symbolic derivatives with their manually computed forms. let @variables X Y @parameters v K n - @test isequal(Symbolics.derivative(Catalyst.mm(X, v, K), X), v * K / (K + X)^2) - @test isequal(Symbolics.derivative(Catalyst.mm(X, v, K), v), X / (K + X)) - @test isequal(Symbolics.derivative(Catalyst.mm(X, v, K), K), -v * X / (K + X)^2) + @test isequal(derivative(Catalyst.mm(X, v, K), X), v * K / (K + X)^2) + @test isequal(derivative(Catalyst.mm(X, v, K), v), X / (K + X)) + @test isequal(derivative(Catalyst.mm(X, v, K), K), -v * X / (K + X)^2) - @test isequal(Symbolics.derivative(Catalyst.mmr(X, v, K), X), -v * K / (K + X)^2) - @test isequal(Symbolics.derivative(Catalyst.mmr(X, v, K), v), K / (K + X)) - @test isequal(Symbolics.derivative(Catalyst.mmr(X, v, K), K), v * X / (K + X)^2) + @test isequal(derivative(Catalyst.mmr(X, v, K), X), -v * K / (K + X)^2) + @test isequal(derivative(Catalyst.mmr(X, v, K), v), K / (K + X)) + @test isequal(derivative(Catalyst.mmr(X, v, K), K), v * X / (K + X)^2) - @test isequal(Symbolics.derivative(Catalyst.hill(X, v, K, n), X), + @test isequal(derivative(Catalyst.hill(X, v, K, n), X), n * v * (K^n) * (X^(n - 1)) / (K^n + X^n)^2) - @test isequal(Symbolics.derivative(Catalyst.hill(X, v, K, n), v), X^n / (K^n + X^n)) - @test isequal(Symbolics.derivative(Catalyst.hill(X, v, K, n), K), + @test isequal(derivative(Catalyst.hill(X, v, K, n), v), X^n / (K^n + X^n)) + @test isequal(derivative(Catalyst.hill(X, v, K, n), K), -n * v * (K^(n - 1)) * (X^n) / (K^n + X^n)^2) - @test isequal(Symbolics.derivative(Catalyst.hill(X, v, K, n), n), + @test isequal(derivative(Catalyst.hill(X, v, K, n), n), v * (X^n) * (K^n) * (log(X) - log(K)) / (K^n + X^n)^2) - @test isequal(Symbolics.derivative(Catalyst.hillr(X, v, K, n), X), + @test isequal(derivative(Catalyst.hillr(X, v, K, n), X), -n * v * (K^n) * (X^(n - 1)) / (K^n + X^n)^2) - @test isequal(Symbolics.derivative(Catalyst.hillr(X, v, K, n), v), K^n / (K^n + X^n)) - @test isequal(Symbolics.derivative(Catalyst.hillr(X, v, K, n), K), + @test isequal(derivative(Catalyst.hillr(X, v, K, n), v), K^n / (K^n + X^n)) + @test isequal(derivative(Catalyst.hillr(X, v, K, n), K), n * v * (K^(n - 1)) * (X^n) / (K^n + X^n)^2) - @test isequal(Symbolics.derivative(Catalyst.hillr(X, v, K, n), n), + @test isequal(derivative(Catalyst.hillr(X, v, K, n), n), v * (X^n) * (K^n) * (log(K) - log(X)) / (K^n + X^n)^2) - @test isequal(Symbolics.derivative(Catalyst.hillar(X, Y, v, K, n), X), + @test isequal(derivative(Catalyst.hillar(X, Y, v, K, n), X), n * v * (K^n + Y^n) * (X^(n - 1)) / (K^n + X^n + Y^n)^2) - @test isequal(Symbolics.derivative(Catalyst.hillar(X, Y, v, K, n), Y), + @test isequal(derivative(Catalyst.hillar(X, Y, v, K, n), Y), -n * v * (Y^(n - 1)) * (X^n) / (K^n + X^n + Y^n)^2) - @test isequal(Symbolics.derivative(Catalyst.hillar(X, Y, v, K, n), v), + @test isequal(derivative(Catalyst.hillar(X, Y, v, K, n), v), X^n / (K^n + X^n + Y^n)) - @test isequal(Symbolics.derivative(Catalyst.hillar(X, Y, v, K, n), K), + @test isequal(derivative(Catalyst.hillar(X, Y, v, K, n), K), -n * v * (v^(n - 1)) * (X^n) / (K^n + X^n + Y^n)^2) - @test isequal(Symbolics.derivative(Catalyst.hillar(X, Y, v, K, n), n), + @test isequal(derivative(Catalyst.hillar(X, Y, v, K, n), n), v * (X^n) * ((K^n + Y^n) * log(X) - (K^n) * log(K) - (Y^n) * log(Y)) / (K^n + X^n + Y^n)^2) end -### Tests Current Function Expansion ### +### Tests Function Expansion ### # Tests `ReactionSystem`s. let @@ -233,7 +139,7 @@ end # Tests `Equation`s. let - @variables T X(T) Y(T) + @variables X(t) Y(t) @parameters K V N eq1 = 0 ~ mm(X, V, K) diff --git a/test/miscellaneous_tests/api.jl b/test/miscellaneous_tests/api.jl index 0fa4e64a50..417d511de8 100644 --- a/test/miscellaneous_tests/api.jl +++ b/test/miscellaneous_tests/api.jl @@ -4,8 +4,8 @@ # Fetch packages. using Catalyst, NonlinearSolve, OrdinaryDiffEq, SparseArrays, StochasticDiffEq, Test -using LinearAlgebra: norm -using ModelingToolkit: value +import LinearAlgebra: norm +import ModelingToolkit: value # Sets the default `t` to use. t = default_t() diff --git a/test/programmatic_model_creation/component_based_model_creation.jl b/test/programmatic_model_creation/component_based_model_creation.jl index f3d4f75a28..3cf06c3dc7 100644 --- a/test/programmatic_model_creation/component_based_model_creation.jl +++ b/test/programmatic_model_creation/component_based_model_creation.jl @@ -4,7 +4,7 @@ # Fetch packages. using Catalyst, LinearAlgebra, OrdinaryDiffEq, SciMLNLSolve, Test -using ModelingToolkit: nameof +import ModelingToolkit: nameof # Fetch test networks. t = default_t() diff --git a/test/programmatic_model_creation/programmatic_model_expansion.jl b/test/programmatic_model_creation/programmatic_model_expansion.jl index 6215d7fdcc..710332e8d4 100644 --- a/test/programmatic_model_creation/programmatic_model_expansion.jl +++ b/test/programmatic_model_creation/programmatic_model_expansion.jl @@ -4,7 +4,7 @@ # Fetch packages. using Catalyst, Test -using ModelingToolkit: get_ps, get_unknowns, get_eqs, get_systems, get_iv, getname, nameof +import ModelingToolkit: get_ps, get_unknowns, get_eqs, get_systems, get_iv, getname, nameof # Sets stable rng number. using StableRNGs diff --git a/test/reactionsystem_structure/higher_order_reactions.jl b/test/reactionsystem_structure/higher_order_reactions.jl index e50e037137..d52c84334c 100644 --- a/test/reactionsystem_structure/higher_order_reactions.jl +++ b/test/reactionsystem_structure/higher_order_reactions.jl @@ -1,20 +1,21 @@ -### Fetch Packages and Set Global Variables ### - ### Prepares Tests ### # Fetch packages. -using DiffEqBase, Catalyst, JumpProcesses, Random, Statistics, Test -using ModelingToolkit: get_unknowns, get_ps +using DiffEqBase, Catalyst, JumpProcesses, Statistics, Test +import ModelingToolkit: get_unknowns, get_ps # Sets stable rng number. using StableRNGs rng = StableRNG(12345) +seed = rand(rng, 1:100) # Fetch test functions. include("../test_functions.jl") -# Declares a network used throughout all tests. -higher_order_network_1 = @reaction_network begin +### Basic Tests ### + +# Declares a base network to you for comparisons. +base_higher_order_network = @reaction_network begin p, ∅ ⟼ X1 r1, 2X1 ⟼ 3X2 mm(X1, r2, K), 3X2 ⟼ X3 + 2X4 @@ -25,11 +26,9 @@ higher_order_network_1 = @reaction_network begin d, 2X10 ⟼ ∅ end -### Basic Tests ### - # Tests that ODE and SDE functions are correct (by comparing to network with manually written higher order rates). let - higher_order_network_2 = @reaction_network begin + higher_order_network_alt1 = @reaction_network begin p, ∅ ⟾ X1 r1 * X1^2 / factorial(2), 2X1 ⟾ 3X2 mm(X1, r2, K) * X2^3 / factorial(3), 3X2 ⟾ X3 + 2X4 @@ -42,48 +41,74 @@ let end for factor in [1e-1, 1e0, 1e1, 1e2] - u0 = rnd_u0(higher_order_network_1, rng; factor) - ps = rnd_ps(higher_order_network_1, rng; factor) + u0 = rnd_u0(base_higher_order_network, rng; factor) + ps = rnd_ps(base_higher_order_network, rng; factor) t = rand(rng) - @test f_eval(higher_order_network_1, u0, ps, t) == f_eval(higher_order_network_2, u0, ps, t) - @test jac_eval(higher_order_network_1, u0, ps, t) == jac_eval(higher_order_network_2, u0, ps, t) - @test g_eval(higher_order_network_1, u0, ps, t) == g_eval(higher_order_network_2, u0, ps, t) + @test f_eval(base_higher_order_network, u0, ps, t) == f_eval(higher_order_network_alt1, u0, ps, t) + @test jac_eval(base_higher_order_network, u0, ps, t) == jac_eval(higher_order_network_alt1, u0, ps, t) + @test g_eval(base_higher_order_network, u0, ps, t) == g_eval(higher_order_network_alt1, u0, ps, t) end end # Tests that Jump Systems are correct (by comparing to network with manually written higher order rates). -# Currently fails because binomial only takes Int input (and X is Float64). -# I see several solutions, but depends on whether we allow species to be specified as Int64. -# I have marked this one as broken for now. -@test_broken let - higher_order_network_3 = @reaction_network begin - p, ∅ ⟼ X1 - r1 * binomial(X1, 2), 2X1 ⟾ 3X2 - mm(X1, r2, K) * binomial(X2, 3), 3X2 ⟾ X3 + 2X4 - r3 * binomial(X3, 1) * binomial(X4, 2), X3 + 2X4 ⟾ 3X5 + 3X6 - r4 * X2 * binomial(X5, 3) * binomial(X6, 3), 3X5 + 3X6 ⟾ 3X5 + 2X7 + 4X8 - r5 * binomial(X5, 3) * binomial(X7, 2) * binomial(X8, 4), 3X5 + 2X7 + 4X8 ⟾ 10X9 - r6 * binomial(X9, 10), 10X9 ⟾ X10 - d * binomial(X10, 2), 2X10 ⟾ ∅ - end +# Currently fails for reason I do not understand. Likely reason similar to the weird case in the jump tests. +# Spent loads of time trying to figure out, the best I can get to is that it seems like the rng/seed is +# not fully reproducible. +let + @test_broken false + # Declares a JumpSystem manually. Would have used Catalyst again, but `binomial` still errors when + # called on symbolics. For reference, here is the network as it would be created using Catalyst. + + # higher_order_network_alt2 = @reaction_network begin + # p, ∅ ⟼ X1 + # r1 * binomial(X1, 2), 2X1 ⟾ 3X2 + # mm(X1, r2, K) * binomial(X2, 3), 3X2 ⟾ X3 + 2X4 + # r3 * binomial(X3, 1) * binomial(X4, 2), X3 + 2X4 ⟾ 3X5 + 3X6 + # r4 * X2 * binomial(X5, 3) * binomial(X6, 3), 3X5 + 3X6 ⟾ 3X5 + 2X7 + 4X8 + # r5 * binomial(X5, 3) * binomial(X7, 2) * binomial(X8, 4), 3X5 + 2X7 + 4X8 ⟾ 10X9 + # r6 * binomial(X9, 10), 10X9 ⟾ X10 + # d * binomial(X10, 2), 2X10 ⟾ ∅ + # end - for factor in [1e-1, 1e0] - u0 = rand(rng, 1:Int64(factor * 100), length(get_unknowns(higher_order_network_1))) - p = factor * rand(rng, length(get_ps(higher_order_network_3))) - prob1 = JumpProblem(higher_order_network_1, - DiscreteProblem(higher_order_network_1, u0, (0.0, 1000.0), p), - Direct(); rng) - sol1 = solve(prob1, SSAStepper()) - prob2 = JumpProblem(higher_order_network_3, - DiscreteProblem(higher_order_network_3, u0, (0.0, 1000.0), p), - Direct(); rng) - sol2 = solve(prob2, SSAStepper()) - for i in 1:length(u0) - vals1 = getindex.(sol1.u, i) - vals2 = getindex.(sol1.u, i) - (mean(vals2) > 0.001) && @test 0.8 < mean(vals1) / mean(vals2) < 1.25 - (std(vals2) > 0.001) && @test 0.8 < std(vals1) / std(vals2) < 1.25 - end + rate1(u, p, t) = p[1] + rate2(u, p, t) = p[2] * binomial(u[1], 2) + rate3(u, p, t) = mm(u[1], p[3], p[4]) * binomial(u[2], 3) + rate4(u, p, t) = p[5] * binomial(u[3], 1) * binomial(u[4], 2) + rate5(u, p, t) = p[6] * u[2] * binomial(u[5], 3) * binomial(u[6], 3) + rate6(u, p, t) = p[7] * binomial(u[5], 3) * binomial(u[7], 2) * binomial(u[8], 4) + rate7(u, p, t) = p[8] * binomial(u[9], 10) + rate8(u, p, t) = p[9] * binomial(u[10], 2) + + affect1!(int) = (int.u[1] += 1) + affect2!(int) = (int.u[1] -= 2; int.u[2] += 3;) + affect3!(int) = (int.u[2] -= 3; int.u[3] += 1; int.u[4] += 2;) + affect4!(int) = (int.u[3] -= 1; int.u[4] -= 2; int.u[5] += 3; int.u[6] += 3;) + affect5!(int) = (int.u[5] -= 3; int.u[6] -= 3; int.u[5] += 3; int.u[7] += 2; int.u[8] += 4;) + affect6!(int) = (int.u[5] -= 3; int.u[7] -= 2; int.u[8] -= 4; int.u[9] += 10;) + affect7!(int) = (int.u[9] -= 10; int.u[10] += 1;) + affect8!(int) = (int.u[10] -= 2;) + + higher_order_network_alt2 = ConstantRateJump.([rate1, rate2, rate3, rate4, rate5, rate6, rate7, rate8], + [affect1!, affect2!, affect3!, affect4!, affect5!, affect6!, affect7!, affect8!]) + + # For the systems created via Catalyst and manually, compares that they yield identical simulations. + for n in [5, 50] + # Prepares JumpProblem via Catalyst. + u0_base = rnd_u0_Int64(base_higher_order_network, rng; n) + ps_base = rnd_ps(base_higher_order_network, rng; factor = n/10.0) + dprob_base = DiscreteProblem(base_higher_order_network, u0_base, (0.0, 100.0), ps_base) + jprob_base = JumpProblem(base_higher_order_network, dprob_base, Direct(); rng = StableRNG(1234)) + + # Prepares JumpProblem via manually declared system. + u0_alt = map_to_vec(u0_base, [:X1, :X2, :X3, :X4, :X5, :X6, :X7, :X8, :X9, :X10]) + ps_alt = map_to_vec(ps_base, [:p, :r1, :r2, :K, :r3, :r4, :r5, :r6, :d]) + dprob_alt = DiscreteProblem(u0_alt, (0.0, 100.0), ps_alt) + jprob_alt = JumpProblem(dprob_alt, Direct(), higher_order_network_alt2...; rng = StableRNG(1234)) + + # Compares the simulations + sol_base = solve(jprob_base, SSAStepper(); seed, saveat = 1.0) + sol_alt = solve(jprob_alt, SSAStepper(); seed, saveat = 1.0) + sol_base == sol_alt end -end +end \ No newline at end of file diff --git a/test/reactionsystem_structure/reactionsystem.jl b/test/reactionsystem_structure/reactionsystem.jl index 5731589563..9590a05364 100644 --- a/test/reactionsystem_structure/reactionsystem.jl +++ b/test/reactionsystem_structure/reactionsystem.jl @@ -4,8 +4,6 @@ using Catalyst, LinearAlgebra, JumpProcesses, OrdinaryDiffEq, StochasticDiffEq, Test const MT = ModelingToolkit -t = default_t() - # Sets stable rng number. using StableRNGs rng = StableRNG(12345) From 5896adb72376d71c3f577eb44eb2b700ecdfa91f Mon Sep 17 00:00:00 2001 From: Torkel Date: Sat, 30 Mar 2024 14:53:34 -0400 Subject: [PATCH 35/51] prefer `using Package: function` over `import Package: function` --- test/dsl/custom_functions.jl | 2 +- test/miscellaneous_tests/api.jl | 4 ++-- .../component_based_model_creation.jl | 2 +- .../programmatic_model_expansion.jl | 2 +- test/reactionsystem_structure/higher_order_reactions.jl | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/dsl/custom_functions.jl b/test/dsl/custom_functions.jl index 616d76e48f..49d12620bb 100644 --- a/test/dsl/custom_functions.jl +++ b/test/dsl/custom_functions.jl @@ -2,7 +2,7 @@ # Fetch packages. using Catalyst, Test -import Symbolics: derivative +using Symbolics: derivative # Sets stable rng number. using StableRNGs diff --git a/test/miscellaneous_tests/api.jl b/test/miscellaneous_tests/api.jl index 417d511de8..0fa4e64a50 100644 --- a/test/miscellaneous_tests/api.jl +++ b/test/miscellaneous_tests/api.jl @@ -4,8 +4,8 @@ # Fetch packages. using Catalyst, NonlinearSolve, OrdinaryDiffEq, SparseArrays, StochasticDiffEq, Test -import LinearAlgebra: norm -import ModelingToolkit: value +using LinearAlgebra: norm +using ModelingToolkit: value # Sets the default `t` to use. t = default_t() diff --git a/test/programmatic_model_creation/component_based_model_creation.jl b/test/programmatic_model_creation/component_based_model_creation.jl index 3cf06c3dc7..79db67f99d 100644 --- a/test/programmatic_model_creation/component_based_model_creation.jl +++ b/test/programmatic_model_creation/component_based_model_creation.jl @@ -4,7 +4,7 @@ # Fetch packages. using Catalyst, LinearAlgebra, OrdinaryDiffEq, SciMLNLSolve, Test -import ModelingToolkit: nameof +impousingrt ModelingToolkit: nameof # Fetch test networks. t = default_t() diff --git a/test/programmatic_model_creation/programmatic_model_expansion.jl b/test/programmatic_model_creation/programmatic_model_expansion.jl index 710332e8d4..6215d7fdcc 100644 --- a/test/programmatic_model_creation/programmatic_model_expansion.jl +++ b/test/programmatic_model_creation/programmatic_model_expansion.jl @@ -4,7 +4,7 @@ # Fetch packages. using Catalyst, Test -import ModelingToolkit: get_ps, get_unknowns, get_eqs, get_systems, get_iv, getname, nameof +using ModelingToolkit: get_ps, get_unknowns, get_eqs, get_systems, get_iv, getname, nameof # Sets stable rng number. using StableRNGs diff --git a/test/reactionsystem_structure/higher_order_reactions.jl b/test/reactionsystem_structure/higher_order_reactions.jl index d52c84334c..a72ead0fcd 100644 --- a/test/reactionsystem_structure/higher_order_reactions.jl +++ b/test/reactionsystem_structure/higher_order_reactions.jl @@ -2,7 +2,7 @@ # Fetch packages. using DiffEqBase, Catalyst, JumpProcesses, Statistics, Test -import ModelingToolkit: get_unknowns, get_ps +using ModelingToolkit: get_unknowns, get_ps # Sets stable rng number. using StableRNGs From 233b850f9132388eb2623086e1b6de4a897ce093 Mon Sep 17 00:00:00 2001 From: Torkel Date: Sat, 30 Mar 2024 14:54:29 -0400 Subject: [PATCH 36/51] up --- .../component_based_model_creation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/programmatic_model_creation/component_based_model_creation.jl b/test/programmatic_model_creation/component_based_model_creation.jl index 79db67f99d..f3d4f75a28 100644 --- a/test/programmatic_model_creation/component_based_model_creation.jl +++ b/test/programmatic_model_creation/component_based_model_creation.jl @@ -4,7 +4,7 @@ # Fetch packages. using Catalyst, LinearAlgebra, OrdinaryDiffEq, SciMLNLSolve, Test -impousingrt ModelingToolkit: nameof +using ModelingToolkit: nameof # Fetch test networks. t = default_t() From b8186575fc87fa80534be2462cd521a789e6dd25 Mon Sep 17 00:00:00 2001 From: Torkel Loman Date: Tue, 2 Apr 2024 15:38:11 -0400 Subject: [PATCH 37/51] Update docs/src/catalyst_functionality/compositional_modeling.md Co-authored-by: Sam Isaacson --- docs/src/catalyst_functionality/compositional_modeling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/catalyst_functionality/compositional_modeling.md b/docs/src/catalyst_functionality/compositional_modeling.md index 66d327474c..f760a419bf 100644 --- a/docs/src/catalyst_functionality/compositional_modeling.md +++ b/docs/src/catalyst_functionality/compositional_modeling.md @@ -8,7 +8,7 @@ compartments. ## [A note on *completeness*](@id completeness_note) Catalyst `ReactionSystem` can either be *complete* or *incomplete*. When created using the `@reaction_network` DSL they are *created as complete*. Here, only complete `ReactionSystem`s can be used to create the various problem types (e.g. `ODEProblem`). However, only incomplete `ReactionSystem`s can be composed using the features described below. Hence, for compositional modeling, `ReactionSystem` must be created as incomplete, and later set to complete before simulation. -To create incomplete `ReactionSystem`s using the DSL as complete, use the `@network_component` instead of `@reaction_network`: +To create a `ReactionSystem`s for use in compositional modeling via the DSL, simply use the `@network_component` macro instead of `@reaction_network`: ```@example ex0 using Catalyst degradation_component = @network_component begin From 9b4b00386f7e6adf3cbbad9074d27eb6ab02e028 Mon Sep 17 00:00:00 2001 From: Torkel Loman Date: Tue, 2 Apr 2024 15:43:31 -0400 Subject: [PATCH 38/51] Update docs/src/catalyst_functionality/compositional_modeling.md Co-authored-by: Sam Isaacson --- docs/src/catalyst_functionality/compositional_modeling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/catalyst_functionality/compositional_modeling.md b/docs/src/catalyst_functionality/compositional_modeling.md index f760a419bf..1ebbc41947 100644 --- a/docs/src/catalyst_functionality/compositional_modeling.md +++ b/docs/src/catalyst_functionality/compositional_modeling.md @@ -15,7 +15,7 @@ degradation_component = @network_component begin d, X --> 0 end ``` -Alternatively all `ReactionSystem`s created programmatically are incomplete: +Alternatively one can just build the `ReactionSystem` via the symbolic interface. ```@example ex0 @parameters d @variable t From 889380b1e42677ad1cf93992a3332957cae8eb4b Mon Sep 17 00:00:00 2001 From: Torkel Loman Date: Tue, 2 Apr 2024 15:46:22 -0400 Subject: [PATCH 39/51] Update docs/src/catalyst_functionality/compositional_modeling.md Co-authored-by: Sam Isaacson --- docs/src/catalyst_functionality/compositional_modeling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/catalyst_functionality/compositional_modeling.md b/docs/src/catalyst_functionality/compositional_modeling.md index 1ebbc41947..ce9724bfb4 100644 --- a/docs/src/catalyst_functionality/compositional_modeling.md +++ b/docs/src/catalyst_functionality/compositional_modeling.md @@ -27,7 +27,7 @@ We can test whether a system is complete using the `ModelingToolkit.iscomplete` ```@example ex0 ModelingToolkit.iscomplete(degradation_component) ``` -To make a incomplete system complete, we can use the `complete` function: +To mark a system as complete, after which is should be considered as representing a finalized model, use the `complete` function ```@example ex0 degradation_component_complete = complete(degradation_component) ModelingToolkit.iscomplete(degradation_component_complete) From 5768ec09609578b7edc04278cc53ceb3c3a1e6ec Mon Sep 17 00:00:00 2001 From: Torkel Loman Date: Tue, 2 Apr 2024 15:50:01 -0400 Subject: [PATCH 40/51] Update docs/src/catalyst_functionality/programmatic_CRN_construction.md Co-authored-by: Sam Isaacson --- .../src/catalyst_functionality/programmatic_CRN_construction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/catalyst_functionality/programmatic_CRN_construction.md b/docs/src/catalyst_functionality/programmatic_CRN_construction.md index 507ef458a7..a8e7f15d86 100644 --- a/docs/src/catalyst_functionality/programmatic_CRN_construction.md +++ b/docs/src/catalyst_functionality/programmatic_CRN_construction.md @@ -62,7 +62,7 @@ Alternatively, one can use the `name = :repressilator` keyword argument to the `ReactionSystem` constructor. !!! warn - All `ReactionSystem`s created programmatically (i.e. by calling `ReactionSystem` with some input, rather than using `@reaction_network`) are created as *incomplete*. To simulate them, they must first be made *complete*. This can be done using the `complete` function, i.e. by calling `repressilator = complete(repressilator)`. An expanded description on *completeness* can be found [here](@ref completeness_note). + All `ReactionSystem`s created via the symbolic interface (i.e. by calling `ReactionSystem` with some input, rather than using `@reaction_network`) are not marked as complete. To simulate them, they must first be marked as *complete*, indicating to Catalyst and ModelingToolkit that they represent finalized models. This can be done using the `complete` function, i.e. by calling `repressilator = complete(repressilator)`. An expanded description on *completeness* can be found [here](@ref completeness_note). We can check that this is the same model as the one we defined via the DSL as follows (this requires that we use the same names for rates, species and the From 337d263397f253448569b3be7bd51ed2e515a2ba Mon Sep 17 00:00:00 2001 From: Torkel Loman Date: Tue, 2 Apr 2024 15:50:53 -0400 Subject: [PATCH 41/51] Update docs/src/introduction_to_catalyst/introduction_to_catalyst.md Co-authored-by: Sam Isaacson --- docs/src/introduction_to_catalyst/introduction_to_catalyst.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/introduction_to_catalyst/introduction_to_catalyst.md b/docs/src/introduction_to_catalyst/introduction_to_catalyst.md index f922611160..c0a36a5754 100644 --- a/docs/src/introduction_to_catalyst/introduction_to_catalyst.md +++ b/docs/src/introduction_to_catalyst/introduction_to_catalyst.md @@ -156,7 +156,7 @@ underlying problem. !!! note - Above we have used `repressilator = complete(repressilator)` and `odesys = complete(odesys)` to mark these systems as *complete*. This must be done before any system is given as input to a `convert` call or some problem type. Models created through the @reaction_network` DSL (which is introduced elsewhere, and primarily used throughout these documentation) are created as complete. Hence `complete` does not need to be called on these models. An expanded description on *completeness* can be found [here](@ref completeness_note). + Above we have used `repressilator = complete(repressilator)` and `odesys = complete(odesys)` to mark these systems as *complete*, indicating to Catalyst and ModelingToolkit that these models are finalized. This must be done before any system is given as input to a `convert` call or some problem type. `ReactionSystem` models created through the @reaction_network` DSL (which is introduced elsewhere, and primarily used throughout these documentation) are always marked as complete when generated. Hence `complete` does not need to be called on them. Symbolically generated `ReactionSystem`s, `ReactionSystem`s generated via the `@network_component` macro, and any ModelingToolkit system generated by `convert` always needs to be manually marked as `complete` as we do for `odesys` above. An expanded description on *completeness* can be found [here](@ref completeness_note). At this point we are all set to solve the ODEs. We can now use any ODE solver from within the From 5633b720e2ce5074d4a23986f05fba2812956c76 Mon Sep 17 00:00:00 2001 From: Torkel Loman Date: Tue, 2 Apr 2024 15:51:32 -0400 Subject: [PATCH 42/51] Update src/reactionsystem.jl Co-authored-by: Sam Isaacson --- src/reactionsystem.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reactionsystem.jl b/src/reactionsystem.jl index 6256fa305d..51f75ad0dd 100644 --- a/src/reactionsystem.jl +++ b/src/reactionsystem.jl @@ -1471,7 +1471,7 @@ function spatial_convert_err(rs::ReactionSystem, systype) isspatial(rs) && error("Conversion to $systype is not supported for spatial networks.") end -COMPLETENESS_ERROR = "A ReactionSystem must be complete before it can be converted to other system types. A ReactionSystem can be marked as complete using the `compelte` function." +COMPLETENESS_ERROR = "A ReactionSystem must be complete before it can be converted to other system types. A ReactionSystem can be marked as complete using the `complete` function." """ ```julia From 0c6ddd525bf3fe528bdc2dab32caf779b856edf3 Mon Sep 17 00:00:00 2001 From: Torkel Loman Date: Tue, 2 Apr 2024 15:54:30 -0400 Subject: [PATCH 43/51] Update test/programmatic_model_creation/programmatic_model_expansion.jl Co-authored-by: Sam Isaacson --- test/programmatic_model_creation/programmatic_model_expansion.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/programmatic_model_creation/programmatic_model_expansion.jl b/test/programmatic_model_creation/programmatic_model_expansion.jl index 6215d7fdcc..4c57640e62 100644 --- a/test/programmatic_model_creation/programmatic_model_expansion.jl +++ b/test/programmatic_model_creation/programmatic_model_expansion.jl @@ -62,7 +62,6 @@ let end # Tests creating a network and adding reactions. -# This test seems weird? let unfinished_network = @reaction_network begin @parameters k0 k1 k2 k3 k4 From b8d66aa3a1d3c25e55f591dcc1540bad7f5f0aad Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 2 Apr 2024 21:06:04 -0400 Subject: [PATCH 44/51] updates --- .../structural_identifiability_extension.jl | 3 ++- src/reaction_network.jl | 6 +++--- src/reactionsystem.jl | 4 ++-- test/dsl/custom_functions.jl | 3 ++- test/extensions/bifurcation_kit.jl | 19 ++++++++++++++++++- test/extensions/homotopy_continuation.jl | 17 ++++++++++++++++- test/extensions/structural_identifiability.jl | 19 ++++++++++++++++++- 7 files changed, 61 insertions(+), 10 deletions(-) diff --git a/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl b/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl index 6556436387..68c4c8fa4c 100644 --- a/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl +++ b/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl @@ -157,7 +157,8 @@ end function make_osys(rs::ReactionSystem; remove_conserved=true) # Creates the ODESystem corresponding to the ReactionSystem (expanding functions and flattening it). # Creates a list of the systems all symbols (unknowns and parameters). - rs = complete(Catalyst.expand_registered_functions(flatten(rs))) + iscomplete(rs) || error(COMPLETENESS_ERROR) + rs = Catalyst.expand_registered_functions(flatten(rs)) osys = complete(convert(ODESystem, rs; remove_conserved)) vars = [unknowns(rs); parameters(rs)] diff --git a/src/reaction_network.jl b/src/reaction_network.jl index 3ca59d4c6a..c8a6d22058 100644 --- a/src/reaction_network.jl +++ b/src/reaction_network.jl @@ -148,9 +148,6 @@ emptyrn = @reaction_network ReactionSystems generated through `@reaction_network` are complete. """ -# macro reaction_network(args...) -# return :(complete(@network_component $(args... ))) -# end macro reaction_network(name::Symbol, ex::Expr) :(complete($(make_reaction_system(MacroTools.striplines(ex); name = :($(QuoteNode(name))))))) end @@ -179,6 +176,9 @@ macro reaction_network(name::Symbol = gensym(:ReactionSystem)) :(complete(ReactionSystem(Reaction[], t, [], []; name = $(QuoteNode(name)))))) end +# Ideally, it would have been possible to combine the @reaction_network and @network_component macros. +# However, this issue: /~https://github.com/JuliaLang/julia/issues/37691 causes problem with interpolations +# if we make the @reaction_network macro call the @network_component macro. """ @network_component diff --git a/src/reactionsystem.jl b/src/reactionsystem.jl index 51f75ad0dd..c6fe8a5f41 100644 --- a/src/reactionsystem.jl +++ b/src/reactionsystem.jl @@ -544,7 +544,7 @@ struct ReactionSystem{V <: NetworkProperties} <: # inner constructor is considered private and may change between non-breaking releases. function ReactionSystem(eqs, rxs, iv, sivs, unknowns, spcs, ps, var_to_name, observed, name, systems, defaults, connection_type, nps, cls, cevs, devs, - metadata, complete; checks::Bool = true) + metadata = nothing, complete = false; checks::Bool = true) # unit checks are for ODEs and Reactions only currently nonrx_eqs = Equation[eq for eq in eqs if eq isa Equation] @@ -680,7 +680,7 @@ function ReactionSystem(eqs, iv, unknowns, ps; ReactionSystem(eqs′, rxs, iv′, sivs′, unknowns′, spcs, ps′, var_to_name, observed, name, systems, defaults, connection_type, nps, combinatoric_ratelaws, - ccallbacks, dcallbacks, metadata, false; checks = checks) + ccallbacks, dcallbacks, metadata; checks = checks) end function ReactionSystem(rxs::Vector, iv = Catalyst.DEFAULT_IV; kwargs...) diff --git a/test/dsl/custom_functions.jl b/test/dsl/custom_functions.jl index 49d12620bb..5d1070d65a 100644 --- a/test/dsl/custom_functions.jl +++ b/test/dsl/custom_functions.jl @@ -138,8 +138,9 @@ let end # Tests `Equation`s. +# Tests using non-default independent variable. let - @variables X(t) Y(t) + @variables T X(T) Y(T) @parameters K V N eq1 = 0 ~ mm(X, V, K) diff --git a/test/extensions/bifurcation_kit.jl b/test/extensions/bifurcation_kit.jl index c5805ee7cb..cb0369da4c 100644 --- a/test/extensions/bifurcation_kit.jl +++ b/test/extensions/bifurcation_kit.jl @@ -7,7 +7,8 @@ using BifurcationKit, Catalyst, Test using StableRNGs rng = StableRNG(12345) -### Run Tests ### + +### Basic Tests ### # Brusselator extended with conserved species. # Runs full computation, checks values corresponds to known values. @@ -174,4 +175,20 @@ let # Checks that there is an error if information for conserved quantities computation is not provided. @test_throws Exception bprob = BifurcationProblem(rn, u_guess, p_start, k1; plot_var = X1) +end + + +### Other Tests ### + +# Checks that `BifurcationProblem`s cannot be generated from non-complete `ReactionSystems`s. +let + # Create model. + incomplete_network = @network_component begin + (p, d), 0 <--> X + end + u0_guess = [:X => 1.0] + p_start = [p => 1.0, d => 0.2] + + # Computes bifurcation diagram. + @test_throws Exception BifurcationProblem(incomplete_network, u0_guess, p_start, :p) end \ No newline at end of file diff --git a/test/extensions/homotopy_continuation.jl b/test/extensions/homotopy_continuation.jl index b920bee7d4..76baff926f 100644 --- a/test/extensions/homotopy_continuation.jl +++ b/test/extensions/homotopy_continuation.jl @@ -7,7 +7,7 @@ import HomotopyContinuation # Fetch test functions. include("../test_functions.jl") -### Run Tests ### +### Basic Tests ### # Tests for network without conservation laws. # Tests for Symbol parameter input. @@ -104,4 +104,19 @@ let end @test_throws Exception hc_steady_states(rs, [:v => 5.0, :K => 2.5, :n => 2.7, :d => 1.0]; show_progress=false) +end + + +### Other Tests ### + +# Checks that `hc_steady_states` cannot be applied to non-complete `ReactionSystems`s. +let + # Create model. + incomplete_network = @network_component begin + (p, d), 0 <--> X + end + p_start = [p => 1.0, d => 0.2] + + # Computes bifurcation diagram. + @test_throws Exception hc_steady_states(incomplete_network, p_start) end \ No newline at end of file diff --git a/test/extensions/structural_identifiability.jl b/test/extensions/structural_identifiability.jl index f6aabea82e..38e7753277 100644 --- a/test/extensions/structural_identifiability.jl +++ b/test/extensions/structural_identifiability.jl @@ -17,7 +17,7 @@ function sym_dict(dict_in) end -### Run Tests ### +### Basic Tests ### # Tests for Goodwin model (model with both global, local, and non identifiable components). # Tests for system using Catalyst function (in this case, Michaelis-Menten function) @@ -306,4 +306,21 @@ let :x3 => :globally, ) @test length(find_identifiable_functions(rs, measured_quantities = [:x3])) == 1 +end + + +### Other Tests ### + +# Checks that identifiability functions cannot be applied to non-complete `ReactionSystems`s. +let + # Create model. + incomplete_network = @network_component begin + (p, d), 0 <--> X + end + measured_quantities = [:X] + + # Computes bifurcation diagram. + @test_throws Exception assess_identifiability(incomplete_network; measured_quantities) + @test_throws Exception assess_local_identifiability(incomplete_network; measured_quantities) + @test_throws Exception find_identifiable_functions(incomplete_network; measured_quantities) end \ No newline at end of file From 36ad7dd4536bce4fa3237bd167112a9a8bf971a1 Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 2 Apr 2024 21:19:56 -0400 Subject: [PATCH 45/51] fix tolerances in tests --- test/model_simulation/simulate_ODEs.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/model_simulation/simulate_ODEs.jl b/test/model_simulation/simulate_ODEs.jl index 7296498408..28b6da105b 100644 --- a/test/model_simulation/simulate_ODEs.jl +++ b/test/model_simulation/simulate_ODEs.jl @@ -25,7 +25,7 @@ let p = rnd_ps(exponential_decay, rng; factor) prob = ODEProblem(exponential_decay, u0, (0.0, t_stops[end]), p) - sol = solve(prob, Rosenbrock23(), saveat = t_stops, abstol = 1e-14, reltol = 1e-14) + sol = solve(prob, Vern7(), saveat = t_stops, abstol = 1e-10, reltol = 1e-10) analytic_sol = [u0[1][2] * exp(-p[1][2] * t) for t in t_stops] @test sol[:X] ≈ analytic_sol end @@ -44,12 +44,12 @@ let u0 = rnd_u0(known_equilibrium, rng; factor) p = rnd_ps(known_equilibrium, rng; factor, min = 0.1) prob = ODEProblem(known_equilibrium, u0, (0.0, 100000.0), p) - sol = solve(prob, Vern7(); abstol = 1e-12, reltol = 1e-12) + sol = solve(prob, Rosenbrock23()) - @test sol[:X1][end] / sol[:X2][end] ≈ prob.ps[:k2] / prob.ps[:k1] atol=1e-8 - @test sol[:X3][end] * sol[:X4][end] / sol[:X5][end] ≈ prob.ps[:k4] / prob.ps[:k3] atol=1e-8 - @test (sol[:X6][end]^2 / factorial(2)) / (sol[:X7][end]^3 / factorial(3)) ≈ prob.ps[:k6] / prob.ps[:k5] atol=1e-8 - @test sol[:X8][end] ≈ prob.ps[:k7] / prob.ps[:k8] atol=1e-8 + @test sol[:X1][end] / sol[:X2][end] ≈ prob.ps[:k2] / prob.ps[:k1] + @test sol[:X3][end] * sol[:X4][end] / sol[:X5][end] ≈ prob.ps[:k4] / prob.ps[:k3] + @test (sol[:X6][end]^2 / factorial(2)) / (sol[:X7][end]^3 / factorial(3)) ≈ prob.ps[:k6] / prob.ps[:k5] + @test sol[:X8][end] ≈ prob.ps[:k7] / prob.ps[:k8] end end From 62802eb5cbf4dae604cc1fd0faf32a98cc119133 Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 2 Apr 2024 21:23:48 -0400 Subject: [PATCH 46/51] add Catalyst.has_noise_scaling test --- test/reactionsystem_structure/reactions.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/reactionsystem_structure/reactions.jl b/test/reactionsystem_structure/reactions.jl index df0ffc9830..26b9d939ec 100644 --- a/test/reactionsystem_structure/reactions.jl +++ b/test/reactionsystem_structure/reactions.jl @@ -74,7 +74,7 @@ let @test isequal(Catalyst.getmetadata(r, :md_6), (0.1, 2.0)) end -# tests the noise scaling metadata. +# Tests the noise scaling metadata. let @variables t @parameters k η @@ -85,6 +85,8 @@ let r1 = Reaction(k, [X], [X2], [2], [1]) r2 = Reaction(k, [X], [X2], [2], [1]; metadata=metadata) + @test !Catalyst.has_noise_scaling(r1) + @test Catalyst.has_noise_scaling(r2) @test_throws Exception Catalyst.get_noise_scaling(r1) @test isequal(Catalyst.get_noise_scaling(r2), η) end \ No newline at end of file From f28ec30de557b404b3b54fab1d78cc3dee663f30 Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 2 Apr 2024 21:34:01 -0400 Subject: [PATCH 47/51] update compeltness tests for extensions --- test/extensions/bifurcation_kit.jl | 2 +- test/extensions/homotopy_continuation.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/extensions/bifurcation_kit.jl b/test/extensions/bifurcation_kit.jl index cb0369da4c..d12e94a7b0 100644 --- a/test/extensions/bifurcation_kit.jl +++ b/test/extensions/bifurcation_kit.jl @@ -187,7 +187,7 @@ let (p, d), 0 <--> X end u0_guess = [:X => 1.0] - p_start = [p => 1.0, d => 0.2] + p_start = [:p => 1.0, :d => 0.2] # Computes bifurcation diagram. @test_throws Exception BifurcationProblem(incomplete_network, u0_guess, p_start, :p) diff --git a/test/extensions/homotopy_continuation.jl b/test/extensions/homotopy_continuation.jl index 76baff926f..6f4e9f27f9 100644 --- a/test/extensions/homotopy_continuation.jl +++ b/test/extensions/homotopy_continuation.jl @@ -115,7 +115,7 @@ let incomplete_network = @network_component begin (p, d), 0 <--> X end - p_start = [p => 1.0, d => 0.2] + p_start = [:p => 1.0, :d => 0.2] # Computes bifurcation diagram. @test_throws Exception hc_steady_states(incomplete_network, p_start) From 34ab871a629f25c644a43f67cf19e90618eba8cf Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 2 Apr 2024 22:07:12 -0400 Subject: [PATCH 48/51] compelteness error for SI --- .../structural_identifiability_extension.jl | 2 +- test/model_simulation/simulate_SDEs.jl | 2 +- test/runtests.jl | 52 ------------------- 3 files changed, 2 insertions(+), 54 deletions(-) diff --git a/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl b/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl index 68c4c8fa4c..c39af189da 100644 --- a/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl +++ b/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl @@ -157,7 +157,7 @@ end function make_osys(rs::ReactionSystem; remove_conserved=true) # Creates the ODESystem corresponding to the ReactionSystem (expanding functions and flattening it). # Creates a list of the systems all symbols (unknowns and parameters). - iscomplete(rs) || error(COMPLETENESS_ERROR) + ModelingToolkit.iscomplete(rs) || error("Identifiability should only be computed for complete systems. A ReactionSystem can be marked as complete using the `complete` function.") rs = Catalyst.expand_registered_functions(flatten(rs)) osys = complete(convert(ODESystem, rs; remove_conserved)) vars = [unknowns(rs); parameters(rs)] diff --git a/test/model_simulation/simulate_SDEs.jl b/test/model_simulation/simulate_SDEs.jl index c33d62fa48..617d1de53e 100644 --- a/test/model_simulation/simulate_SDEs.jl +++ b/test/model_simulation/simulate_SDEs.jl @@ -282,7 +282,7 @@ let end # Tests using complicated noise scaling expressions. -@time let +let noise_scaling_network = @reaction_network begin @parameters η1 η2 η3 η4 @species N1(t) N2(t)=0.5 diff --git a/test/runtests.jl b/test/runtests.jl index c9cfa47ae9..2962ff3c09 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,58 +3,6 @@ using SafeTestsets ### Run the tests ### @time begin - - ### Tests the properties of ReactionSystems. ### - @time @safetestset "Reactions" begin include("reactionsystem_structure/reactions.jl") end - @time @safetestset "ReactionSystem" begin include("reactionsystem_structure/reactionsystem.jl") end - @time @safetestset "Higher Order Reactions" begin include("reactionsystem_structure/higher_order_reactions.jl") end - - ### Tests model creation via the @reaction_network DSL. ### - @time @safetestset "Basic DSL" begin include("dsl/dsl_basics.jl") end - @time @safetestset "DSL Model Construction" begin include("dsl/dsl_model_construction.jl") end - @time @safetestset "Custom CRN Functions" begin include("dsl/custom_functions.jl") end - @time @safetestset "DSL Options" begin include("dsl/dsl_options.jl") end - - ### Non-DSL model creation and modification. ### - @time @safetestset "ReactionSystem Components Based Creation" begin include("programmatic_model_creation/component_based_model_creation.jl") end - @time @safetestset "Programmatic Model Expansion" begin include("programmatic_model_creation/programmatic_model_expansion.jl") end - - # Runs various miscellaneous tests. - @time @safetestset "API" begin include("miscellaneous_tests/api.jl") end - @time @safetestset "Symbolic Stoichiometry" begin include("miscellaneous_tests/symbolic_stoichiometry.jl") end - @time @safetestset "NonlinearProblems and Steady State Solving" begin include("miscellaneous_tests/nonlinear_solve.jl") end - @time @safetestset "Events" begin include("miscellaneous_tests/events.jl") end - @time @safetestset "Compound species" begin include("miscellaneous_tests/compound_macro.jl") end - @time @safetestset "Reaction balancing" begin include("miscellaneous_tests/reaction_balancing.jl") end - @time @safetestset "Units" begin include("miscellaneous_tests/units.jl") end - - ### Reaction network analysis. ### - @time @safetestset "Conservation Laws" begin include("network_analysis/conservation_laws.jl") end - @time @safetestset "Network Properties" begin include("network_analysis/network_properties.jl") end - - ### Tests ODE, SDE, PDE, and Gillespie Simulations. ### - @time @safetestset "ODE System Simulations" begin include("model_simulation/simulate_ODEs.jl") end - @time @safetestset "Automatic Jacobian Construction" begin include("model_simulation/make_jacobian.jl") end - @time @safetestset "U0 and Parameters Input Variants" begin include("model_simulation/u0_n_parameter_inputs.jl") end - @time @safetestset "SDE System Simulations" begin include("model_simulation/simulate_SDEs.jl") end - @time @safetestset "Jump System Simulations" begin include("model_simulation/simulate_jumps.jl") end - - ### Tests Spatial Network Simulations. ### - @time @safetestset "PDE Systems Simulations" begin include("spatial_reaction_systems/simulate_PDEs.jl") end - @time @safetestset "Lattice Reaction Systems" begin include("spatial_reaction_systems/lattice_reaction_systems.jl") end - @time @safetestset "ODE Lattice Systems Simulations" begin include("spatial_reaction_systems/lattice_reaction_systems_ODEs.jl") end - - ### Tests network visualization. ### - @time @safetestset "Latexify" begin include("visualization/latexify.jl") end - # @time @safetestset "Basic Plotting" begin include("visualization/plotting.jl") end - # Disable on Macs as can't install GraphViz via jll - if !Sys.isapple() - @time @safetestset "Graphs" begin include("visualization/graphs.jl") end - end - - ### Tests extensions. ### - @time @safetestset "BifurcationKit Extension" begin include("extensions/bifurcation_kit.jl") end - @time @safetestset "HomotopyContinuation Extension" begin include("extensions/homotopy_continuation.jl") end @time @safetestset "Structural Identifiability Extension" begin include("extensions/structural_identifiability.jl") end end # @time From d1035255d1d4d5bbccfc5d5be145bf445ef3e3c4 Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 2 Apr 2024 22:07:35 -0400 Subject: [PATCH 49/51] bettter completeness error --- test/runtests.jl | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index 2962ff3c09..c9cfa47ae9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,6 +3,58 @@ using SafeTestsets ### Run the tests ### @time begin + + ### Tests the properties of ReactionSystems. ### + @time @safetestset "Reactions" begin include("reactionsystem_structure/reactions.jl") end + @time @safetestset "ReactionSystem" begin include("reactionsystem_structure/reactionsystem.jl") end + @time @safetestset "Higher Order Reactions" begin include("reactionsystem_structure/higher_order_reactions.jl") end + + ### Tests model creation via the @reaction_network DSL. ### + @time @safetestset "Basic DSL" begin include("dsl/dsl_basics.jl") end + @time @safetestset "DSL Model Construction" begin include("dsl/dsl_model_construction.jl") end + @time @safetestset "Custom CRN Functions" begin include("dsl/custom_functions.jl") end + @time @safetestset "DSL Options" begin include("dsl/dsl_options.jl") end + + ### Non-DSL model creation and modification. ### + @time @safetestset "ReactionSystem Components Based Creation" begin include("programmatic_model_creation/component_based_model_creation.jl") end + @time @safetestset "Programmatic Model Expansion" begin include("programmatic_model_creation/programmatic_model_expansion.jl") end + + # Runs various miscellaneous tests. + @time @safetestset "API" begin include("miscellaneous_tests/api.jl") end + @time @safetestset "Symbolic Stoichiometry" begin include("miscellaneous_tests/symbolic_stoichiometry.jl") end + @time @safetestset "NonlinearProblems and Steady State Solving" begin include("miscellaneous_tests/nonlinear_solve.jl") end + @time @safetestset "Events" begin include("miscellaneous_tests/events.jl") end + @time @safetestset "Compound species" begin include("miscellaneous_tests/compound_macro.jl") end + @time @safetestset "Reaction balancing" begin include("miscellaneous_tests/reaction_balancing.jl") end + @time @safetestset "Units" begin include("miscellaneous_tests/units.jl") end + + ### Reaction network analysis. ### + @time @safetestset "Conservation Laws" begin include("network_analysis/conservation_laws.jl") end + @time @safetestset "Network Properties" begin include("network_analysis/network_properties.jl") end + + ### Tests ODE, SDE, PDE, and Gillespie Simulations. ### + @time @safetestset "ODE System Simulations" begin include("model_simulation/simulate_ODEs.jl") end + @time @safetestset "Automatic Jacobian Construction" begin include("model_simulation/make_jacobian.jl") end + @time @safetestset "U0 and Parameters Input Variants" begin include("model_simulation/u0_n_parameter_inputs.jl") end + @time @safetestset "SDE System Simulations" begin include("model_simulation/simulate_SDEs.jl") end + @time @safetestset "Jump System Simulations" begin include("model_simulation/simulate_jumps.jl") end + + ### Tests Spatial Network Simulations. ### + @time @safetestset "PDE Systems Simulations" begin include("spatial_reaction_systems/simulate_PDEs.jl") end + @time @safetestset "Lattice Reaction Systems" begin include("spatial_reaction_systems/lattice_reaction_systems.jl") end + @time @safetestset "ODE Lattice Systems Simulations" begin include("spatial_reaction_systems/lattice_reaction_systems_ODEs.jl") end + + ### Tests network visualization. ### + @time @safetestset "Latexify" begin include("visualization/latexify.jl") end + # @time @safetestset "Basic Plotting" begin include("visualization/plotting.jl") end + # Disable on Macs as can't install GraphViz via jll + if !Sys.isapple() + @time @safetestset "Graphs" begin include("visualization/graphs.jl") end + end + + ### Tests extensions. ### + @time @safetestset "BifurcationKit Extension" begin include("extensions/bifurcation_kit.jl") end + @time @safetestset "HomotopyContinuation Extension" begin include("extensions/homotopy_continuation.jl") end @time @safetestset "Structural Identifiability Extension" begin include("extensions/structural_identifiability.jl") end end # @time From c9297ace8c9c44717c312f8b36dd56541bd1cf1b Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 2 Apr 2024 22:45:32 -0400 Subject: [PATCH 50/51] imrpvoe extension compeleness handling --- .../structural_identifiability_extension.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl b/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl index c39af189da..9ae15dc55e 100644 --- a/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl +++ b/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl @@ -158,7 +158,7 @@ function make_osys(rs::ReactionSystem; remove_conserved=true) # Creates the ODESystem corresponding to the ReactionSystem (expanding functions and flattening it). # Creates a list of the systems all symbols (unknowns and parameters). ModelingToolkit.iscomplete(rs) || error("Identifiability should only be computed for complete systems. A ReactionSystem can be marked as complete using the `complete` function.") - rs = Catalyst.expand_registered_functions(flatten(rs)) + rs = complete(Catalyst.expand_registered_functions(flatten(rs))) osys = complete(convert(ODESystem, rs; remove_conserved)) vars = [unknowns(rs); parameters(rs)] From 9bbb8d8b70a32a7e8461232562975dbfe2c71b02 Mon Sep 17 00:00:00 2001 From: Torkel Date: Tue, 2 Apr 2024 23:15:40 -0400 Subject: [PATCH 51/51] up --- test/extensions/structural_identifiability.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/extensions/structural_identifiability.jl b/test/extensions/structural_identifiability.jl index 38e7753277..520ccd6a23 100644 --- a/test/extensions/structural_identifiability.jl +++ b/test/extensions/structural_identifiability.jl @@ -211,6 +211,7 @@ let (k3, k4), X3 <--> X4 end @named rs_catalyst = compose(rs1, [rs2]) + rs_catalyst = complete(rs_catalyst) @unpack X1, X2, k1, k2 = rs1 gi_1 = assess_identifiability(rs_catalyst; measured_quantities=[X1, X2, rs2.X3], known_p=[k1]) li_1 = assess_local_identifiability(rs_catalyst; measured_quantities=[X1, X2, rs2.X3], known_p=[k1])