From 64c8b0e0215c00a8cdfe2587cc1c30a305770102 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Wed, 3 Jun 2026 12:29:43 -0400 Subject: [PATCH 1/2] Centralize CI on SciML reusable workflows; apply Runic + fix typos Convert/ensure CI checks use the centralized SciML/.github reusable workflows pinned at @v1, with `secrets: "inherit"` on every caller. Workflow changes: - Downgrade.yml: inline -> downgrade.yml@v1 (julia-version 1.10, skip "Pkg,TOML", allow-reresolve false; preserves prior behavior). - SpellCheck.yml: inline crate-ci/typos -> spellcheck.yml@v1. - Runic.yml: ADDED runic.yml@v1 format check (repo previously only had format-suggestions-on-pr, no enforcing Runic check). All .jl files parse cleanly, so no `exclude:` needed. - Test.yml: already a tests.yml@v1 caller; unchanged. - Documentation.yml / TestExtensions.yml: left inline (custom behavior the central callers cannot reproduce; see PR notes). Formatting + spelling: - Ran Runic in-place across the repo. - Fixed clear prose typos (typos -w); added `oint` and `Lamba` to .typos.toml as domain identifiers (ODEIntegrator var; LambaEM solver). dependabot.yml: - Removed the crate-ci/typos ignore (version pinned centrally now). - julia ecosystem dirs: "/", "/docs", "/test/extensions" (the latter has a Project.toml; "/test" has none and was dropped). Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/dependabot.yml | 5 +- .github/workflows/Downgrade.yml | 26 +- .github/workflows/Runic.yml | 15 + .github/workflows/SpellCheck.yml | 12 +- .typos.toml | 2 + docs/make.jl | 24 +- docs/pages.jl | 170 +++--- .../global_sensitivity_analysis.md | 2 +- .../optimization_ode_param_fitting.md | 4 +- .../model_creation/functional_parameters.md | 2 +- .../bifurcation_kit_extension.jl | 18 +- ext/CatalystCairoMakieExtension.jl | 2 +- ...cairo_makie_extension_spatial_modelling.jl | 66 ++- ...graph_makie_extension_spatial_modelling.jl | 22 +- .../rn_graph_plot.jl | 60 ++- .../homotopy_continuation_extension.jl | 67 ++- .../structural_identifiability_extension.jl | 72 ++- src/Catalyst.jl | 34 +- src/chemistry_functionality.jl | 102 ++-- src/dsl.jl | 304 ++++++----- src/expression_utils.jl | 4 +- src/latex_utils.jl | 2 +- src/latexify_recipes.jl | 58 ++- src/mtk_nullspace_function.jl | 56 +- src/network_analysis.jl | 208 ++++---- src/plotting.jl | 11 +- src/reaction.jl | 94 ++-- src/reactionsystem.jl | 294 +++++++---- src/reactionsystem_conversions.jl | 490 +++++++++++------- .../serialisation_support.jl | 65 ++- .../serialise_fields.jl | 30 +- .../serialise_reactionsystem.jl | 52 +- src/registered_functions.jl | 20 +- .../discrete_space_jump_systems.jl | 60 ++- .../discrete_space_reaction_systems.jl | 107 ++-- .../discrete_space_sim_struct_interfacing.jl | 66 ++- .../spatial_ODE_systems.jl | 100 ++-- .../spatial_reactions.jl | 40 +- src/spatial_reaction_systems/utility.jl | 74 ++- src/steady_state_stability.jl | 18 +- src/unit_helpers.jl | 158 ++++-- test/dsl/dsl_basic_model_construction.jl | 192 ++++--- test/extensions/bifurcation_kit.jl | 52 +- test/extensions/dspace_simulation_plotting.jl | 12 +- test/extensions/graphmakie.jl | 34 +- test/extensions/homotopy_continuation.jl | 50 +- test/extensions/stability_computation.jl | 32 +- test/extensions/structural_identifiability.jl | 150 +++--- test/miscellaneous_tests/compound_macro.jl | 72 +-- test/miscellaneous_tests/explicit_imports.jl | 12 +- .../miscellaneous_tests/reaction_balancing.jl | 90 ++-- .../reactionsystem_serialisation.jl | 120 +++-- test/network_analysis/conservation_laws.jl | 96 ++-- test/network_analysis/crn_theory.jl | 26 +- test/network_analysis/network_properties.jl | 211 ++++---- .../coupled_equation_crn_systems.jl | 226 ++++---- .../custom_crn_functions.jl | 194 ++++--- test/reactionsystem_core/events.jl | 122 +++-- .../functional_parameters.jl | 65 +-- .../higher_order_reactions.jl | 26 +- .../parameter_type_designation.jl | 14 +- test/reactionsystem_core/reaction.jl | 84 +-- test/reactionsystem_core/reactionsystem.jl | 378 ++++++++------ .../symbolic_stoichiometry.jl | 90 ++-- test/runtests.jl | 154 ++++-- test/runtests_extensions.jl | 24 +- test/simulation_and_solving/hybrid_models.jl | 154 +++--- .../jacobian_construction.jl | 72 +-- test/simulation_and_solving/simulate_ODEs.jl | 54 +- test/simulation_and_solving/simulate_SDEs.jl | 81 +-- test/simulation_and_solving/simulate_jumps.jl | 62 ++- .../simulation_and_solving/solve_nonlinear.jl | 48 +- .../dspace_reaction_systems.jl | 88 ++-- .../dspace_reaction_systems_ODEs.jl | 200 +++---- .../dspace_reaction_systems_jumps.jl | 44 +- .../dspace_reaction_systems_space_types.jl | 112 ++-- .../dspace_simulation_struct_interfacing.jl | 98 ++-- test/spatial_modelling/simulate_PDEs.jl | 46 +- test/spatial_modelling/spatial_reactions.jl | 18 +- test/spatial_test_networks.jl | 42 +- test/test_functions.jl | 16 +- test/test_networks.jl | 20 +- test/upstream/mtk_structure_indexing.jl | 196 ++++--- 83 files changed, 4027 insertions(+), 2866 deletions(-) create mode 100644 .github/workflows/Runic.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index cd7b7f2490..f61be96b6d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,14 +5,11 @@ updates: directory: "/" # Location of package manifests schedule: interval: "weekly" - ignore: - - dependency-name: "crate-ci/typos" - update-types: ["version-update:semver-patch", "version-update:semver-minor"] - package-ecosystem: "julia" directories: - "/" - "/docs" - - "/test" + - "/test/extensions" schedule: interval: "daily" groups: diff --git a/.github/workflows/Downgrade.yml b/.github/workflows/Downgrade.yml index 2ea21366fe..413d4d56c5 100644 --- a/.github/workflows/Downgrade.yml +++ b/.github/workflows/Downgrade.yml @@ -11,21 +11,11 @@ on: paths-ignore: - 'docs/**' jobs: - test: - runs-on: ubuntu-latest - strategy: - matrix: - downgrade_mode: ['alldeps'] - julia-version: ['1.10'] - steps: - - uses: actions/checkout@v6 - - uses: julia-actions/setup-julia@v2 - with: - version: ${{ matrix.julia-version }} - - uses: julia-actions/julia-downgrade-compat@v2 - with: - skip: Pkg,TOML - - uses: julia-actions/julia-buildpkg@v1 - - uses: julia-actions/julia-runtest@v1 - with: - ALLOW_RERESOLVE: false \ No newline at end of file + downgrade: + name: "Downgrade" + uses: "SciML/.github/.github/workflows/downgrade.yml@v1" + with: + julia-version: "1.10" + skip: "Pkg,TOML" + allow-reresolve: false + secrets: "inherit" diff --git a/.github/workflows/Runic.yml b/.github/workflows/Runic.yml new file mode 100644 index 0000000000..e1cf7d9ad9 --- /dev/null +++ b/.github/workflows/Runic.yml @@ -0,0 +1,15 @@ +name: "Runic Formatting" + +on: + push: + branches: + - 'master' + - 'release-' + tags: '*' + pull_request: + +jobs: + runic: + name: "Runic Format Check" + uses: "SciML/.github/.github/workflows/runic.yml@v1" + secrets: "inherit" diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml index a8e5983d09..c0e2934d45 100644 --- a/.github/workflows/SpellCheck.yml +++ b/.github/workflows/SpellCheck.yml @@ -3,11 +3,7 @@ name: Spell Check on: [pull_request] jobs: - typos-check: - name: Spell Check with Typos - runs-on: ubuntu-latest - steps: - - name: Checkout Actions Repository - uses: actions/checkout@v6 - - name: Check spelling - uses: crate-ci/typos@v1.18.0 + spellcheck: + name: "Spell Check with Typos" + uses: "SciML/.github/.github/workflows/spellcheck.yml@v1" + secrets: "inherit" diff --git a/.typos.toml b/.typos.toml index 5209b91ea0..929b80d11f 100644 --- a/.typos.toml +++ b/.typos.toml @@ -2,6 +2,8 @@ # Catalyst specific systemes = "systemes" Hass = "Hass" +oint = "oint" # ODEIntegrator variable name used in tests +Lamba = "Lamba" # LambaEM/LambaEulerHeun SDE solvers (StochasticDiffEq) # Julia-specific functions indexin = "indexin" diff --git a/docs/make.jl b/docs/make.jl index e9d15219fa..706dbfccbe 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -32,20 +32,28 @@ include("pages.jl") # clean = true, # pages = pages) -makedocs(sitename = "Catalyst.jl", +makedocs( + sitename = "Catalyst.jl", authors = "Samuel Isaacson", - format = Documenter.HTML(; analytics = "UA-90474609-3", + format = Documenter.HTML(; + analytics = "UA-90474609-3", prettyurls = (get(ENV, "CI", nothing) == "true"), collapselevel = 1, assets = ["assets/favicon.ico"], - canonical = "https://docs.sciml.ai/Catalyst/stable/"), - modules = [Catalyst, ModelingToolkitBase, SymbolicIndexingInterface, - Base.get_extension(Catalyst, :CatalystGraphMakieExtension)], + canonical = "https://docs.sciml.ai/Catalyst/stable/" + ), + modules = [ + Catalyst, ModelingToolkitBase, SymbolicIndexingInterface, + Base.get_extension(Catalyst, :CatalystGraphMakieExtension), + ], doctest = false, clean = true, pages = pages, pagesonly = true, - warnonly = [:missing_docs, :cross_references]) # `:cross_references` here temporarily while getting docs to work on v16. + warnonly = [:missing_docs, :cross_references] +) # `:cross_references` here temporarily while getting docs to work on v16. -deploydocs(repo = "github.com/SciML/Catalyst.jl.git"; - push_preview = true) +deploydocs( + repo = "github.com/SciML/Catalyst.jl.git"; + push_preview = true +) diff --git a/docs/pages.jl b/docs/pages.jl index 9e80f95acf..55db8e474d 100644 --- a/docs/pages.jl +++ b/docs/pages.jl @@ -1,87 +1,87 @@ 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/math_models_intro.md" -], -"Model creation and properties" => Any[ - "model_creation/dsl_basics.md", - "model_creation/dsl_advanced.md", - "model_creation/programmatic_CRN_construction.md", - "model_creation/compositional_modeling.md", - "model_creation/coupled_non_crn_models.md", - "model_creation/events.md", - "model_creation/conservation_laws.md", - "model_creation/parametric_stoichiometry.md", - "model_creation/functional_parameters.md", - "model_creation/model_file_loading_and_export.md", - "model_creation/model_visualisation.md", - "model_creation/reactionsystem_content_accessing.md", - "model_creation/chemistry_related_functionality.md", - "Examples" => Any[ - "model_creation/examples/basic_CRN_library.md", - "model_creation/examples/programmatic_generative_linear_pathway.md", - "model_creation/examples/hodgkin_huxley_equation.md", - "model_creation/examples/smoluchowski_coagulation_equation.md", - "model_creation/examples/noise_modelling_approaches.md" - ] -], -"Model simulation and visualization" => Any[ - "model_simulation/simulation_introduction.md", - "model_simulation/simulation_plotting.md", - "model_simulation/simulation_structure_interfacing.md", - "model_simulation/ensemble_simulations.md", - "model_simulation/hybrid_simulations.md", - "model_simulation/ode_simulation_performance.md", - "model_simulation/sde_simulation_performance.md", - # "model_simulation/finite_state_projection_simulation.md", # Wait for update with new version. - "Examples" => Any[ - "model_simulation/examples/periodic_events_simulation.md", - "model_simulation/examples/activation_time_distribution_measurement.md", - "model_simulation/examples/interactive_brusselator_simulation.md" - ] -], -"Network Analysis" => Any[ - "network_analysis/odes.md", - "network_analysis/crn_theory.md", - "network_analysis/network_properties.md" -], -"Steady state analysis" => Any[ - "steady_state_functionality/homotopy_continuation.md", - "steady_state_functionality/nonlinear_solve.md", - "steady_state_functionality/steady_state_stability_computation.md", - "steady_state_functionality/bifurcation_diagrams.md", - "steady_state_functionality/dynamical_systems.md", - "Examples" => Any[ - "steady_state_functionality/examples/nullcline_plotting.md", - "steady_state_functionality/examples/bifurcationkit_periodic_orbits.md", - "steady_state_functionality/examples/bifurcationkit_codim2.md" - ] -], -"Inverse problems" => Any[ - "inverse_problems/petab_ode_param_fitting.md", - "inverse_problems/optimization_ode_param_fitting.md", - "inverse_problems/behaviour_optimisation.md", - "inverse_problems/structural_identifiability.md", - "inverse_problems/likelihood_profiler.md", - "inverse_problems/global_sensitivity_analysis.md", - "inverse_problems/turing_ode_param_fitting.md", - "Examples" => Any[ - "inverse_problems/examples/ode_fitting_oscillation.md" - ] -], -"Spatial modelling" => Any[ - "spatial_modelling/discrete_space_reaction_systems.md", - "spatial_modelling/discrete_space_simulation_structure_interaction.md", - "spatial_modelling/discrete_space_simulation_plotting.md", - "spatial_modelling/spatial_ode_simulations.md", - "spatial_modelling/spatial_jump_simulations.md" -], -"FAQs" => "faqs.md", -"API" => Any[ - "api/core_api.md", - "api/network_analysis_api.md" -], -"Developer Documentation" => "devdocs/dev_guide.md" + "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/math_models_intro.md", + ], + "Model creation and properties" => Any[ + "model_creation/dsl_basics.md", + "model_creation/dsl_advanced.md", + "model_creation/programmatic_CRN_construction.md", + "model_creation/compositional_modeling.md", + "model_creation/coupled_non_crn_models.md", + "model_creation/events.md", + "model_creation/conservation_laws.md", + "model_creation/parametric_stoichiometry.md", + "model_creation/functional_parameters.md", + "model_creation/model_file_loading_and_export.md", + "model_creation/model_visualisation.md", + "model_creation/reactionsystem_content_accessing.md", + "model_creation/chemistry_related_functionality.md", + "Examples" => Any[ + "model_creation/examples/basic_CRN_library.md", + "model_creation/examples/programmatic_generative_linear_pathway.md", + "model_creation/examples/hodgkin_huxley_equation.md", + "model_creation/examples/smoluchowski_coagulation_equation.md", + "model_creation/examples/noise_modelling_approaches.md", + ], + ], + "Model simulation and visualization" => Any[ + "model_simulation/simulation_introduction.md", + "model_simulation/simulation_plotting.md", + "model_simulation/simulation_structure_interfacing.md", + "model_simulation/ensemble_simulations.md", + "model_simulation/hybrid_simulations.md", + "model_simulation/ode_simulation_performance.md", + "model_simulation/sde_simulation_performance.md", + # "model_simulation/finite_state_projection_simulation.md", # Wait for update with new version. + "Examples" => Any[ + "model_simulation/examples/periodic_events_simulation.md", + "model_simulation/examples/activation_time_distribution_measurement.md", + "model_simulation/examples/interactive_brusselator_simulation.md", + ], + ], + "Network Analysis" => Any[ + "network_analysis/odes.md", + "network_analysis/crn_theory.md", + "network_analysis/network_properties.md", + ], + "Steady state analysis" => Any[ + "steady_state_functionality/homotopy_continuation.md", + "steady_state_functionality/nonlinear_solve.md", + "steady_state_functionality/steady_state_stability_computation.md", + "steady_state_functionality/bifurcation_diagrams.md", + "steady_state_functionality/dynamical_systems.md", + "Examples" => Any[ + "steady_state_functionality/examples/nullcline_plotting.md", + "steady_state_functionality/examples/bifurcationkit_periodic_orbits.md", + "steady_state_functionality/examples/bifurcationkit_codim2.md", + ], + ], + "Inverse problems" => Any[ + "inverse_problems/petab_ode_param_fitting.md", + "inverse_problems/optimization_ode_param_fitting.md", + "inverse_problems/behaviour_optimisation.md", + "inverse_problems/structural_identifiability.md", + "inverse_problems/likelihood_profiler.md", + "inverse_problems/global_sensitivity_analysis.md", + "inverse_problems/turing_ode_param_fitting.md", + "Examples" => Any[ + "inverse_problems/examples/ode_fitting_oscillation.md", + ], + ], + "Spatial modelling" => Any[ + "spatial_modelling/discrete_space_reaction_systems.md", + "spatial_modelling/discrete_space_simulation_structure_interaction.md", + "spatial_modelling/discrete_space_simulation_plotting.md", + "spatial_modelling/spatial_ode_simulations.md", + "spatial_modelling/spatial_jump_simulations.md", + ], + "FAQs" => "faqs.md", + "API" => Any[ + "api/core_api.md", + "api/network_analysis_api.md", + ], + "Developer Documentation" => "devdocs/dev_guide.md", ] diff --git a/docs/src/inverse_problems/global_sensitivity_analysis.md b/docs/src/inverse_problems/global_sensitivity_analysis.md index ea98f82263..870e381118 100644 --- a/docs/src/inverse_problems/global_sensitivity_analysis.md +++ b/docs/src/inverse_problems/global_sensitivity_analysis.md @@ -35,7 +35,7 @@ p_setter = setp_oop(oprob_base, [:β, :γ]) # Creates a function that simulates the model an returns the peak number of cases. function peak_cases(p) - # Updates the ODEProblem with teh proposed parameter set. + # Updates the ODEProblem with the proposed parameter set. p = p_setter(oprob_base, p) oprob = remake(oprob_base; p) sol = solve(oprob; maxiters = 100000, verbose = false) diff --git a/docs/src/inverse_problems/optimization_ode_param_fitting.md b/docs/src/inverse_problems/optimization_ode_param_fitting.md index e3b2d87cbf..94ba0be71f 100644 --- a/docs/src/inverse_problems/optimization_ode_param_fitting.md +++ b/docs/src/inverse_problems/optimization_ode_param_fitting.md @@ -38,11 +38,11 @@ p_setter = setp_oop(oprob_base, [:p, :d]) # A loss function measuring the sum-of-square distance between a simulation and the data. function loss(p, (oprob_base, p_setter, t_samples, X_samples)) - # Updates the ODEProblem with teh proposed parameter set. + # Updates the ODEProblem with the proposed parameter set. p = p_setter(oprob_base, p) oprob = remake(oprob_base; p) - # Simulate the model. If sucesfull, return sum-of-squares distance as loss. + # Simulate the model. If successful, return sum-of-squares distance as loss. sol = solve(oprob; saveat = t_samples, verbose = false, maxiters = 10000) SciMLBase.successful_retcode(sol) || return Inf return sum(abs2, sol[:X] .- X_samples) diff --git a/docs/src/model_creation/functional_parameters.md b/docs/src/model_creation/functional_parameters.md index 61cb8e913a..2c5946b767 100644 --- a/docs/src/model_creation/functional_parameters.md +++ b/docs/src/model_creation/functional_parameters.md @@ -34,7 +34,7 @@ bd_model = @reaction_network begin d, X --> 0 end -# Finally we simualte the model normally, but using `spline` as the `pIn` parameter's value. +# Finally we simulate the model normally, but using `spline` as the `pIn` parameter's value. u0 = [:X => 0.5] ps = [:d => 2.0, :pIn => spline] oprob = ODEProblem(bd_model, u0, tend, ps) diff --git a/ext/CatalystBifurcationKitExtension/bifurcation_kit_extension.jl b/ext/CatalystBifurcationKitExtension/bifurcation_kit_extension.jl index 09e520fc84..56ab85fcc9 100644 --- a/ext/CatalystBifurcationKitExtension/bifurcation_kit_extension.jl +++ b/ext/CatalystBifurcationKitExtension/bifurcation_kit_extension.jl @@ -1,8 +1,10 @@ ### Dispatch for BifurcationKit BifurcationProblems ### # Creates a BifurcationProblem, using a ReactionSystem as an input. -function BK.BifurcationProblem(rs::ReactionSystem, u0_bif, ps, bif_par, args...; - plot_var = nothing, record_from_solution = BK.record_sol_default, jac = true, u0 = [], kwargs...) +function BK.BifurcationProblem( + rs::ReactionSystem, u0_bif, ps, bif_par, args...; + plot_var = nothing, record_from_solution = BK.record_sol_default, jac = true, u0 = [], kwargs... + ) if !isautonomous(rs) error("Attempting to create a `BifurcationProblem` for a non-autonomous system (e.g. where some rate depend on $(get_iv(rs))). This is not possible.") end @@ -20,19 +22,21 @@ function BK.BifurcationProblem(rs::ReactionSystem, u0_bif, ps, bif_par, args...; u0 = Catalyst.symmap_to_varmap(rs, u0) end - # Creates nonlinear System. If there are conservation laws, these are manually added as + # Creates nonlinear System. If there are conservation laws, these are manually added as # equations, and the conservationlaw parameter values are manually computed and added to `ps`. Catalyst.conservationlaw_errorcheck(rs, merge(Dict(ps), Dict(u0))) nsys = if Catalyst.num_cons_laws(rs) == 0 complete(ss_ode_model(rs)) else - Γ_vals = Catalyst.get_networkproperties(rs).conservedconst => + Γ_vals = Catalyst.get_networkproperties(rs).conservedconst => [Symbolics.substitute(ceq.rhs, u0) for ceq in conservationlaw_constants(rs)] ps = merge(Dict(ps), Dict(Γ_vals)) complete(ss_ode_model(rs; remove_conserved = true, include_cl_as_eqs = true)) end # Makes BifurcationProblem (this call goes through the ModelingToolkit-based BifurcationKit extension). - return BK.BifurcationProblem(nsys, u0_bif, ps, bif_par, args...; plot_var, - record_from_solution, jac, kwargs...) -end \ No newline at end of file + return BK.BifurcationProblem( + nsys, u0_bif, ps, bif_par, args...; plot_var, + record_from_solution, jac, kwargs... + ) +end diff --git a/ext/CatalystCairoMakieExtension.jl b/ext/CatalystCairoMakieExtension.jl index aadbc6e1b6..e50df3bef9 100644 --- a/ext/CatalystCairoMakieExtension.jl +++ b/ext/CatalystCairoMakieExtension.jl @@ -3,7 +3,7 @@ module CatalystCairoMakieExtension # Fetch packages. using Catalyst, CairoMakie, SparseArrays import Catalyst: dspace_plot, dspace_animation, dspace_kymograph, - demask_vals, extract_vals, extract_grid_axes + demask_vals, extract_vals, extract_grid_axes # Creates and exports utilities for plotting discrete space simulations. include("CatalystCairoMakieExtension/cairo_makie_extension_spatial_modelling.jl") diff --git a/ext/CatalystCairoMakieExtension/cairo_makie_extension_spatial_modelling.jl b/ext/CatalystCairoMakieExtension/cairo_makie_extension_spatial_modelling.jl index dc87acdc55..b5593a6f13 100644 --- a/ext/CatalystCairoMakieExtension/cairo_makie_extension_spatial_modelling.jl +++ b/ext/CatalystCairoMakieExtension/cairo_makie_extension_spatial_modelling.jl @@ -3,13 +3,16 @@ # Internal dispatch for the plotting of a discrete space simulation on a 1d space (Cartesian or masked). function dspace_plot( sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:Catalyst.GridLattice{1, S}, T}; - t = sol.t[end], markersize = 20, kwargs...) where {Q, R, S, T} + t = sol.t[end], markersize = 20, kwargs... + ) where {Q, R, S, T} # Prepares and creates the plot. vals = spat_getu(sol, sp, dsrs; t = [t]) vals = Catalyst.demask_vals(vals, dsrs)[1] - return scatterlines(vals; axis = (xlabel = "Compartment", ylabel = "$(sp)"), - markersize = markersize, kwargs...) + return scatterlines( + vals; axis = (xlabel = "Compartment", ylabel = "$(sp)"), + markersize = markersize, kwargs... + ) end # Internal dispatch for the animation of a discrete space simulation on a 1d space (Cartesian or masked). @@ -17,7 +20,8 @@ function dspace_animation( sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:Catalyst.GridLattice{1, S}, T}, filename::String; markersize = 20, plot_min = nothing, plot_max = nothing, nframes = 200, framerate = 20, - ttitle = true, kwargs...) where {Q, R, S, T} + ttitle = true, kwargs... + ) where {Q, R, S, T} # Prepares the inputs to the figure. t = LinRange(sol.prob.tspan[1], sol.prob.tspan[2], nframes) @@ -35,9 +39,11 @@ function dspace_animation( (; title = Makie.@lift(string("Time: ", round(t[$frame]; sigdigits = 3)))), ) end - fig, ax, plt = scatterlines(Makie.@lift(vals[$frame]); + fig, ax, plt = scatterlines( + Makie.@lift(vals[$frame]); axis = axis_kwargs, - markersize = markersize, kwargs...) + markersize = markersize, kwargs... + ) # Creates the animation. record(fig, filename, 1:1:nframes; framerate) do i @@ -50,7 +56,8 @@ end function dspace_kymograph( sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:Catalyst.GridLattice{1, S}, T}; colormap = :BuGn_7, - plot_min = nothing, plot_max = nothing, nframes = 200, kwargs...) where {Q, R, S, T} + plot_min = nothing, plot_max = nothing, nframes = 200, kwargs... + ) where {Q, R, S, T} # Prepares the inputs to the figure. t = LinRange(sol.prob.tspan[1], sol.prob.tspan[2], nframes) @@ -59,22 +66,28 @@ function dspace_kymograph( # Creates the figure. y_vals = LinRange(1, grid_size(dsrs)[1], grid_size(dsrs)[1]) - return heatmap(t, + return heatmap( + t, y_vals, vals; - axis = (xlabel = "Time", ylabel = "Compartment", - xgridvisible = false, ygridvisible = false), + axis = ( + xlabel = "Time", ylabel = "Compartment", + xgridvisible = false, ygridvisible = false, + ), colormap, - colorrange = (plot_min, plot_max)) + colorrange = (plot_min, plot_max) + ) end ### 2d Lattice Simulation Plots/Animations ### # Internal dispatch for the plotting of a discrete space simulation on a 2d space (Cartesian or masked). -function dspace_plot(sol, sp, +function dspace_plot( + sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:Catalyst.GridLattice{2, S}, T}; t = sol.t[end], colormap = :BuGn_7, plot_min = nothing, plot_max = nothing, - kwargs...) where {Q, R, S, T} + kwargs... + ) where {Q, R, S, T} # Prepares the inputs to the figure (the `extract_vals` call only finds limits). _, plot_min, plot_max = Catalyst.extract_vals(sol, sp, dsrs, plot_min, plot_max, nothing) @@ -83,14 +96,18 @@ function dspace_plot(sol, sp, x_vals, y_vals = Catalyst.extract_grid_axes(dsrs) # Creates the figure. - return heatmap(x_vals, + return heatmap( + x_vals, y_vals, vals; - axis = (xlabel = "Compartment", ylabel = "Compartment", - xgridvisible = false, ygridvisible = false), + axis = ( + xlabel = "Compartment", ylabel = "Compartment", + xgridvisible = false, ygridvisible = false, + ), colormap, colorrange = (plot_min, plot_max), - kwargs...) + kwargs... + ) end # Internal dispatch for the animation of a discrete space simulation on a 2d space (Cartesian or masked). @@ -98,7 +115,8 @@ function dspace_animation( sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:Catalyst.GridLattice{2, S}, T}, filename::String; colormap = :BuGn_7, nframes = 200, framerate = 20, plot_min = nothing, - plot_max = nothing, ttitle::Bool = true, kwargs...) where {Q, R, S, T} + plot_max = nothing, ttitle::Bool = true, kwargs... + ) where {Q, R, S, T} # Prepares the inputs to the figure. t = LinRange(sol.prob.tspan[1], sol.prob.tspan[2], nframes) @@ -117,10 +135,12 @@ function dspace_animation( (; title = Makie.@lift(string("Time: ", round(t[$frame]; sigdigits = 3)))), ) end - fig, ax, hm = heatmap(x_vals, y_vals, Makie.@lift(vals[$frame]); + fig, ax, hm = heatmap( + x_vals, y_vals, Makie.@lift(vals[$frame]); axis = axis_kwargs, colormap, colorrange = (plot_min, plot_max), - kwargs...) + kwargs... + ) # Creates the animation. record(fig, filename, 1:1:nframes; framerate) do i @@ -134,13 +154,15 @@ end # Internal dispatch for the plotting of a discrete space simulation on a 3d space (Cartesian or masked). function dspace_plot( sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:Catalyst.GridLattice{3, S}, T}; - kwargs...) where {Q, R, S, T} + kwargs... + ) where {Q, R, S, T} throw(ArgumentError("The `dspace_plot` function does not support 3d Cartesian/masked spaces.")) end # Internal dispatch for the animation of a space simulation on a 3d space (Cartesian or masked). function dspace_animation( sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:Catalyst.GridLattice{3, S}, T}, - filename::String; kwargs...) where {Q, R, S, T} + filename::String; kwargs... + ) where {Q, R, S, T} throw(ArgumentError("The `dspace_animation` function does not support 3d Cartesian/masked spaces.")) end diff --git a/ext/CatalystGraphMakieExtension/graph_makie_extension_spatial_modelling.jl b/ext/CatalystGraphMakieExtension/graph_makie_extension_spatial_modelling.jl index e52e81dc6e..d5d1c7f36f 100644 --- a/ext/CatalystGraphMakieExtension/graph_makie_extension_spatial_modelling.jl +++ b/ext/CatalystGraphMakieExtension/graph_makie_extension_spatial_modelling.jl @@ -1,9 +1,11 @@ ### Graph Lattice Simulation Plots/Animations ### -# Internal dispatch for the plotting of a discrete space simulation on a unstructured (graph) space. -function dspace_plot(sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:AbstractGraph, T}; +# Internal dispatch for the plotting of a discrete space simulation on a unstructured (graph) space. +function dspace_plot( + sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:AbstractGraph, T}; t = sol.t[end], plot_min = nothing, plot_max = nothing, colormap = :BuGn_7, - node_size = 50, kwargs...) where {Q, R, T} + node_size = 50, kwargs... + ) where {Q, R, T} # Prepares the inputs to the figure. plot_graph = SimpleGraph(Catalyst.dspace(dsrs)) _, plot_min, plot_max = Catalyst.extract_vals(sol, sp, dsrs, plot_min, plot_max, nothing) @@ -11,16 +13,18 @@ function dspace_plot(sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:Abstract vals = Catalyst.demask_vals(vals, dsrs)[1] println(kwargs) # Creates the figure. - return graphplot(plot_graph; node_color = vals, + return graphplot( + plot_graph; node_color = vals, node_attr = (colorrange = (plot_min, plot_max), colormap), node_size, kwargs... ) end -# Internal dispatch for the animation of a discrete space simulation on a unstructured (graph) space. +# Internal dispatch for the animation of a discrete space simulation on a unstructured (graph) space. function dspace_animation( sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:AbstractGraph, T}, filename::String; t = sol.t[end], nframes = 200, framerate = 20, plot_min = nothing, plot_max = nothing, - colormap = :BuGn_7, node_size = 50, ttitle::Bool = true, kwargs...) where {Q, R, T} + colormap = :BuGn_7, node_size = 50, ttitle::Bool = true, kwargs... + ) where {Q, R, T} # Prepares the inputs to the figure. plot_graph = SimpleGraph(Catalyst.dspace(dsrs)) t = LinRange(sol.prob.tspan[1], sol.prob.tspan[2], nframes) @@ -33,8 +37,10 @@ function dspace_animation( else (;) end - fig, ax, plt = graphplot(plot_graph; node_color = Makie.@lift(vals[$frame]), - node_attr = (colorrange = (plot_min, plot_max), colormap), node_size, axis_kwarg..., kwargs...) + fig, ax, plt = graphplot( + plot_graph; node_color = Makie.@lift(vals[$frame]), + node_attr = (colorrange = (plot_min, plot_max), colormap), node_size, axis_kwarg..., kwargs... + ) # Creates the animation. GraphMakie.record(fig, filename, 1:1:nframes; framerate) do i diff --git a/ext/CatalystGraphMakieExtension/rn_graph_plot.jl b/ext/CatalystGraphMakieExtension/rn_graph_plot.jl index 46a64381d4..8696d9898d 100644 --- a/ext/CatalystGraphMakieExtension/rn_graph_plot.jl +++ b/ext/CatalystGraphMakieExtension/rn_graph_plot.jl @@ -33,21 +33,21 @@ function SRGraphWrap(rn::ReactionSystem) for spec in deps specidx = sm[spec] has_edge(srg, specidx, i + length(specs)) ? - push!(multiedges, Graphs.SimpleEdge(specidx, i + length(specs))) : - add_edge!(srg, Graphs.SimpleEdge(specidx, i + length(specs))) + push!(multiedges, Graphs.SimpleEdge(specidx, i + length(specs))) : + add_edge!(srg, Graphs.SimpleEdge(specidx, i + length(specs))) end end end edgelist = vcat(collect(Graphs.edges(srg)), multiedges) edgeorder = sortperm(edgelist) - MultiGraphWrap(srg, multiedges, edgeorder) + return MultiGraphWrap(srg, multiedges, edgeorder) end # Automatically set edge drawing order if not supplied function MultiGraphWrap(g::SimpleDiGraph{T}, multiedges::Vector{Graphs.SimpleEdge{T}}) where {T} edgelist = vcat(collect(Graphs.edges(g)), multiedges) edgeorder = sortperm(edgelist) - MultiGraphWrap(g, multiedges, edgeorder) + return MultiGraphWrap(g, multiedges, edgeorder) end # Return the multigraph and reaction order corresponding to the complex graph. The reaction order is the order of reactions(rn) that would match the edge order given by g.edgeorder. @@ -80,7 +80,7 @@ function ComplexGraphWrap(rn::ReactionSystem) for i in 2:length(edgelist) isequal(edgelist[i], edgelist[i - 1]) && push!(multiedges, edgelist[i]) end - MultiGraphWrap(img, multiedges), rxorder + return MultiGraphWrap(img, multiedges), rxorder end Base.eltype(g::MultiGraphWrap) = eltype(g.g) @@ -101,11 +101,11 @@ function Graphs.adjacency_matrix(g::MultiGraphWrap) for e in g.multiedges adj[src(e), dst(e)] = 1 end - adj + return adj end function Graphs.edges(g::MultiGraphWrap) - edgelist = vcat(collect(Graphs.edges(g.g)), g.multiedges)[g.edgeorder] + return edgelist = vcat(collect(Graphs.edges(g.g)), g.multiedges)[g.edgeorder] end function gen_distances(g::MultiGraphWrap; inc = 0.2) @@ -114,9 +114,9 @@ function gen_distances(g::MultiGraphWrap; inc = 0.2) edgedict = Dict(edgelist[1] => [1]) for (i, e) in enumerate(@view edgelist[2:end]) if edgelist[i] != edgelist[i + 1] - edgedict[e] = [i+1] + edgedict[e] = [i + 1] else - push!(edgedict[e], i+1) + push!(edgedict[e], i + 1) end end @@ -126,18 +126,18 @@ function gen_distances(g::MultiGraphWrap; inc = 0.2) inds_ = edgedict[Edge(dst(edge), src(edge))] len = length(inds) + length(inds_) - sp = -inc/2*(len-1) - ep = sp + inc*(len-1) + sp = -inc / 2 * (len - 1) + ep = sp + inc * (len - 1) dists = collect(sp:inc:ep) distances[inds] = dists[1:length(inds)] distances[inds_] = -dists[(length(inds) + 1):end] else - sp = -inc/2*(length(inds)-1) - ep = sp + inc*(length(inds)-1) + sp = -inc / 2 * (length(inds) - 1) + ep = sp + inc * (length(inds) - 1) distances[inds] = collect(sp:inc:ep) end end - distances + return distances end # Convert a species to a string label, handling array species properly. @@ -175,18 +175,24 @@ For a list of accepted keyword arguments to the graph plot, please see the [Grap function Catalyst.plot_network(rn::ReactionSystem; kwargs...) srg = SRGraphWrap(rn) ns = length(species(rn)) - nodecolors = vcat([:skyblue3 for i in 1:ns], - [:green for i in (ns + 1):nv(srg)]) - ilabels = vcat(map(species_label, species(rn)), - ["R$i" for i in 1:(nv(srg) - ns)]) + nodecolors = vcat( + [:skyblue3 for i in 1:ns], + [:green for i in (ns + 1):nv(srg)] + ) + ilabels = vcat( + map(species_label, species(rn)), + ["R$i" for i in 1:(nv(srg) - ns)] + ) ssm = substoichmat(rn) psm = prodstoichmat(rn) # Get stoichiometry of reaction edgelabels = map(Graphs.edges(srg.g)) do e - string(src(e) > ns ? - psm[dst(e), src(e) - ns] : - ssm[src(e), dst(e) - ns]) + string( + src(e) > ns ? + psm[dst(e), src(e) - ns] : + ssm[src(e), dst(e) - ns] + ) end edgecolors = [:black for i in 1:ne(srg)] @@ -206,7 +212,8 @@ function Catalyst.plot_network(rn::ReactionSystem; kwargs...) layout = if !haskey(kwargs, :layout) Stress() end - f = graphplot(srg; + f = graphplot( + srg; layout, edge_color = edgecolors, elabels = edgelabels, @@ -226,7 +233,7 @@ function Catalyst.plot_network(rn::ReactionSystem; kwargs...) hidespines!(f.axis) f.axis.aspect = DataAspect() - f + return f end """ @@ -263,7 +270,8 @@ function Catalyst.plot_complexes(rn::ReactionSystem; show_rate_labels::Bool = fa Stress() end - f = graphplot(cg; + f = graphplot( + cg; layout, edge_color = edgecolors, elabels = edgelabels, @@ -281,7 +289,7 @@ function Catalyst.plot_complexes(rn::ReactionSystem; show_rate_labels::Bool = fa hidespines!(f.axis) f.axis.aspect = DataAspect() - f + return f end function complexelem_tostr(e::Catalyst.ReactionComplexElement, specstrs) @@ -310,5 +318,5 @@ function complexlabels(rn::ReactionSystem) push!(labels, str) end end - labels + return labels end diff --git a/ext/CatalystHomotopyContinuationExtension/homotopy_continuation_extension.jl b/ext/CatalystHomotopyContinuationExtension/homotopy_continuation_extension.jl index 9e15686fc9..004229ad0f 100644 --- a/ext/CatalystHomotopyContinuationExtension/homotopy_continuation_extension.jl +++ b/ext/CatalystHomotopyContinuationExtension/homotopy_continuation_extension.jl @@ -35,8 +35,10 @@ Notes: - Homotopy-based steady state finding only works when all rates are rational polynomials (e.g. constant, linear, mm, or hill functions). ``` """ -function Catalyst.hc_steady_states(rs::ReactionSystem, ps; filter_negative = true, - neg_thres = -1e-15, u0 = [], kwargs...) +function Catalyst.hc_steady_states( + rs::ReactionSystem, ps; filter_negative = true, + neg_thres = -1.0e-15, u0 = [], kwargs... + ) if !isautonomous(rs) error("Attempting to compute steady state for a non-autonomous system (e.g. where some rate depend on $(get_iv(rs))). This is not possible.") end @@ -55,20 +57,20 @@ function steady_state_polynomial(rs::ReactionSystem, ps, u0) pre_varmap = merge(Dict(Catalyst.symmap_to_varmap(rs, u0)), Dict(Catalyst.symmap_to_varmap(rs, ps))) Catalyst.conservationlaw_errorcheck(rs, pre_varmap) for u in setdiff(unknowns(rs), Catalyst.conslaw_species(rs)) - haskey(pre_varmap, u) || (pre_varmap[u] = -1.0) + haskey(pre_varmap, u) || (pre_varmap[u] = -1.0) end nprob_dummy = NonlinearProblem(ns, pre_varmap) p_dict_pre = Dict(p => nprob_dummy.ps[p] for p in parameters(ns)) # Expands vector variable stored in the dictionary so they can be substituted into the polynomial (e.g. from `Γ` to `Γ[1]` and `Γ[2]`) p_dict = typeof(p_dict_pre)() - for (k,v) in p_dict_pre + for (k, v) in p_dict_pre if v isa AbstractArray - for i = 1:length(v) + for i in 1:length(v) p_dict[k[i]] = v[i] end else - p_dict[k] = v + p_dict[k] = v end end @@ -106,11 +108,11 @@ end # Parses and expression and return a version where any exponents that are Float64 (but an int, like 2.0) are turned into Int64s. function make_int_exps(expr) - wrap(Rewriters.Postwalk(Rewriters.PassThrough(___make_int_exps))(unwrap(expr))).val + return wrap(Rewriters.Postwalk(Rewriters.PassThrough(___make_int_exps))(unwrap(expr))).val end function ___make_int_exps(expr) !iscall(expr) && return expr - if (operation(expr) == ^) + return if (operation(expr) == ^) if _isinteger(sorted_arguments(expr)[2]) return sorted_arguments(expr)[1]^Int64(Symbolics.value(sorted_arguments(expr)[2])) else @@ -179,11 +181,11 @@ function reorder_sols!(sols, ss_poly, rs::ReactionSystem) var_names_extended = String.(Symbol.(HC.variables(ss_poly))) var_names = [Symbol(s[1:prevind(s, findlast('_', s))]) for s in var_names_extended] sort_pattern = indexin(MT.getname.(unknowns(rs)), var_names) - foreach(sol -> permute!(sol, sort_pattern), sols) + return foreach(sol -> permute!(sol, sort_pattern), sols) end # Filters away solutions with negative species concentrations (and for neg_thres < val < 0.0, sets val=0.0). -function filter_negative_f(sols; neg_thres = -1e-15) +function filter_negative_f(sols; neg_thres = -1.0e-15) for sol in sols, idx in 1:length(sol) (neg_thres < sol[idx] < 0) && (sol[idx] = 0) @@ -193,29 +195,36 @@ end # Sometimes (when polynomials are created from coupled CRN/DAEs), the steady state polynomial have the wrong type. # This converts it to the correct type, which homotopy continuation can handle. -const WRONG_POLY_TYPE = Vector{DynamicPolynomials.Polynomial{ - DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, - DynamicPolynomials.Graded{DynamicPolynomials.LexOrder}}} -const CORRECT_POLY_TYPE = Vector{DynamicPolynomials.Polynomial{ - DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, - DynamicPolynomials.Graded{DynamicPolynomials.LexOrder}, Float64}} +const WRONG_POLY_TYPE = Vector{ + DynamicPolynomials.Polynomial{ + DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, + DynamicPolynomials.Graded{DynamicPolynomials.LexOrder}, + }, +} +const CORRECT_POLY_TYPE = Vector{ + DynamicPolynomials.Polynomial{ + DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, + DynamicPolynomials.Graded{DynamicPolynomials.LexOrder}, Float64, + }, +} function poly_type_convert(ss_poly) (typeof(ss_poly) == WRONG_POLY_TYPE) && return convert(CORRECT_POLY_TYPE, ss_poly) return ss_poly end - ### SAVED ARCHIVED MTK FUNCTION - REMOVE SOME TIME ### # pre-v10 version of function -function varmap_to_vars_mtkv9(varmap, varlist; defaults = Dict(), check = true, +function varmap_to_vars_mtkv9( + varmap, varlist; defaults = Dict(), check = true, toterm = MT.default_toterm, promotetoconcrete = nothing, - tofloat = true, use_union = true) + tofloat = true, use_union = true + ) varlist = collect(map(unwrap, varlist)) # Edge cases where one of the arguments is effectively empty. is_incomplete_initialization = varmap isa DiffEqBase.NullParameters || - varmap === nothing + varmap === nothing if is_incomplete_initialization || isempty(varmap) if isempty(defaults) if !is_incomplete_initialization && check @@ -238,8 +247,10 @@ function varmap_to_vars_mtkv9(varmap, varlist; defaults = Dict(), check = true, vals = if eltype(varmap) <: Pair # `varmap` is a dict or an array of pairs varmap = MT.todict(varmap) - _varmap_to_vars_mtkv9(varmap, varlist; defaults = defaults, check = check, - toterm = toterm) + _varmap_to_vars_mtkv9( + varmap, varlist; defaults = defaults, check = check, + toterm = toterm + ) else # plain array-like initialization varmap end @@ -254,13 +265,17 @@ function varmap_to_vars_mtkv9(varmap, varlist; defaults = Dict(), check = true, elseif container_type <: Tuple (vals...,) else - SymbolicUtils.Code.create_array(container_type, eltype(vals), Val{1}(), - Val(length(vals)), vals...) + SymbolicUtils.Code.create_array( + container_type, eltype(vals), Val{1}(), + Val(length(vals)), vals... + ) end end -function _varmap_to_vars_mtkv9(varmap::Dict, varlist; defaults = Dict(), check = false, - toterm = Symbolics.diff2term, initialization_phase = false) +function _varmap_to_vars_mtkv9( + varmap::Dict, varlist; defaults = Dict(), check = false, + toterm = Symbolics.diff2term, initialization_phase = false + ) varmap = canonicalize_varmap_mtkv9(varmap; toterm) defaults = canonicalize_varmap_mtkv9(defaults; toterm) varmap = merge(defaults, varmap) diff --git a/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl b/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl index 24bdb7efd1..530bc262f4 100644 --- a/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl +++ b/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl @@ -26,13 +26,17 @@ Notes: - This function is part of the StructuralIdentifiability.jl extension. StructuralIdentifiability.jl must be imported to access it. - `measured_quantities` and `known_p` input may also be symbolic (e.g. measured_quantities = [rs.X]) """ -function Catalyst.make_si_ode(rs::ReactionSystem; measured_quantities = [], known_p = [], - ignore_no_measured_warn = false, remove_conserved = true) +function Catalyst.make_si_ode( + rs::ReactionSystem; measured_quantities = [], known_p = [], + ignore_no_measured_warn = false, remove_conserved = true + ) # Creates a MTK ODE System, and a list of measured quantities (there are equations). # Gives these to SI to create an SI ode model of its preferred form. osys, conseqs, _, _ = make_osys(rs; remove_conserved) - measured_quantities = make_measured_quantities(rs, measured_quantities, known_p, - conseqs; ignore_no_measured_warn) + measured_quantities = make_measured_quantities( + rs, measured_quantities, known_p, + conseqs; ignore_no_measured_warn + ) return SI.mtk_to_si(osys, measured_quantities)[1] end @@ -63,18 +67,24 @@ Notes: - This function is part of the StructuralIdentifiability.jl extension. StructuralIdentifiability.jl must be imported to access it. - `measured_quantities` and `known_p` input may also be symbolic (e.g. measured_quantities = [rs.X]) """ -function SI.assess_local_identifiability(rs::ReactionSystem, args...; +function SI.assess_local_identifiability( + rs::ReactionSystem, args...; measured_quantities = [], known_p = [], funcs_to_check = Vector(), - remove_conserved = true, ignore_no_measured_warn = false, kwargs...) + remove_conserved = true, ignore_no_measured_warn = false, kwargs... + ) # Creates an ODE System, list of measured quantities, and functions to check, of SI's preferred form. osys, conseqs, consconsts, vars = make_osys(rs; remove_conserved) - measured_quantities = make_measured_quantities(rs, measured_quantities, known_p, - conseqs; ignore_no_measured_warn) + measured_quantities = make_measured_quantities( + rs, measured_quantities, known_p, + conseqs; ignore_no_measured_warn + ) funcs_to_check = make_ftc(funcs_to_check, conseqs, vars) # Computes identifiability and converts it to a easy to read form. - out = SI.assess_local_identifiability(osys, args...; measured_quantities, - funcs_to_check, kwargs...) + out = SI.assess_local_identifiability( + osys, args...; measured_quantities, + funcs_to_check, kwargs... + ) return make_output(out, funcs_to_check, consconsts) end @@ -103,20 +113,26 @@ Notes: - This function is part of the StructuralIdentifiability.jl extension. StructuralIdentifiability.jl must be imported to access it. - `measured_quantities` and `known_p` input may also be symbolic (e.g. measured_quantities = [rs.X]) """ -function SI.assess_identifiability(rs::ReactionSystem, args...; +function SI.assess_identifiability( + rs::ReactionSystem, args...; measured_quantities = [], known_p = [], funcs_to_check = Vector(), - remove_conserved = true, ignore_no_measured_warn = false, kwargs...) + remove_conserved = true, ignore_no_measured_warn = false, kwargs... + ) # Creates an ODE System, list of measured quantities, and functions to check, of SI's preferred form. osys, conseqs, consconsts, vars = make_osys(rs; remove_conserved) - measured_quantities = make_measured_quantities(rs, measured_quantities, known_p, - conseqs; ignore_no_measured_warn) + measured_quantities = make_measured_quantities( + rs, measured_quantities, known_p, + conseqs; ignore_no_measured_warn + ) funcs_to_check = make_ftc(funcs_to_check, conseqs, vars) # Computes identifiability and converts it to a easy to read form. # The `::System` designation fixes: https://github.com/SciML/StructuralIdentifiability.jl/issues/360, # however, the exact mechanisms of this is still not fully clear. - out = SI.assess_identifiability(osys::System, args...; measured_quantities, - funcs_to_check, kwargs...) + out = SI.assess_identifiability( + osys::System, args...; measured_quantities, + funcs_to_check, kwargs... + ) return make_output(out, funcs_to_check, consconsts) end @@ -145,13 +161,17 @@ Notes: - This function is part of the StructuralIdentifiability.jl extension. StructuralIdentifiability.jl must be imported to access it. - `measured_quantities` and `known_p` input may also be symbolic (e.g. measured_quantities = [rs.X]) """ -function SI.find_identifiable_functions(rs::ReactionSystem, args...; +function SI.find_identifiable_functions( + rs::ReactionSystem, args...; measured_quantities = [], known_p = [], remove_conserved = true, - ignore_no_measured_warn = false, kwargs...) + ignore_no_measured_warn = false, kwargs... + ) # Creates an ODE System, and list of measured quantities, of SI's preferred form. osys, conseqs, consconsts, _ = make_osys(rs; remove_conserved) - measured_quantities = make_measured_quantities(rs, measured_quantities, known_p, - conseqs; ignore_no_measured_warn) + measured_quantities = make_measured_quantities( + rs, measured_quantities, known_p, + conseqs; ignore_no_measured_warn + ) # Computes identifiable functions and converts it to a easy to read form. out = SI.find_identifiable_functions(osys, args...; measured_quantities, kwargs...) @@ -190,8 +210,10 @@ end # Creates a list of measured quantities of a form that SI can read. # Each measured quantity must have a form like: # `obs_var ~ X` # (Here, `obs_var` is a variable, and X is whatever we can measure). -function make_measured_quantities(rs::ReactionSystem, measured_quantities::Vector{T}, - known_p::Vector{S}, conseqs; ignore_no_measured_warn = false) where {T, S} +function make_measured_quantities( + rs::ReactionSystem, measured_quantities::Vector{T}, + known_p::Vector{S}, conseqs; ignore_no_measured_warn = false + ) where {T, S} # Warning if the user didn't give any measured quantities. if !ignore_no_measured_warn && isempty(measured_quantities) @warn "No measured quantity provided to the `measured_quantities` argument, any further identifiability analysis will likely fail. You can disable this warning by setting `ignore_no_measured_warn = true`." @@ -205,8 +227,10 @@ function make_measured_quantities(rs::ReactionSystem, measured_quantities::Vecto # Creates one internal observation variable for each measured quantity (`___internal_observables`). # Creates a vector of equations, setting each measured quantity equal to one observation variable. @variables (___internal_observables(Catalyst.get_iv(rs)))[1:length(mqs)] - return Equation[(q isa Equation) ? q : (___internal_observables[i] ~ q) - for (i, q) in enumerate(mqs)] + return Equation[ + (q isa Equation) ? q : (___internal_observables[i] ~ q) + for (i, q) in enumerate(mqs) + ] end # Creates the functions that we wish to check for identifiability. diff --git a/src/Catalyst.jl b/src/Catalyst.jl index 2339490f58..e91211d7bb 100644 --- a/src/Catalyst.jl +++ b/src/Catalyst.jl @@ -9,8 +9,8 @@ import SciMLBase using LaTeXStrings, Latexify using LinearAlgebra, Combinatorics using JumpProcesses: JumpProcesses, JumpProblem, - MassActionJump, ConstantRateJump, VariableRateJump, - SpatialMassActionJump, CartesianGrid, CartesianGridRej + MassActionJump, ConstantRateJump, VariableRateJump, + SpatialMassActionJump, CartesianGrid, CartesianGridRej # ModelingToolkit imports and convenience functions we use using ModelingToolkitBase @@ -26,14 +26,14 @@ RuntimeGeneratedFunctions.init(@__MODULE__) import Symbolics: SymbolicT using Symbolics: iscall, sorted_arguments, value using ModelingToolkitBase: get_unknowns, get_ps, get_iv, get_systems, - get_eqs, toparam, get_var_to_name, get_observed, - getvar, has_iv, JumpType + get_eqs, toparam, get_var_to_name, get_observed, + getvar, has_iv, JumpType import ModelingToolkitBase: get_variables, namespace_expr, namespace_equation, - modified_unknowns!, namespace_variables, - namespace_parameters, renamespace, flatten, - is_alg_equation, is_diff_equation, collect_vars!, - eqtype_supports_collect_vars + modified_unknowns!, namespace_variables, + namespace_parameters, renamespace, flatten, + is_alg_equation, is_diff_equation, collect_vars!, + eqtype_supports_collect_vars # Import from owner modules (not re-exporters) per ExplicitImports.jl audit import Symbolics: get_variables!, rename @@ -43,7 +43,7 @@ import ModelingToolkitBase: SymmapT # internal but needed ModelingToolkit functions import ModelingToolkitBase: check_variables, check_parameters, - check_equations, iscomplete + check_equations, iscomplete # Import from owner module (SymbolicUtils) per ExplicitImports.jl audit import SymbolicUtils: _iszero, unwrap @@ -81,8 +81,10 @@ const CONSERVED_CONSTANT_SYMBOL = :Γ # Declares symbols which may neither be used as parameters nor unknowns. const forbidden_symbols_skip = Set([:ℯ, :pi, :π, :t, :∅, :Ø]) -const forbidden_symbols_error = union(Set([:im, :nothing, CONSERVED_CONSTANT_SYMBOL]), - forbidden_symbols_skip) +const forbidden_symbols_error = union( + Set([:im, :nothing, CONSERVED_CONSTANT_SYMBOL]), + forbidden_symbols_skip +) ### Unit Helpers ### @@ -129,7 +131,7 @@ include("reactionsystem_metadata.jl") # Conversions of the `ReactionSystem` structure. include("reactionsystem_conversions.jl") export ODEProblem, SDEProblem, JumpProblem, NonlinearProblem, - SteadyStateProblem, HybridProblem + SteadyStateProblem, HybridProblem export ismassaction, oderatelaw, jumpratelaw # reaction_network macro @@ -141,15 +143,15 @@ export @reaction_network, @network_component, @reaction, @species include("network_analysis.jl") export reactioncomplexmap, reactioncomplexes, incidencemat export complexstoichmat, laplacianmat, fluxmat, massactionvector, complexoutgoingmat, - adjacencymat + adjacencymat export incidencematgraph, linkageclasses, stronglinkageclasses, - terminallinkageclasses, deficiency, subnetworks + terminallinkageclasses, deficiency, subnetworks export linkagedeficiencies, isreversible, isweaklyreversible export conservationlaws, conservedquantities, conservedequations, conservationlaw_constants export satisfiesdeficiencyone, satisfiesdeficiencyzero export iscomplexbalanced, isdetailedbalanced, robustspecies -# Containes the `nullspace` function required for conservation law elimination. +# Contains the `nullspace` function required for conservation law elimination. include("mtk_nullspace_function.jl") # registers CRN specific functions using Symbolics.jl @@ -202,7 +204,7 @@ export DiscreteSpaceReactionSystem export spatial_species, vertex_parameters, edge_parameters export CartesianGrid, CartesianGridReJ # (Implemented in JumpProcesses) export has_cartesian_dspace, has_masked_dspace, has_grid_dspace, has_graph_dspace, - grid_dims, grid_size + grid_dims, grid_size export make_edge_p_values, make_directed_edge_values # Specific spatial problem types. diff --git a/src/chemistry_functionality.jl b/src/chemistry_functionality.jl index 6ec8c76450..1ba1c640fd 100644 --- a/src/chemistry_functionality.jl +++ b/src/chemistry_functionality.jl @@ -16,7 +16,7 @@ Returns `true` if the input is a compound species (else false). """ iscompound(s::Num) = iscompound(value(s)) function iscompound(s) - MT.getmetadata(s, CompoundSpecies, false) + return MT.getmetadata(s, CompoundSpecies, false) end """ @@ -26,7 +26,7 @@ Returns a vector with a list of all the components of a compound species (create """ components(s::Num) = components(value(s)) function components(s) - MT.getmetadata(s, CompoundComponents) + return MT.getmetadata(s, CompoundComponents) end """ @@ -36,7 +36,7 @@ Returns a vector with a list of all the stoichiometric coefficients of the compo """ coefficients(s::Num) = coefficients(value(s)) function coefficients(s) - MT.getmetadata(s, CompoundCoefficients) + return MT.getmetadata(s, CompoundCoefficients) end """ @@ -67,7 +67,7 @@ Notes: - The component species must be defined before using the `@compound` macro. """ macro compound(expr) - make_compound(striplines(expr)) + return make_compound(striplines(expr)) end # Declares compound error messages: @@ -85,8 +85,10 @@ function make_compound(expr) # Loops through all components, add the component and the coefficients to the corresponding vectors # Cannot extract directly using e.g. "getfield.(composition, :reactant)" because then # we get something like :([:C, :O]), rather than :([C, O]). - composition = Catalyst.recursive_find_reactants!(expr.args[3], 1, - Vector{DSLReactant}(undef, 0)) + composition = Catalyst.recursive_find_reactants!( + expr.args[3], 1, + Vector{DSLReactant}(undef, 0) + ) components = :([]) # Becomes something like :([C, O]). coefficients = :([]) # Becomes something like :([1, 2]). for comp in composition @@ -103,9 +105,16 @@ function make_compound(expr) # If no ivs were given, inserts an expression which evaluates to the union of the ivs # for the species the compound depends on. - ivs_get_expr = :(unique(reduce( - vcat, (Symbolics.sorted_arguments(Symbolics.unwrap(comp)) - for comp in $components)))) + ivs_get_expr = :( + unique( + reduce( + vcat, ( + Symbolics.sorted_arguments(Symbolics.unwrap(comp)) + for comp in $components + ) + ) + ) + ) if isempty(ivs) species_expr = Catalyst.insert_independent_variable(species_expr, :($ivs_get_expr...)) end @@ -122,23 +131,48 @@ function make_compound(expr) # `CO2 = ModelingToolkitBase.setmetadata(CO2, Catalyst.CompoundSpecies, [1, 2])` # `CO2 = ModelingToolkitBase.wrap(CO2)` species_declaration_expr = Expr(:escape, :(@species $species_expr)) - multiple_ivs_error_check_expr = Expr(:escape, - :($(isempty(ivs)) && (length($ivs_get_expr) > 1) && - error($COMPOUND_CREATION_ERROR_DEPENDENT_VAR_REQUIRED))) - iv_check_expr = Expr(:escape, - :(issetequal(Symbolics.arguments(Symbolics.unwrap($species_name)), $ivs_get_expr) || - error("The independent variable(S) provided to the compound ($(Symbolics.arguments(Symbolics.unwrap($species_name)))), and those of its components ($($ivs_get_expr)))), are not identical."))) - compound_designation_expr = Expr(:escape, - :($species_name = ModelingToolkitBase.setmetadata( - $species_name, Catalyst.CompoundSpecies, true))) - components_designation_expr = Expr(:escape, - :($species_name = ModelingToolkitBase.setmetadata( - $species_name, Catalyst.CompoundComponents, $components))) - coefficients_designation_expr = Expr(:escape, - :($species_name = ModelingToolkitBase.setmetadata( - $species_name, Catalyst.CompoundCoefficients, $coefficients))) - compound_wrap_expr = Expr(:escape, - :($species_name = ModelingToolkitBase.wrap($species_name))) + multiple_ivs_error_check_expr = Expr( + :escape, + :( + $(isempty(ivs)) && (length($ivs_get_expr) > 1) && + error($COMPOUND_CREATION_ERROR_DEPENDENT_VAR_REQUIRED) + ) + ) + iv_check_expr = Expr( + :escape, + :( + issetequal(Symbolics.arguments(Symbolics.unwrap($species_name)), $ivs_get_expr) || + error("The independent variable(S) provided to the compound ($(Symbolics.arguments(Symbolics.unwrap($species_name)))), and those of its components ($($ivs_get_expr)))), are not identical.") + ) + ) + compound_designation_expr = Expr( + :escape, + :( + $species_name = ModelingToolkitBase.setmetadata( + $species_name, Catalyst.CompoundSpecies, true + ) + ) + ) + components_designation_expr = Expr( + :escape, + :( + $species_name = ModelingToolkitBase.setmetadata( + $species_name, Catalyst.CompoundComponents, $components + ) + ) + ) + coefficients_designation_expr = Expr( + :escape, + :( + $species_name = ModelingToolkitBase.setmetadata( + $species_name, Catalyst.CompoundCoefficients, $coefficients + ) + ) + ) + compound_wrap_expr = Expr( + :escape, + :($species_name = ModelingToolkitBase.wrap($species_name)) + ) # Returns the rephrased expression. return quote @@ -173,7 +207,7 @@ Notes: - The component species must be defined before using the `@compound` macro. """ macro compounds(expr) - make_compounds(striplines(expr)) + return make_compounds(striplines(expr)) end # Function managing the @compound macro. @@ -199,8 +233,12 @@ function make_compounds(expr) push!(compound_declarations.args, :($(Expr(:escape, :($(compound_syms)))))) # The output needs to be converted to Vector{Num} (from Vector{SymbolicUtils.SymbolicT}) to be consistent with e.g. @variables. - compound_declarations.args[end] = :([ModelingToolkitBase.wrap(cmp) - for cmp in $(compound_declarations.args[end])]) + compound_declarations.args[end] = :( + [ + ModelingToolkitBase.wrap(cmp) + for cmp in $(compound_declarations.args[end]) + ] + ) # Returns output that. return compound_declarations @@ -267,8 +305,10 @@ function balance_reaction(reaction::Reaction) prodstoich = stoich[(length(reaction.substrates) + 1):end] # Create a new reaction with the balanced stoichiometries - balancedrx = Reaction(reaction.rate, reaction.substrates, reaction.products, - substoich, prodstoich) + balancedrx = Reaction( + reaction.rate, reaction.substrates, reaction.products, + substoich, prodstoich + ) # Add the reaction to the vector of all reactions balancedrxs[i] = balancedrx diff --git a/src/dsl.jl b/src/dsl.jl index 88a2c8b6c3..1c4b2708f4 100644 --- a/src/dsl.jl +++ b/src/dsl.jl @@ -8,9 +8,11 @@ const double_arrows = Set{Symbol}([:↔, :⟷, :⇄, :⇆, :⇌, :⇋, :⇔, : const pure_rate_arrows = Set{Symbol}([:(=>), :(<=), :⇐, :⟽, :⇒, :⟾, :⇔, :⟺]) # Declares the keys used for various options. -const option_keys = (:species, :parameters, :variables, :discretes, :ivs, :compounds, :observables, +const option_keys = ( + :species, :parameters, :variables, :discretes, :ivs, :compounds, :observables, :default_noise_scaling, :differentials, :equations, :continuous_events, :discrete_events, - :tstops, :brownians, :poissonians, :combinatoric_ratelaws, :require_declaration, :unit_checks) + :tstops, :brownians, :poissonians, :combinatoric_ratelaws, :require_declaration, :unit_checks, +) ### `@species` Macro ### @@ -63,17 +65,17 @@ of observables). Each option is designated by a tag starting with a `@` followed A list of options can be found [here](https://docs.sciml.ai/Catalyst/stable/api/#api_dsl_options). """ macro reaction_network(name::Symbol, network_expr::Expr) - make_rs_expr(QuoteNode(name), network_expr) + return make_rs_expr(QuoteNode(name), network_expr) end # The case where the name contains an interpolation. macro reaction_network(name::Expr, network_expr::Expr) - make_rs_expr(esc(name.args[1]), network_expr) + return make_rs_expr(esc(name.args[1]), network_expr) end # The case where nothing, or only a name, is provided. macro reaction_network(name::Symbol = gensym(:ReactionSystem)) - make_rs_expr(QuoteNode(name)) + return make_rs_expr(QuoteNode(name)) end # Handles two disjoint cases. @@ -91,17 +93,17 @@ Equivalent to `@reaction_network` except the generated `ReactionSystem` is not m complete. """ macro network_component(name::Symbol, network_expr::Expr) - make_rs_expr(QuoteNode(name), network_expr; complete = false) + return make_rs_expr(QuoteNode(name), network_expr; complete = false) end # The case where the name contains an interpolation. macro network_component(name::Expr, network_expr::Expr) - make_rs_expr(esc(name.args[1]), network_expr; complete = false) + return make_rs_expr(esc(name.args[1]), network_expr; complete = false) end # The case where nothing, or only a name, is provided. macro network_component(name::Symbol = gensym(:ReactionSystem)) - make_rs_expr(QuoteNode(name); complete = false) + return make_rs_expr(QuoteNode(name); complete = false) end # Handles two disjoint cases. @@ -146,12 +148,14 @@ struct DSLReaction metadata::Expr rxexpr::Expr - function DSLReaction(sub_line::ExprValues, prod_line::ExprValues, - rate::ExprValues, metadata_line::ExprValues, rx_line::Expr) + function DSLReaction( + sub_line::ExprValues, prod_line::ExprValues, + rate::ExprValues, metadata_line::ExprValues, rx_line::Expr + ) subs = recursive_find_reactants!(sub_line, 1, Vector{DSLReactant}(undef, 0)) prods = recursive_find_reactants!(prod_line, 1, Vector{DSLReactant}(undef, 0)) metadata = extract_metadata(metadata_line) - new(subs, prods, rate, metadata, rx_line) + return new(subs, prods, rate, metadata, rx_line) end end @@ -159,8 +163,10 @@ end # stoichiometry. Recursion makes it able to handle weird cases like 2(X + Y + 3(Z + XY)). The # reactants are stored in the `reactants` vector. As the expression tree is parsed, the # stoichiometry is updated and new reactants are added. -function recursive_find_reactants!(ex::ExprValues, mult::ExprValues, - reactants::Vector{DSLReactant}) +function recursive_find_reactants!( + ex::ExprValues, mult::ExprValues, + reactants::Vector{DSLReactant} + ) # We have reached the end of the expression tree and can finalise and return the reactants. if (typeof(ex) != Expr) || (ex.head == :escape) || (ex.head == :ref) # The final bit of the expression is not a relevant reactant, no additions are required. @@ -196,13 +202,13 @@ function recursive_find_reactants!(ex::ExprValues, mult::ExprValues, else throw("Malformed reaction, bad operator: $(ex.args[1]) found in stoichiometry expression $ex.") end - reactants + return reactants end # Helper function for updating the multiplicity throughout recursion (handles e.g. parametric # stoichiometries). The `op` argument is an operation (e.g. `*`, but could also e.g. be `+`). function processmult(op, mult, stoich) - if (mult isa Number) && (stoich isa Number) + return if (mult isa Number) && (stoich isa Number) op(mult, stoich) else :($op($mult, $stoich)) @@ -230,7 +236,7 @@ end function Base.showerror(io::IO, err::UndeclaredSymbolicError) print(io, "UndeclaredSymbolicError: ") - print(io, err.msg) + return print(io, err.msg) end ### DSL Internal Master Function ### @@ -250,7 +256,7 @@ function make_reaction_system(ex::Expr, name) error("@reaction_network input contain $(length(ex.args) - numlines) malformed lines.") options = Dict(Symbol(String(arg.args[1])[2:end]) => arg for arg in option_lines) any(!in(option_keys), keys(options)) && - error("The following unsupported options were used: $(filter(opt_in->!in(opt_in,option_keys), keys(options)))") + error("The following unsupported options were used: $(filter(opt_in -> !in(opt_in, option_keys), keys(options)))") # Read options that explicitly declare some symbol (e.g. `@species`). Compiles a list of # all declared symbols and checks that there have been no double-declarations. @@ -263,8 +269,14 @@ function make_reaction_system(ex::Expr, name) diffsexpr, diffs_declared = read_differentials_option(options) brownsexpr_init, browns_declared = read_brownians_option(options) poissexpr_init, poiss_declared = read_poissonians_option(options) - syms_declared = collect(Iterators.flatten((cmps_declared, sps_declared, ps_declared, - vs_declared, discs_declared, ivs, diffs_declared, browns_declared, poiss_declared))) + syms_declared = collect( + Iterators.flatten( + ( + cmps_declared, sps_declared, ps_declared, + vs_declared, discs_declared, ivs, diffs_declared, browns_declared, poiss_declared, + ) + ) + ) if !allunique(syms_declared) nonunique_syms = [s for s in syms_declared if count(x -> x == s, syms_declared) > 1] error("The following symbols $(unique(nonunique_syms)) have explicitly been declared as multiple types of components (e.g. occur in at least two of the `@species`, `@parameters`, `@variables`, `@ivs`, `@compounds`, `@differentials`). This is not allowed.") @@ -274,14 +286,18 @@ function make_reaction_system(ex::Expr, name) requiredec = haskey(options, :require_declaration) reactions = get_reactions(reaction_lines) sps_inferred, ps_pre_inferred, stoich_ps = extract_sps_and_ps(reactions, syms_declared; requiredec) - vs_inferred, diffs_inferred, equations = read_equations_option!(diffsexpr, options, - union(syms_declared, sps_inferred), tiv; requiredec) + vs_inferred, diffs_inferred, equations = read_equations_option!( + diffsexpr, options, + union(syms_declared, sps_inferred), tiv; requiredec + ) ps_inferred = setdiff(ps_pre_inferred, vs_inferred, diffs_inferred) syms_inferred = union(sps_inferred, ps_inferred, vs_inferred, diffs_inferred) all_syms = union(syms_declared, syms_inferred) validate_poissonian_rate_syms(options, all_syms) - obsexpr, obs_eqs, obs_syms = read_observables_option(options, ivs, - union(sps_declared, vs_declared), all_syms; requiredec) + obsexpr, obs_eqs, obs_syms = read_observables_option( + options, ivs, + union(sps_declared, vs_declared), all_syms; requiredec + ) # Read options not related to the declaration or inference of symbols. discs_inferred = Vector{Symbol}() @@ -308,41 +324,48 @@ function make_reaction_system(ex::Expr, name) # Assemblies the full expression that declares all required symbolic variables, and # then the output `ReactionSystem`. - MacroTools.flatten(striplines(quote - # Inserts the expressions which generate the `ReactionSystem` input. - $ivsexpr - $psexpr - $vsexpr - $spsexpr - $discsexpr - $obsexpr - $cmpsexpr - $diffsexpr - $brownsexpr - $poissexpr - - # Stores each kwarg in a variable. Not necessary, but useful when debugging generated code. - name = $name - spatial_ivs = $sivs - _unit_checks = $unit_checks - rx_eq_vec = $rxsexprs - us = setdiff(union($spsvar, $vsvar, $cmpsvar), $obs_syms) - ps = union($psvar, $discsvar) - _observed = $obs_eqs - _continuous_events = $continuous_events_expr - _discrete_events = $discrete_events_expr - _tstops = $tstops_expr - _combinatoric_ratelaws = $combinatoric_ratelaws - _default_reaction_metadata = $default_reaction_metadata - - remake_ReactionSystem_internal( - make_ReactionSystem_internal(rx_eq_vec, $tiv, us, ps, $brownsvar; poissonians = $poissvar, - name, spatial_ivs, observed = _observed, continuous_events = _continuous_events, - discrete_events = _discrete_events, tstops = _tstops, - combinatoric_ratelaws = _combinatoric_ratelaws, - unit_checks = _unit_checks); - default_reaction_metadata = _default_reaction_metadata) - end)) + return MacroTools.flatten( + striplines( + quote + # Inserts the expressions which generate the `ReactionSystem` input. + $ivsexpr + $psexpr + $vsexpr + $spsexpr + $discsexpr + $obsexpr + $cmpsexpr + $diffsexpr + $brownsexpr + $poissexpr + + # Stores each kwarg in a variable. Not necessary, but useful when debugging generated code. + name = $name + spatial_ivs = $sivs + _unit_checks = $unit_checks + rx_eq_vec = $rxsexprs + us = setdiff(union($spsvar, $vsvar, $cmpsvar), $obs_syms) + ps = union($psvar, $discsvar) + _observed = $obs_eqs + _continuous_events = $continuous_events_expr + _discrete_events = $discrete_events_expr + _tstops = $tstops_expr + _combinatoric_ratelaws = $combinatoric_ratelaws + _default_reaction_metadata = $default_reaction_metadata + + remake_ReactionSystem_internal( + make_ReactionSystem_internal( + rx_eq_vec, $tiv, us, ps, $brownsvar; poissonians = $poissvar, + name, spatial_ivs, observed = _observed, continuous_events = _continuous_events, + discrete_events = _discrete_events, tstops = _tstops, + combinatoric_ratelaws = _combinatoric_ratelaws, + unit_checks = _unit_checks + ); + default_reaction_metadata = _default_reaction_metadata + ) + end + ) + ) end ### DSL Reaction Reading Functions ### @@ -360,7 +383,7 @@ function get_reactions(exprs::Vector{Expr}) # Currently, reaction bundling where rates (but neither substrates nor products) are # bundled, is disabled. See discussion in https://github.com/SciML/Catalyst.jl/issues/1219. if !in(arrow, double_arrows) && Meta.isexpr(rate, :tuple) && - !Meta.isexpr(reaction.args[2], :tuple) && !Meta.isexpr(reaction.args[3], :tuple) + !Meta.isexpr(reaction.args[2], :tuple) && !Meta.isexpr(reaction.args[3], :tuple) error("Bundling of reactions with multiple rates but singular substrates and product sets is disallowed. This error is potentially due to a bidirectional (`<-->`) reaction being incorrectly typed as `-->`.") end @@ -371,16 +394,24 @@ function get_reactions(exprs::Vector{Expr}) (typeof(metadata) != Expr || metadata.head != :tuple) && error("Error: Must provide a tuple of reaction metadata when declaring a bi-directional reaction.") - push_reactions!(reactions, reaction.args[2], reaction.args[3], - rate.args[1], metadata.args[1], arrow, line) - push_reactions!(reactions, reaction.args[3], reaction.args[2], - rate.args[2], metadata.args[2], arrow, line) + push_reactions!( + reactions, reaction.args[2], reaction.args[3], + rate.args[1], metadata.args[1], arrow, line + ) + push_reactions!( + reactions, reaction.args[3], reaction.args[2], + rate.args[2], metadata.args[2], arrow, line + ) elseif in(arrow, fwd_arrows) - push_reactions!(reactions, reaction.args[2], reaction.args[3], - rate, metadata, arrow, line) + push_reactions!( + reactions, reaction.args[2], reaction.args[3], + rate, metadata, arrow, line + ) elseif in(arrow, bwd_arrows) - push_reactions!(reactions, reaction.args[3], reaction.args[2], - rate, metadata, arrow, line) + push_reactions!( + reactions, reaction.args[3], reaction.args[2], + rate, metadata, arrow, line + ) else throw("Malformed reaction, invalid arrow type used in: $(striplines(line))") end @@ -413,8 +444,10 @@ end # Takes a reaction line and creates reaction(s) from it and pushes those to the reaction vector. # Used to create multiple reactions from bundled reactions (like `k, (X,Y) --> 0`). -function push_reactions!(reactions::Vector{DSLReaction}, subs::ExprValues, - prods::ExprValues, rate::ExprValues, metadata::ExprValues, arrow::Symbol, line::Expr) +function push_reactions!( + reactions::Vector{DSLReaction}, subs::ExprValues, + prods::ExprValues, rate::ExprValues, metadata::ExprValues, arrow::Symbol, line::Expr + ) # The rates, substrates, products, and metadata may be in a tuple form (e.g. `k, (X,Y) --> 0`). # This finds these tuples' lengths (or 1 for non-tuple forms). Inconsistent lengths yield error. lengs = (tup_leng(subs), tup_leng(prods), tup_leng(rate), tup_leng(metadata)) @@ -440,6 +473,7 @@ function push_reactions!(reactions::Vector{DSLReaction}, subs::ExprValues, subs_i, prods_i, rate_i = get_tup_arg.((subs, prods, rate), i) push!(reactions, DSLReaction(subs_i, prods_i, rate_i, metadata_i, line)) end + return end ### DSL Species and Parameters Extraction ### @@ -461,7 +495,7 @@ end # Function looping through all reactions, to find undeclared symbols (species or # parameters) and assign them to the right category. # `stoich_ps` records parameters used in stoichiometries (if these are not declare separately, -# these are infered to be integers).. +# these are inferred to be integers).. function extract_sps_and_ps(reactions, excluded_syms; requiredec = false) # Loops through all reactants and extract undeclared ones as species. species = OrderedSet{Union{Symbol, Expr}}() @@ -488,7 +522,7 @@ function extract_sps_and_ps(reactions, excluded_syms; requiredec = false) end end - collect(species), collect(parameters), collect(stoich_ps) + return collect(species), collect(parameters), collect(stoich_ps) end # Function called by `extract_sps_and_ps`, recursively loops through an expression and find @@ -506,7 +540,7 @@ function add_syms_from_expr!(push_symbols::AbstractSet, expr::ExprValues, exclud add_syms_from_expr!(push_symbols, expr.args[i], excluded_syms, push_symbols2) end end - nothing + return nothing end ### DSL Output Expression Builders ### @@ -525,7 +559,7 @@ function get_psexpr(parameters_extracted, stoich_ps, options) (pexprs.args[3].args) : (pexprs.args) foreach(p -> push!(arg_vec, p), setdiff(parameters_extracted, stoich_ps)) foreach(p -> push!(arg_vec, :($p::Int64)), stoich_ps) - pexprs + return pexprs end # Given the extracted species (or variables) and the option dictionary, create the @@ -544,7 +578,7 @@ function get_usexpr(us_extracted, options, key = :species; ivs = (DEFAULT_IV_SYM for u in us_extracted u isa Symbol && push!(arg_vec, Expr(:call, u, ivs...)) end - usexpr + return usexpr end # From the system reactions (as `DSLReaction`s) and equations (as expressions), @@ -565,8 +599,12 @@ function get_rxexpr(rx::DSLReaction) subs_stoich_init = deepcopy(subs_init) prod_init = isempty(rx.products) ? nothing : :([]) prod_stoich_init = deepcopy(prod_init) - rx_constructor = :(Reaction($rate, $subs_init, $prod_init, $subs_stoich_init, - $prod_stoich_init; metadata = $(rx.metadata), unit_checks = _unit_checks)) + rx_constructor = :( + Reaction( + $rate, $subs_init, $prod_init, $subs_stoich_init, + $prod_stoich_init; metadata = $(rx.metadata), unit_checks = _unit_checks + ) + ) # Loops through all products and substrates, and adds them (and their stoichiometries) # to the `Reaction` expression. @@ -617,7 +655,7 @@ end function escape_equation!(eqexpr::Expr, all_syms) eqexpr.args[2] = recursive_escape_functions!(eqexpr.args[2], all_syms) eqexpr.args[3] = recursive_escape_functions!(eqexpr.args[3], all_syms) - eqexpr + return eqexpr end ### DSL Option Handling ### @@ -650,8 +688,10 @@ function read_compounds_option(options) if haskey(options, :compounds) cmpexpr_init = options[:compounds] cmpexpr_init.args[3] = option_block_form(get_block_option(cmpexpr_init)) - cmps_declared = [find_varinfo_in_declaration(arg.args[2])[1] - for arg in cmpexpr_init.args[3].args] + cmps_declared = [ + find_varinfo_in_declaration(arg.args[2])[1] + for arg in cmpexpr_init.args[3].args + ] (length(cmps_declared) == 1) && (cmpexpr_init.args[1] = Symbol("@compound")) else # If option is not used, return empty vectors and expressions. cmpexpr_init = :() @@ -666,8 +706,10 @@ function read_differentials_option(options) # Creates the differential expression. # If differentials were provided as options, this is used as the initial expression. # If the default differential (D(...)) was used in equations, this is added to the expression. - diffsexpr = (haskey(options, :differentials) ? - get_block_option(options[:differentials]) : striplines(:(begin end))) + diffsexpr = ( + haskey(options, :differentials) ? + get_block_option(options[:differentials]) : striplines(:(begin end)) + ) diffsexpr = option_block_form(diffsexpr) # Goes through all differentials, checking that they are correctly formatted. Adds their @@ -686,10 +728,10 @@ function read_differentials_option(options) return diffsexpr, diffs_declared end -# Creates the initial expression for declaring brownians. Also extracts any symbols +# Creates the initial expression for declaring brownians. Also extracts any symbols # declared as brownians by the `@brownian` option. function read_brownians_option(options) - browns_declared = extract_syms(options, :brownians) + browns_declared = extract_syms(options, :brownians) brownsexpr_init = haskey(options, :brownians) ? options[:brownians] : :() return brownsexpr_init, browns_declared end @@ -750,7 +792,7 @@ end # Recursively collect bare Symbol names from an expression, skipping escaped nodes. function _collect_symbols!(syms::Vector{Symbol}, ex) - if ex isa Symbol + return if ex isa Symbol push!(syms, ex) elseif ex isa Expr is_escaped_expr(ex) && return @@ -768,11 +810,14 @@ end function validate_poissonian_rate_syms(options, all_syms) poiss_rate_syms = extract_poissonian_rate_syms(options) undeclared = setdiff(poiss_rate_syms, all_syms) - if !isempty(undeclared) - throw(UndeclaredSymbolicError( - "Unrecognized symbol(s) $(join(undeclared, ", ")) in a `@poissonians` rate " * - "expression. Symbols in poissonian rates must be pre-declared (e.g. via " * - "`@parameters`, `@species`, or `@variables`) or interpolated.")) + return if !isempty(undeclared) + throw( + UndeclaredSymbolicError( + "Unrecognized symbol(s) $(join(undeclared, ", ")) in a `@poissonians` rate " * + "expression. Symbols in poissonian rates must be pre-declared (e.g. via " * + "`@parameters`, `@species`, or `@variables`) or interpolated." + ) + ) end end @@ -780,11 +825,12 @@ end # as well as the equation vector. If the default differential was used, update the `diffsexpr` # expression so that this declares this as well. function read_equations_option!( - diffsexpr, options, syms_unavailable, tiv; requiredec = false) + diffsexpr, options, syms_unavailable, tiv; requiredec = false + ) # Prepares the equations. First, extract equations from the provided option (converting to block form if required). # Next, uses `parse_equations!` function to split input into a vector with the equations. eqs_input = haskey(options, :equations) ? get_block_option(options[:equations]) : - MacroTools.striplines(:(begin end)) + MacroTools.striplines(:(begin end)) eqs_input = option_block_form(eqs_input) equations = eqs_input.args @@ -800,16 +846,22 @@ function read_equations_option!( # If the default differential (`D`) is used, record that it should be declared later on. if (:D ∉ syms_unavailable) && find_D_call(eq) - requiredec && throw(UndeclaredSymbolicError( - "Unrecognized symbol D was used as a differential in an equation: \"$eq\". Since the @require_declaration flag is set, all differentials in equations must be explicitly declared using the @differentials option.")) + requiredec && throw( + UndeclaredSymbolicError( + "Unrecognized symbol D was used as a differential in an equation: \"$eq\". Since the @require_declaration flag is set, all differentials in equations must be explicitly declared using the @differentials option." + ) + ) add_default_diff = true push!(syms_unavailable, :D) end # Any undeclared symbolic variables encountered should be extracted as variables. add_syms_from_expr!(vs_inferred, eq, syms_unavailable) - (!isempty(vs_inferred) && requiredec) && throw(UndeclaredSymbolicError( - "Unrecognized symbol $(join(vs_inferred, ", ")) detected in equation expression: \"$(string(eq))\". Since the flag @require_declaration is declared, all symbolic variables must be explicitly declared with the @species, @variables, and @parameters options.")) + (!isempty(vs_inferred) && requiredec) && throw( + UndeclaredSymbolicError( + "Unrecognized symbol $(join(vs_inferred, ", ")) detected in equation expression: \"$(string(eq))\". Since the flag @require_declaration is declared, all symbolic variables must be explicitly declared with the @species, @variables, and @parameters options." + ) + ) end # If `D` differential is used, add it to the differential expression and inferred differentials list. @@ -838,7 +890,8 @@ end # a vector containing the observable equations, and a list of all observable symbols (this # list contains both those declared separately or inferred from the `@observables` option` input`). function read_observables_option( - options, all_ivs, us_declared, all_syms; requiredec = false) + options, all_ivs, us_declared, all_syms; requiredec = false + ) syms_unavailable = setdiff(all_syms, us_declared) if haskey(options, :observables) # Gets list of observable equations and prepares variable declaration expression. @@ -850,7 +903,7 @@ function read_observables_option( for (idx, obs_eq) in enumerate(obs_eqs.args) # Extract the observable, checks for errors. obs_name, ivs, _, defaults, - metadata = find_varinfo_in_declaration(obs_eq.args[2]) + metadata = find_varinfo_in_declaration(obs_eq.args[2]) # Error checks. (requiredec && !in(obs_name, us_declared)) && @@ -875,13 +928,28 @@ function read_observables_option( if !((obs_name in us_declared) || is_escaped_expr(obs_eq.args[2])) # Creates an expression which extracts the ivs of the species & variables the # observable depends on, and splats them out in the correct order. - dep_var_expr = :(filter(!MT.isparameter, - Symbolics.get_variables($(obs_eq.args[3])))) - ivs_get_expr = :(unique(reduce( - vcat, [sorted_arguments(unwrap(dep)) - for dep in $dep_var_expr]))) - ivs_get_expr_sorted = :(sort($(ivs_get_expr); - by = iv -> findfirst(MT.getname(iv) == ivs for ivs in $all_ivs))) + dep_var_expr = :( + filter( + !MT.isparameter, + Symbolics.get_variables($(obs_eq.args[3])) + ) + ) + ivs_get_expr = :( + unique( + reduce( + vcat, [ + sorted_arguments(unwrap(dep)) + for dep in $dep_var_expr + ] + ) + ) + ) + ivs_get_expr_sorted = :( + sort( + $(ivs_get_expr); + by = iv -> findfirst(MT.getname(iv) == ivs for ivs in $all_ivs) + ) + ) obs_expr = insert_independent_variable(obs_eq.args[2], :($ivs_get_expr_sorted...)) push!(obsexpr.args[1].args, obs_expr) @@ -933,7 +1001,7 @@ function expr_contains_pre(expr) end # Read the events (continuous or discrete) provided as options to the DSL. Returns an expression which evaluates to these. -# Infered parameters that are updated byu the event should be declared using e.g. `@discretes p(t)`. +# Inferred parameters that are updated byu the event should be declared using e.g. `@discretes p(t)`. # `read_events_option!` moves these from `ps_inferred` to `discs_inferred` function read_events_option!(options, discs_inferred::Vector, ps_inferred::Vector, discs_declared::Vector, event_type::Symbol) # Prepares the events, if required to, converts them to block form. @@ -941,7 +1009,7 @@ function read_events_option!(options, discs_inferred::Vector, ps_inferred::Vecto error("Trying to read an unsupported event type.") end events_input = haskey(options, event_type) ? get_block_option(options[event_type]) : - striplines(:(begin end)) + striplines(:(begin end)) events_input = option_block_form(events_input) # Goes through the events, checks for errors, and adds them to the output vector. @@ -950,11 +1018,11 @@ function read_events_option!(options, discs_inferred::Vector, ps_inferred::Vecto # Formatting error checks. # NOTE: Maybe we should move these deeper into the system (rather than the DSL), throwing errors more generally? if (arg isa Expr) && (arg.head != :call) || (arg.args[1] != :(=>)) || - (length(arg.args) != 3) + (length(arg.args) != 3) error("Events should be on form `condition => affect`, separated by a `=>`. This appears not to be the case for: $(arg).") end if (arg isa Expr) && (arg.args[2] isa Expr) && (arg.args[2].head != :vect) && - (event_type == :continuous_events) + (event_type == :continuous_events) error("The condition part of continuous events (the left-hand side) must be a vector. This is not the case for: $(arg).") end if (arg isa Expr) && (arg.args[3] isa Expr) && (arg.args[3].head != :vect) @@ -991,8 +1059,10 @@ function read_events_option!(options, discs_inferred::Vector, ps_inferred::Vecto end # Adds the correctly formatted event to the event creation expression. - event_func = (event_type == :continuous_events ? :(MT.SymbolicContinuousCallback) : - :(MT.SymbolicDiscreteCallback)) + event_func = ( + event_type == :continuous_events ? :(MT.SymbolicContinuousCallback) : + :(MT.SymbolicDiscreteCallback) + ) event = :($event_func($(arg.args[2]) => $affects; discrete_parameters = $disc_ps)) push!(events_expr.args, event) end @@ -1051,7 +1121,7 @@ end # be used or not. If not provided, use the default (true). function read_combinatoric_ratelaws_option(options) return haskey(options, :combinatoric_ratelaws) ? - get_block_option(options[:combinatoric_ratelaws]) : true + get_block_option(options[:combinatoric_ratelaws]) : true end # Reads unit_checks option, which determines if unit validation should be run or not. @@ -1121,7 +1191,7 @@ Notes: macro. See [The Reaction DSL](@ref dsl_description) tutorial for more details. """ macro reaction(ex) - make_reaction(ex) + return make_reaction(ex) end # Function for creating a Reaction structure (used by the @reaction macro). @@ -1145,7 +1215,7 @@ function make_reaction(ex::Expr) # Returns a rephrased expression which generates the `Reaction`. # _unit_checks is defined here so that get_rxexpr's generated code can reference it. - quote + return quote $pexprs $iv $spexprs @@ -1168,13 +1238,15 @@ end # Special function calls like "hill(...)" are not expanded. function recursive_escape_functions!(expr::ExprValues, syms_skip = []) (typeof(expr) != Expr) && (return expr) - foreach(i -> expr.args[i] = recursive_escape_functions!(expr.args[i], syms_skip), - 1:length(expr.args)) + foreach( + i -> expr.args[i] = recursive_escape_functions!(expr.args[i], syms_skip), + 1:length(expr.args) + ) if (expr.head == :call) && (expr.args[1] isa Symbol) &&!isdefined(Catalyst, expr.args[1]) && expr.args[1] ∉ syms_skip expr.args[1] = esc(expr.args[1]) end - expr + return expr end # Returns the length of an expression tuple, or 1 if it is not an expression tuple (probably a Symbol/Numerical). diff --git a/src/expression_utils.jl b/src/expression_utils.jl index fd997b18f3..809684ca87 100644 --- a/src/expression_utils.jl +++ b/src/expression_utils.jl @@ -13,7 +13,7 @@ function esc_dollars!(ex) ex.args[i] = esc_dollars!(ex.args[i]) end end - ex + return ex end # Checks if an expression is an escaped expression (e.g. on the form `$(Expr(:escape, :Y))`) @@ -26,7 +26,7 @@ end # Throws an error when a forbidden symbol is used. function forbidden_symbol_check(syms) used_forbidden_syms = intersect(forbidden_symbols_error, syms) - isempty(used_forbidden_syms) || + return isempty(used_forbidden_syms) || error("The following symbol(s) are used as species or parameters: $used_forbidden_syms, this is not permitted.") end diff --git a/src/latex_utils.jl b/src/latex_utils.jl index 7f8dfc6728..bfb98d5b25 100644 --- a/src/latex_utils.jl +++ b/src/latex_utils.jl @@ -32,5 +32,5 @@ as plain math italic instead of `\\mathtt{}` in LaTeX. No-op if already set. """ function _add_latex_wrapper(s) _has_latex_wrapper(s) && return s - _set_latex_wrapper(s, string) + return _set_latex_wrapper(s, string) end diff --git a/src/latexify_recipes.jl b/src/latexify_recipes.jl index 5a16819598..9b64215c12 100644 --- a/src/latexify_recipes.jl +++ b/src/latexify_recipes.jl @@ -47,14 +47,16 @@ function processsym(s) var = value(Symbolics.variable(MT.getname(s), idxs...)) end _has_latex_wrapper(var) || (var = _set_latex_wrapper(var, string)) - var + return var end -function chemical_arrows(rn::ReactionSystem; +function chemical_arrows( + rn::ReactionSystem; double_linebreak = LATEX_DEFS.double_linebreak, starred = LATEX_DEFS.starred, mathrm = true, mathjax = LATEX_DEFS.mathjax, math_delimiters = false, - show_time_arg = false, kwargs...) + show_time_arg = false, kwargs... + ) any_nonrx_subsys(rn) && (@warn "Latexify currently ignores non-ReactionSystem subsystems. Please call `flatsys = flatten(sys)` to obtain a flattened version of your system before trying to Latexify it.") @@ -74,8 +76,10 @@ function chemical_arrows(rn::ReactionSystem; # test if in IJulia since their mathjax is outdated... # VSCODE uses Katex and doesn't have this issue. - if mathjax || (isdefined(Main, :IJulia) && Main.IJulia.inited && - !any(s -> occursin("VSCODE", s), collect(keys(ENV)))) + if mathjax || ( + isdefined(Main, :IJulia) && Main.IJulia.inited && + !any(s -> occursin("VSCODE", s), collect(keys(ENV))) + ) str *= "\\require{mhchem} \n" end @@ -97,19 +101,23 @@ function chemical_arrows(rn::ReactionSystem; rate = (r.rate isa SymbolicT && !show_time_arg) ? subber(r.rate) : r.rate ### Generate formatted string of substrates - substrates = [make_stoich_str(substrate[1], substrate[2], subber; mathrm, - kwargs...) - for substrate in zip(r.substrates, r.substoich)] + substrates = [ + make_stoich_str( + substrate[1], substrate[2], subber; mathrm, + kwargs... + ) + for substrate in zip(r.substrates, r.substoich) + ] isempty(substrates) && (substrates = ["\\varnothing"]) str *= join(substrates, " + ") ### Generate reaction arrows if i + 1 <= length(rxs) && issetequal(r.products, rxs[i + 1].substrates) && - issetequal(r.substrates, rxs[i + 1].products) + issetequal(r.substrates, rxs[i + 1].products) ### Bi-directional arrows rate_backwards = (rxs[i + 1].rate isa SymbolicT && !show_time_arg) ? subber(rxs[i + 1].rate) : - rxs[i + 1].rate + rxs[i + 1].rate str *= " &" * rev_arrow str *= "[" * latexraw(rate_backwards; kwargs...) * "]" str *= "{" * latexraw(rate; kwargs...) * "} " @@ -120,14 +128,20 @@ function chemical_arrows(rn::ReactionSystem; end ### Generate formatted string of products - products = [make_stoich_str(product[1], product[2], subber; mathrm = true, - kwargs...) - for product in zip(r.products, r.prodstoich)] + products = [ + make_stoich_str( + product[1], product[2], subber; mathrm = true, + kwargs... + ) + for product in zip(r.products, r.prodstoich) + ] isempty(products) && (products = ["\\varnothing"]) str *= join(products, " + ") - if ((i == lastidx) || - (((i + 1) == lastidx) && (backwards_reaction == true))) && - isempty(nonrxs) + if ( + (i == lastidx) || + (((i + 1) == lastidx) && (backwards_reaction == true)) + ) && + isempty(nonrxs) str *= " \n " else str *= " $eol" @@ -169,7 +183,7 @@ function any_nonrx_subsys(rn::MT.AbstractSystem) for subsys in get_systems(rn) any_nonrx_subsys(subsys) && (return true) end - false + return false end function make_stoich_str(spec, stoich, subber; mathrm = true, kwargs...) @@ -181,17 +195,17 @@ function make_stoich_str(spec, stoich, subber; mathrm = true, kwargs...) poststr = "" end - if isequal(stoich, one(stoich)) + return if isequal(stoich, one(stoich)) prestr * latexraw(subber(spec); kwargs...) * poststr else if (stoich isa SymbolicT) && iscall(stoich) LaTeXString("(") * - latexraw(subber(stoich); kwargs...) * - LaTeXString(")") * - prestr * latexraw(subber(spec); kwargs...) * poststr + latexraw(subber(stoich); kwargs...) * + LaTeXString(")") * + prestr * latexraw(subber(spec); kwargs...) * poststr else latexraw(subber(stoich); kwargs...) * LaTeXString(" ") * - prestr * latexraw(subber(spec); kwargs...) * poststr + prestr * latexraw(subber(spec); kwargs...) * poststr end end end diff --git a/src/mtk_nullspace_function.jl b/src/mtk_nullspace_function.jl index 6a5cb7dfc8..f89a31b772 100644 --- a/src/mtk_nullspace_function.jl +++ b/src/mtk_nullspace_function.jl @@ -12,13 +12,15 @@ using SparseArrays using SparseArrays: AbstractSparseMatrixCSC, getcolptr macro swap(a, b) - esc(:(($a, $b) = ($b, $a))) + return esc(:(($a, $b) = ($b, $a))) end import Base: swaprows! -function bareiss_update!(zero!, M::StridedMatrix, k, swapto, pivot, - prev_pivot::Base.BitInteger) +function bareiss_update!( + zero!, M::StridedMatrix, k, swapto, pivot, + prev_pivot::Base.BitInteger + ) flag = zero(prev_pivot) prev_pivot = Base.MultiplicativeInverses.SignedMultiplicativeInverse(prev_pivot) @inbounds for i in (k + 1):size(M, 2) @@ -29,14 +31,14 @@ function bareiss_update!(zero!, M::StridedMatrix, k, swapto, pivot, end end iszero(flag) || error("Overflow occurred") - zero!(M, (k + 1):size(M, 1), k) + return zero!(M, (k + 1):size(M, 1), k) end function bareiss_update!(zero!, M::StridedMatrix, k, swapto, pivot, prev_pivot) @inbounds for i in (k + 1):size(M, 2), j in (k + 1):size(M, 1) M[j, i] = exactdiv(M[j, i] * pivot - M[j, k] * M[k, i], prev_pivot) end - zero!(M, (k + 1):size(M, 1), k) + return zero!(M, (k + 1):size(M, 1), k) end @views function bareiss_update!(zero!, M::AbstractMatrix, k, swapto, pivot, prev_pivot) @@ -51,14 +53,16 @@ end end end -function bareiss_update_virtual_colswap!(zero!, M::AbstractMatrix, k, swapto, pivot, - prev_pivot) +function bareiss_update_virtual_colswap!( + zero!, M::AbstractMatrix, k, swapto, pivot, + prev_pivot + ) if prev_pivot isa Base.BitInteger prev_pivot = Base.MultiplicativeInverses.SignedMultiplicativeInverse(prev_pivot) end V = @view M[(k + 1):end, :] V .= @views exactdiv.(V .* pivot .- M[(k + 1):end, swapto[2]] * M[k, :]', prev_pivot) - zero!(M, (k + 1):size(M, 1), swapto[2]) + return zero!(M, (k + 1):size(M, 1), swapto[2]) end bareiss_zero!(M, i, j) = M[i, j] .= zero(eltype(M)) @@ -67,19 +71,21 @@ function find_pivot_col(M, i) p = findfirst(!iszero, @view M[i, i:end]) p === nothing && return nothing idx = CartesianIndex(i, p + i - 1) - (idx, M[idx]) + return (idx, M[idx]) end function find_pivot_any(M, i) p = findfirst(!iszero, @view M[i:end, i:end]) p === nothing && return nothing idx = p + CartesianIndex(i - 1, i - 1) - (idx, M[idx]) + return (idx, M[idx]) end const bareiss_colswap = (Base.swapcols!, swaprows!, bareiss_update!, bareiss_zero!) -const bareiss_virtcolswap = ((M, i, j) -> nothing, swaprows!, - bareiss_update_virtual_colswap!, bareiss_zero!) +const bareiss_virtcolswap = ( + (M, i, j) -> nothing, swaprows!, + bareiss_update_virtual_colswap!, bareiss_zero!, +) """ bareiss!(M, [swap_strategy]) @@ -91,8 +97,10 @@ swap_strategy is an optional argument that determines how the swapping of rows a bareiss_colswap (the default) swaps the columns and rows normally. bareiss_virtcolswap pretends to swap the columns which can be faster for sparse matrices. """ -function bareiss!(M::AbstractMatrix{T}, swap_strategy = bareiss_colswap; - find_pivot = find_pivot_any, column_pivots = nothing) where {T} +function bareiss!( + M::AbstractMatrix{T}, swap_strategy = bareiss_colswap; + find_pivot = find_pivot_any, column_pivots = nothing + ) where {T} swapcols!, swaprows!, update!, zero! = swap_strategy prev = one(eltype(M)) n = size(M, 1) @@ -139,25 +147,27 @@ function nullspace(A; col_order = nothing) end fill!(pivots_cache, 0) - + # Modified: Was `N = ModelingToolkit.reduced_echelon_nullspace(rank, B, pivots_cache)`. - N = reduced_echelon_nullspace(rank, B, pivots_cache) - apply_inv_pivot_rows!(N, column_pivots) + N = reduced_echelon_nullspace(rank, B, pivots_cache) + return apply_inv_pivot_rows!(N, column_pivots) end function apply_inv_pivot_rows!(M, ipiv) for i in size(M, 1):-1:1 swaprows!(M, i, ipiv[i]) end - M + return M end ### ### Modified from AbstractAlgebra.jl ### ### https://github.com/Nemocas/AbstractAlgebra.jl/blob/4803548c7a945f3f7bd8c63f8bb7c79fac92b11a/LICENSE.md -function reduce_echelon!(A::AbstractMatrix{T}, rank, d, - pivots_cache = zeros(Int, size(A, 2))) where {T} +function reduce_echelon!( + A::AbstractMatrix{T}, rank, d, + pivots_cache = zeros(Int, size(A, 2)) + ) where {T} m, n = size(A) isreduced = true @inbounds for i in 1:rank @@ -222,8 +232,10 @@ function reduce_echelon!(A::AbstractMatrix{T}, rank, d, return A end -function reduced_echelon_nullspace(rank, A::AbstractMatrix{T}, - pivots_cache = zeros(Int, size(A, 2))) where {T} +function reduced_echelon_nullspace( + rank, A::AbstractMatrix{T}, + pivots_cache = zeros(Int, size(A, 2)) + ) where {T} n = size(A, 2) nullity = n - rank U = zeros(T, n, nullity) diff --git a/src/network_analysis.jl b/src/network_analysis.jl index 277319fe9d..6a16cd38c2 100644 --- a/src/network_analysis.jl +++ b/src/network_analysis.jl @@ -1,9 +1,9 @@ ### Reaction Complex Handling ### -# Checks if a reaction has integer stoichiometry. Used to detect and error on +# Checks if a reaction has integer stoichiometry. Used to detect and error on # non-integer stoichiometry in network analysis functions. function has_integer_stoichiometry(rx::Reaction) - (eltype(rx.substoich) <: Integer) && (eltype(rx.prodstoich) <: Integer) + return (eltype(rx.substoich) <: Integer) && (eltype(rx.prodstoich) <: Integer) end # Helper function to check all reactions for integer stoichiometry and throw an informative error. @@ -12,13 +12,15 @@ function check_integer_stoichiometry(rn::ReactionSystem) nonint_rxs = filter(!has_integer_stoichiometry, rxs) if !isempty(nonint_rxs) rx_strs = [" $rx" for rx in nonint_rxs] - error("Network analysis functions require integer stoichiometric coefficients, " * - "but the following reaction(s) have non-integer stoichiometry:\n" * - join(rx_strs, "\n") * - "\n\nNon-integer stoichiometry is not currently supported for " * - "network analysis functions.") + error( + "Network analysis functions require integer stoichiometric coefficients, " * + "but the following reaction(s) have non-integer stoichiometry:\n" * + join(rx_strs, "\n") * + "\n\nNon-integer stoichiometry is not currently supported for " * + "network analysis functions." + ) end - nothing + return nothing end # get the species indices and stoichiometry while filtering out constant species. @@ -38,7 +40,7 @@ function filter_constspecs(specs, stoich::AbstractVector{V}, smap) where {V <: I ids = map(Base.Fix1(getindex, smap), specs) filtered_stoich = copy(stoich) end - ids, filtered_stoich + return ids, filtered_stoich end """ @@ -89,7 +91,7 @@ function reactioncomplexmap(rn::ReactionSystem) complextorxsmap[prodrc] = [i => 1] end end - complextorxsmap + return complextorxsmap end @doc raw""" @@ -121,17 +123,19 @@ function reactioncomplexes(rn::ReactionSystem; sparse = false) if isempty(nps.complexes) || (sparse != issparse(nps.complexes)) complextorxsmap = reactioncomplexmap(rn) nps.complexes, - nps.incidencemat = if sparse + nps.incidencemat = if sparse reactioncomplexes(SparseMatrixCSC{Int, Int}, rn, complextorxsmap) else reactioncomplexes(Matrix{Int}, rn, complextorxsmap) end end - nps.complexes, nps.incidencemat + return nps.complexes, nps.incidencemat end -function reactioncomplexes(::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSystem, - complextorxsmap) +function reactioncomplexes( + ::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSystem, + complextorxsmap + ) complexes = collect(keys(complextorxsmap)) Is = Int[] Js = Int[] @@ -144,7 +148,7 @@ function reactioncomplexes(::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSystem end end B = sparse(Is, Js, Vs, length(complexes), numreactions(rn)) - complexes, B + return complexes, B end function reactioncomplexes(::Type{Matrix{Int}}, rn::ReactionSystem, complextorxsmap) @@ -155,7 +159,7 @@ function reactioncomplexes(::Type{Matrix{Int}}, rn::ReactionSystem, complextorxs B[i, j] = σ end end - complexes, B + return complexes, B end """ @@ -190,7 +194,7 @@ function complexstoichmat(rn::ReactionSystem; sparse = false) complexstoichmat(Matrix{Int}, rn, keys(reactioncomplexmap(rn))) end end - nps.complexstoichmat + return nps.complexstoichmat end function complexstoichmat(::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSystem, rcs) @@ -204,7 +208,7 @@ function complexstoichmat(::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSystem, push!(Vs, rcel.speciesstoich) end end - Z = sparse(Is, Js, Vs, numspecies(rn), length(rcs)) + return Z = sparse(Is, Js, Vs, numspecies(rn), length(rcs)) end function complexstoichmat(::Type{Matrix{Int}}, rn::ReactionSystem, rcs) @@ -214,7 +218,7 @@ function complexstoichmat(::Type{Matrix{Int}}, rn::ReactionSystem, rcs) Z[rcel.speciesid, i] = rcel.speciesstoich end end - Z + return Z end @doc raw""" @@ -229,7 +233,7 @@ Returns a symbolic matrix by default, but will return a numerical matrix if para function laplacianmat(rn::ReactionSystem, pmap::Dict = Dict(); sparse = false) D = incidencemat(rn; sparse) K = fluxmat(rn, pmap; sparse) - D * K + return D * K end @doc raw""" @@ -277,7 +281,7 @@ function fluxmat(::Type{SparseMatrixCSC{T, Int}}, rcmap, rates) where {T} end end end - Z = sparse(Is, Js, Vs, length(rates), length(rcmap)) + return Z = sparse(Is, Js, Vs, length(rates), length(rcmap)) end function fluxmat(::Type{Matrix{T}}, rcmap, rates) where {T} @@ -289,17 +293,17 @@ function fluxmat(::Type{Matrix{T}}, rcmap, rates) where {T} dir == -1 && (K[rx, i] = rates[rx]) end end - K + return K end function fluxmat(rn::ReactionSystem, pmap::Vector) pdict = Dict(pmap) - fluxmat(rn, pdict) + return fluxmat(rn, pdict) end function fluxmat(rn::ReactionSystem, pmap::Tuple) pdict = Dict(pmap) - fluxmat(rn, pdict) + return fluxmat(rn, pdict) end # Helper to substitute values into a (vector of) symbolic expressions. The syms are the symbols to substitute and the symexprs are the expressions to substitute into. @@ -308,7 +312,7 @@ function substitutevals(rn::ReactionSystem, map::Dict, syms, symexprs) error("Incorrect number of parameter-value pairs were specified.") map = symmap_to_varmap(rn, map) map = Dict(value(k) => v for (k, v) in map) - vals = [value(substitute(expr, map)) for expr in symexprs] + return vals = [value(substitute(expr, map)) for expr in symexprs] end """ @@ -322,8 +326,10 @@ If the `combinatoric_ratelaws` option is set, will include prefactors for that ( **Warning**: Unlike other Catalyst functions, the `massactionvector` function will return a `Vector{Num}` in the symbolic case. This is to allow easier computation of the matrix decomposition of the ODEs. """ -function massactionvector(rn::ReactionSystem, scmap::Dict = Dict(); - combinatoric_ratelaws = Catalyst.get_combinatoric_ratelaws(rn)) +function massactionvector( + rn::ReactionSystem, scmap::Dict = Dict(); + combinatoric_ratelaws = Catalyst.get_combinatoric_ratelaws(rn) + ) r = numreactions(rn) rxs = reactions(rn) sm = speciesmap(rn) @@ -354,19 +360,23 @@ function massactionvector(rn::ReactionSystem, scmap::Dict = Dict(); push!(Φ, maprod) end - Φ + return Φ end -function massactionvector(rn::ReactionSystem, scmap::Tuple; - combinatoric_ratelaws = Catalyst.get_combinatoric_ratelaws(rn)) +function massactionvector( + rn::ReactionSystem, scmap::Tuple; + combinatoric_ratelaws = Catalyst.get_combinatoric_ratelaws(rn) + ) sdict = Dict(scmap) - massactionvector(rn, sdict; combinatoric_ratelaws) + return massactionvector(rn, sdict; combinatoric_ratelaws) end -function massactionvector(rn::ReactionSystem, scmap::Vector; - combinatoric_ratelaws = Catalyst.get_combinatoric_ratelaws(rn)) +function massactionvector( + rn::ReactionSystem, scmap::Vector; + combinatoric_ratelaws = Catalyst.get_combinatoric_ratelaws(rn) + ) sdict = Dict(scmap) - massactionvector(rn, sdict; combinatoric_ratelaws) + return massactionvector(rn, sdict; combinatoric_ratelaws) end @doc raw""" @@ -398,7 +408,7 @@ function complexoutgoingmat(rn::ReactionSystem; sparse = false) complexoutgoingmat(Matrix{Int}, rn, B) end end - nps.complexoutgoingmat + return nps.complexoutgoingmat end function complexoutgoingmat(::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSystem, B) @@ -420,7 +430,7 @@ function complexoutgoingmat(::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSyste end end end - sparse(Is, Js, Vs, size(B, 1), size(B, 2)) + return sparse(Is, Js, Vs, size(B, 1), size(B, 2)) end function complexoutgoingmat(::Type{Matrix{Int}}, rn::ReactionSystem, B) @@ -428,7 +438,7 @@ function complexoutgoingmat(::Type{Matrix{Int}}, rn::ReactionSystem, B) for (I, b) in pairs(Δ) (b == 1) && (Δ[I] = 0) end - Δ + return Δ end """ @@ -452,7 +462,7 @@ function incidencematgraph(rn::ReactionSystem) isempty(nps.incidencemat) && reactioncomplexes(rn) nps.incidencegraph = incidencematgraph(nps.incidencemat) end - nps.incidencegraph + return nps.incidencegraph end function incidencematgraph(incidencemat::Matrix{Int}) @@ -519,13 +529,13 @@ function species_reaction_graph(rn::ReactionSystem) edgelist = Graphs.Edge[] for (i, rx) in enumerate(rxs) for spec in rx.substrates - push!(edgelist, Graphs.Edge(sm[spec], s+i)) + push!(edgelist, Graphs.Edge(sm[spec], s + i)) end for spec in rx.products - push!(edgelist, Graphs.Edge(s+i, sm[spec])) + push!(edgelist, Graphs.Edge(s + i, sm[spec])) end end - srg = Graphs.SimpleDiGraphFromIterator(edgelist) + return srg = Graphs.SimpleDiGraphFromIterator(edgelist) end ### Linkage, Deficiency, Reversibility ### @@ -557,7 +567,7 @@ function linkageclasses(rn::ReactionSystem) if isempty(nps.linkageclasses) nps.linkageclasses = linkageclasses(incidencematgraph(rn)) end - nps.linkageclasses + return nps.linkageclasses end linkageclasses(incidencegraph) = Graphs.connected_components(incidencegraph) @@ -573,7 +583,7 @@ function stronglinkageclasses(rn::ReactionSystem) if isempty(nps.stronglinkageclasses) nps.stronglinkageclasses = stronglinkageclasses(incidencematgraph(rn)) end - nps.stronglinkageclasses + return nps.stronglinkageclasses end stronglinkageclasses(incidencegraph) = Graphs.strongly_connected_components(incidencegraph) @@ -591,7 +601,7 @@ function terminallinkageclasses(rn::ReactionSystem) tslcs = filter(lc -> isterminal(lc, rn), slcs) nps.terminallinkageclasses = tslcs end - nps.terminallinkageclasses + return nps.terminallinkageclasses end # Helper function for terminallinkageclasses. Given a linkage class and a reaction network, say whether the linkage class is terminal, @@ -609,7 +619,7 @@ function isterminal(lc::Vector, rn::ReactionSystem) p in Set(lc) ? continue : return false end end - true + return true end function isforestlike(rn::ReactionSystem) @@ -622,7 +632,7 @@ function isforestlike(rn::ReactionSystem) nps = get_networkproperties(subnet) isempty(nps.incidencemat) && reactioncomplexes(subnet; sparse = sparseig) end - all(Graphs.is_tree ∘ SimpleGraph ∘ incidencematgraph, subnets) + return all(Graphs.is_tree ∘ SimpleGraph ∘ incidencematgraph, subnets) end @doc raw""" @@ -657,13 +667,19 @@ function deficiency(rn::ReactionSystem) r = nps.rank nps.deficiency = Graphs.nv(ig) - length(lc) - r end - nps.deficiency + return nps.deficiency end # Used in the subsequent function. function subnetworkmapping(linkageclass, allrxs, complextorxsmap, p) - rxinds = sort!(collect(Set(rxidx for rcidx in linkageclass - for rxidx in complextorxsmap[rcidx]))) + rxinds = sort!( + collect( + Set( + rxidx for rcidx in linkageclass + for rxidx in complextorxsmap[rcidx] + ) + ) + ) rxs = allrxs[rxinds] specset = Set(s for rx in rxs for s in rx.substrates if !isconstant(s)) for rx in rxs @@ -677,7 +693,7 @@ function subnetworkmapping(linkageclass, allrxs, complextorxsmap, p) Symbolics.get_variables!(newps, rx.rate, p) end newps_vec = collect(newps) - rxs, specs, newps_vec # reactions and species involved in reactions of subnetwork + return rxs, specs, newps_vec # reactions and species involved in reactions of subnetwork end """ @@ -706,10 +722,12 @@ function subnetworks(rs::ReactionSystem) for i in 1:length(lcs) reacs, specs, newps = subnetworkmapping(lcs[i], rxs, complextorxsmap, p) newname = Symbol(nameof(rs), "_", i) - push!(subnetworks, - ReactionSystem(reacs, t, specs, newps; name = newname, spatial_ivs)) + push!( + subnetworks, + ReactionSystem(reacs, t, specs, newps; name = newname, spatial_ivs) + ) end - subnetworks + return subnetworks end """ @@ -735,7 +753,7 @@ function linkagedeficiencies(rs::ReactionSystem) nps = get_networkproperties(subnet) δ[i] = length(lcs[i]) - 1 - nps.rank end - δ + return δ end """ @@ -754,7 +772,7 @@ isreversible(sir) """ function isreversible(rn::ReactionSystem) ig = incidencematgraph(rn) - Graphs.reverse(ig) == ig + return Graphs.reverse(ig) == ig end """ @@ -783,7 +801,7 @@ function isweaklyreversible(rn::ReactionSystem, subnets) end # A network is weakly reversible if all of its subnetworks are strongly connected - all(Graphs.is_strongly_connected ∘ incidencematgraph, subnets) + return all(Graphs.is_strongly_connected ∘ incidencematgraph, subnets) end ### Conservation Laws ### @@ -803,7 +821,7 @@ function isconserved(x, default = false) if iscall(x) && operation(x) === getindex x = first(arguments(x)) end - Symbolics.getmetadata(x, ConservedParameter, default) + return Symbolics.getmetadata(x, ConservedParameter, default) end """ @@ -833,7 +851,7 @@ gives function conservedequations(rn::ReactionSystem) conservationlaws(rn) nps = get_networkproperties(rn) - nps.conservedeqs + return nps.conservedeqs end """ @@ -863,7 +881,7 @@ gives function conservationlaw_constants(rn::ReactionSystem) conservationlaws(rn) nps = get_networkproperties(rn) - nps.constantdefs + return nps.constantdefs end """ @@ -874,7 +892,7 @@ conservation laws, each represented as a row in the output. """ function conservationlaws(nsm::Matrix; col_order = nothing) conslaws = positive_nullspace(nsm'; col_order = col_order) - Matrix(conslaws) + return Matrix(conslaws) end # Used in the subsequent function. @@ -891,13 +909,13 @@ function cache_conservationlaw_eqs!(rn::ReactionSystem, N::AbstractMatrix, col_o rhs_terms = Vector{SymbolicT}(undef, nullity) for (i, depidx) in enumerate(depidxs) scaleby = (N[i, depidx] != 1) ? N[i, depidx] : one(eltype(N)) - (scaleby != 0) || + (scaleby != 0) || error("Error, found a zero in the conservation law matrix where one was not expected.") coefs = @view N[i, indepidxs] rhs_terms[i] = sum(p -> p[1] / scaleby * p[2], zip(coefs, indepspecs)) end - # Declare the conservation constant parameters + # Declare the conservation constant parameters #`using guesses is for consistency and possibly faster initialisation guesses = [Initial(depspecs[i] + rhs_terms[i]) for i in 1:nullity] Γs = @parameters $(CONSERVED_CONSTANT_SYMBOL)[1:nullity] = missing [conserved = true, guess = guesses] @@ -917,7 +935,7 @@ function cache_conservationlaw_eqs!(rn::ReactionSystem, N::AbstractMatrix, col_o nps.constantdefs = constantdefs nps.conservedconst = constants - nothing + return nothing end """ @@ -936,7 +954,7 @@ function conservationlaws(rs::ReactionSystem) nsm = netstoichmat(rs) nps.conservationmat = conservationlaws(nsm; col_order = nps.col_order) cache_conservationlaw_eqs!(rs, nps.conservationmat, nps.col_order) - nps.conservationmat + return nps.conservationmat end """ @@ -965,10 +983,10 @@ function conservationlaw_errorcheck(rs, pre_varmap) vars_with_vals = Set(p[1] for p in pre_varmap) missing_cl_vals = filter(sp -> (sp ∉ vars_with_vals) && !MT.hasdefault(sp), conslaw_species(rs)) isempty(missing_cl_vals) && return - error("The system has conservation laws but initial conditions were not provided for these conservation law-invovled species: $(missing_cl_vals).") + error("The system has conservation laws but initial conditions were not provided for these conservation law-involved species: $(missing_cl_vals).") end -# Returns a vector with all species that are invovled in conservation laws. +# Returns a vector with all species that are involved in conservation laws. # Note: uses Symbolics.get_variables! instead of MT.collect_vars! because # the latter fails to extract variables on Julia 1.10 LTS (empty result # despite valid input expressions). @@ -988,7 +1006,7 @@ end Constructively compute whether a kinetic system (a reaction network with a set of rate constants) will admit detailed-balanced equilibrium solutions, using the Wegscheider conditions, [Feinberg, 1989](https://www.sciencedirect.com/science/article/pii/0009250989851243). A detailed-balanced solution is one for which the rate of every forward reaction exactly equals its reverse reaction. Accepts a dictionary, vector, or tuple of variable-to-value mappings, e.g. [k1 => 1.0, k2 => 2.0,...]. """ -function isdetailedbalanced(rs::ReactionSystem, parametermap::Dict; abstol = 0, reltol = 1e-9) +function isdetailedbalanced(rs::ReactionSystem, parametermap::Dict; abstol = 0, reltol = 1.0e-9) if length(parametermap) != numparams(rs) error("Incorrect number of parameters specified.") elseif !isreversible(rs) @@ -1032,10 +1050,18 @@ function isdetailedbalanced(rs::ReactionSystem, parametermap::Dict; abstol = 0, for i in 1:size(sols, 2) α = sols[:, i] - fwd = prod([K[Graphs.src(e), Graphs.dst(e)]^α[i] - for (e, i) in zip(spanning_forest, 1:length(α))]) - rev = prod([K[Graphs.dst(e), Graphs.src(e)]^α[i] - for (e, i) in zip(spanning_forest, 1:length(α))]) + fwd = prod( + [ + K[Graphs.src(e), Graphs.dst(e)]^α[i] + for (e, i) in zip(spanning_forest, 1:length(α)) + ] + ) + rev = prod( + [ + K[Graphs.dst(e), Graphs.src(e)]^α[i] + for (e, i) in zip(spanning_forest, 1:length(α)) + ] + ) isapprox(fwd, rev; atol = abstol, rtol = reltol) ? continue : return false end end @@ -1053,12 +1079,12 @@ end function isdetailedbalanced(rs::ReactionSystem, parametermap::Vector{<:Pair}) pdict = Dict(parametermap) - isdetailedbalanced(rs, pdict) + return isdetailedbalanced(rs, pdict) end function isdetailedbalanced(rs::ReactionSystem, parametermap::Tuple{<:Pair}) pdict = Dict(parametermap) - isdetailedbalanced(rs, pdict) + return isdetailedbalanced(rs, pdict) end function isdetailedbalanced(rs::ReactionSystem, parametermap) @@ -1102,12 +1128,12 @@ end function iscomplexbalanced(rs::ReactionSystem, parametermap::Vector{<:Pair}) pdict = Dict(parametermap) - iscomplexbalanced(rs, pdict) + return iscomplexbalanced(rs, pdict) end function iscomplexbalanced(rs::ReactionSystem, parametermap::Tuple) pdict = Dict(parametermap) - iscomplexbalanced(rs, pdict) + return iscomplexbalanced(rs, pdict) end function iscomplexbalanced(rs::ReactionSystem, parametermap) @@ -1165,7 +1191,7 @@ function adjacencymat(::Type{SparseMatrixCSC{T, Int}}, D, rates) where {T} push!(Js, p) push!(Vs, rates[r]) end - A = sparse(Is, Js, Vs, nc, nc) + return A = sparse(Is, Js, Vs, nc, nc) end function adjacencymat(::Type{Matrix{T}}, D, rates) where {T} @@ -1177,17 +1203,17 @@ function adjacencymat(::Type{Matrix{T}}, D, rates) where {T} p = findfirst(==(1), @view D[:, r]) A[s, p] = rates[r] end - A + return A end function adjacencymat(rn::ReactionSystem, pmap::Vector{<:Pair}; sparse = false) pdict = Dict(pmap) - adjacencymat(rn, pdict; sparse) + return adjacencymat(rn, pdict; sparse) end function adjacencymat(rn::ReactionSystem, pmap::Tuple; sparse = false) pdict = Dict(pmap) - adjacencymat(rn, pdict; sparse) + return adjacencymat(rn, pdict; sparse) end function adjacencymat(rn::ReactionSystem, pmap) @@ -1238,7 +1264,7 @@ function treeweight(t::SimpleDiGraph, g::SimpleDiGraph, distmx::Matrix) t = Graphs.dst(e) prod *= distmx[s, t] end - prod + return prod end """ @@ -1253,11 +1279,11 @@ function cycles(rs::ReactionSystem) nsm = netstoichmat(rs) !isempty(nps.cyclemat) && return nps.cyclemat nps.cyclemat = cycles(nsm; col_order = nps.col_order) - nps.cyclemat + return nps.cyclemat end function cycles(nsm::Matrix; col_order = nothing) - positive_nullspace(nsm; col_order) + return positive_nullspace(nsm; col_order) end function positive_nullspace(M::T; col_order = nothing) where {T <: AbstractMatrix} @@ -1270,11 +1296,13 @@ function positive_nullspace(M::T; col_order = nothing) where {T <: AbstractMatri end # check we haven't overflowed - iszero(M * N) || error("Calculation of the cycle matrix was inaccurate, " - * "likely due to numerical overflow. Please use a larger integer " - * "type like Int128 or BigInt for the net stoichiometry matrix.") + iszero(M * N) || error( + "Calculation of the cycle matrix was inaccurate, " + * "likely due to numerical overflow. Please use a larger integer " + * "type like Int128 or BigInt for the net stoichiometry matrix." + ) - T(N) + return T(N) end """ @@ -1283,7 +1311,7 @@ end See documentation for [`cycles`](@ref). """ function fluxvectors(rs::ReactionSystem) - cycles(rs) + return cycles(rs) end ### Deficiency one @@ -1307,7 +1335,7 @@ function satisfiesdeficiencyone(rn::ReactionSystem) # 1) the deficiency of each individual linkage class is at most 1; # 2) the sum of the linkage deficiencies is the total deficiency, and # 3) there is only one terminal linkage class per linkage class. - all(<=(1), δ_l) && (sum(δ_l) == δ) && (length(lcs) == length(tslcs)) + return all(<=(1), δ_l) && (sum(δ_l) == δ) && (length(lcs) == length(tslcs)) end """ @@ -1319,7 +1347,7 @@ function satisfiesdeficiencyzero(rn::ReactionSystem) all(r -> ismassaction(r, rn), reactions(rn)) || error("The deficiency zero theorem is only valid for reaction networks that are mass action.") δ = deficiency(rn) - δ == 0 && isweaklyreversible(rn, subnetworks(rn)) + return δ == 0 && isweaklyreversible(rn, subnetworks(rn)) end """ @@ -1365,5 +1393,5 @@ function robustspecies(rn::ReactionSystem) nps.robustspecies = robust_species end - nps.robustspecies + return nps.robustspecies end diff --git a/src/plotting.jl b/src/plotting.jl index b4efb42a96..f67b926d7a 100644 --- a/src/plotting.jl +++ b/src/plotting.jl @@ -58,13 +58,16 @@ end for (_, mime) in _showables MIMEType = typeof(MIME(mime)) - @eval Base.show(io::IO, ::$MIMEType, s::Showable{>:$MIMEType}; - options...) = show( - io, $MIMEType(), s.content; s.options..., options...) + @eval Base.show( + io::IO, ::$MIMEType, s::Showable{>:$MIMEType}; + options... + ) = show( + io, $MIMEType(), s.content; s.options..., options... + ) end macro mime_str(s) - :(Showable{MIME{Symbol($s)}}) + return :(Showable{MIME{Symbol($s)}}) end for (name, mime) in _showables diff --git a/src/reaction.jl b/src/reaction.jl index 8c1dd8c446..f1248d531a 100644 --- a/src/reaction.jl +++ b/src/reaction.jl @@ -19,7 +19,7 @@ function isconstant(s) if iscall(s) && operation(s) === getindex s = first(arguments(s)) end - MT.getmetadata(s, ParameterConstantSpecies, false) + return MT.getmetadata(s, ParameterConstantSpecies, false) end """ @@ -29,7 +29,7 @@ Tests if the given symbolic variable corresponds to a boundary condition species """ isbc(s::Num) = isbc(value(s)) function isbc(s) - MT.getmetadata(s, VariableBCSpecies, false) + return MT.getmetadata(s, VariableBCSpecies, false) end """ @@ -56,9 +56,9 @@ Notes: function tospecies(s) MT.isparameter(s) && throw(ArgumentError("Parameters, including isconstantspecies parameters, can not be converted to species. Please pass a variable.")) - MT.getmetadata(unwrap(s), ParameterConstantSpecies, false) && - throw(ArgumentError("isconstantspecies metadata can only be used with parameters.")) - MT.setmetadata(s, VariableSpecies, true) + MT.getmetadata(unwrap(s), ParameterConstantSpecies, false) && + throw(ArgumentError("isconstantspecies metadata can only be used with parameters.")) + return MT.setmetadata(s, VariableSpecies, true) end # Other species functions. @@ -89,7 +89,7 @@ function get_netstoich(subs, prods, sstoich, pstoich) end # stoichiometry as a vector - [el for el in nsdict if !_iszero(el[2])] + return [el for el in nsdict if !_iszero(el[2])] end # Get the net stoichiometries' type. @@ -170,10 +170,12 @@ struct Reaction{S, T} end # Five-argument constructor accepting rate, substrates, and products, and their stoichiometries. -function Reaction(rate, subs, prods, substoich, prodstoich; +function Reaction( + rate, subs, prods, substoich, prodstoich; netstoich = nothing, metadata = Pair{Symbol, Any}[], only_use_rate = metadata_only_use_rate_check(metadata), - unit_checks::Bool = false, kwargs...) + unit_checks::Bool = false, kwargs... + ) # Handles empty/nothing vectors. isnothing(subs) || isempty(subs) && (subs = nothing) isnothing(prods) || isempty(prods) && (prods = nothing) @@ -230,7 +232,7 @@ function Reaction(rate, subs, prods, substoich, prodstoich; get_netstoich(subs, prods, substoich′, prodstoich′) else (netstoich_stoichtype(netstoich) != stoich_type) ? - convert.(stoich_type, netstoich) : netstoich + convert.(stoich_type, netstoich) : netstoich end # Check that all metadata entries are unique. (cannot use `in` since some entries may be symbolics). @@ -248,14 +250,14 @@ function Reaction(rate, subs, prods, substoich, prodstoich; rx = Reaction(value(rate), subs, prods, substoich′, prodstoich′, ns, only_use_rate, metadata) unit_checks && assert_valid_units(rx) - rx + return rx end # Three argument constructor assumes stoichiometric coefs are one and integers. function Reaction(rate, subs, prods; kwargs...) sstoich = isnothing(subs) ? nothing : ones(Int, length(subs)) pstoich = isnothing(prods) ? nothing : ones(Int, length(prods)) - Reaction(rate, subs, prods, sstoich, pstoich; kwargs...) + return Reaction(rate, subs, prods, sstoich, pstoich; kwargs...) end ### Base Function Dispatches ### @@ -268,7 +270,7 @@ function print_rxside(io::IO, specs, stoich) else for (i, spec) in enumerate(specs) prspec = (MT.isparameter(spec) || (MT.operation(spec) == getindex)) ? - spec : MT.operation(spec) + spec : MT.operation(spec) if isequal(stoich[i], one(stoich[i])) print(io, prspec) elseif iscall(stoich[i]) @@ -280,7 +282,7 @@ function print_rxside(io::IO, specs, stoich) (i < length(specs)) && print(io, " + ") end end - nothing + return nothing end # Show function for `Reaction`s. @@ -289,7 +291,7 @@ function Base.show(io::IO, rx::Reaction) print_rxside(io, rx.substrates, rx.substoich) arrow = rx.only_use_rate ? "⇒" : "-->" print(io, " ", arrow, " ") - print_rxside(io, rx.products, rx.prodstoich) + return print_rxside(io, rx.products, rx.prodstoich) end """ @@ -309,7 +311,7 @@ function (==)(rx1::Reaction, rx2::Reaction) issetequal(zip(rx1.products, rx1.prodstoich), zip(rx2.products, rx2.prodstoich)) || return false issetequal(rx1.netstoich, rx2.netstoich) || return false - rx1.only_use_rate == rx2.only_use_rate + return rx1.only_use_rate == rx2.only_use_rate end # Hash function. @@ -324,7 +326,7 @@ function hash(rx::Reaction, h::UInt) for s in rx.netstoich h ⊻= hash(s) end - Base.hash(rx.only_use_rate, h) + return Base.hash(rx.only_use_rate, h) end ### ModelingToolkit Function Dispatches ### @@ -334,7 +336,7 @@ function apply_if_nonempty(f, v) isempty(v) && return v s = similar(v) map!(f, s, v) - s + return s end # Returns a name-spaced version of a reaction. @@ -351,8 +353,10 @@ function MT.namespace_equation(rx::Reaction, name; kw...) ns = similar(rx.netstoich) map!(n -> f(n[1]) => f(n[2]), ns, rx.netstoich) end - Reaction(rate, subs, prods, substoich, prodstoich, netstoich, - rx.only_use_rate, rx.metadata) + return Reaction( + rate, subs, prods, substoich, prodstoich, netstoich, + rx.only_use_rate, rx.metadata + ) end # Overwrites equation-type functions to give the correct input for `Reaction`s. @@ -361,9 +365,11 @@ MT.is_alg_equation(rx::Reaction) = false # MTK functions for extracting variables within equation type object MT.eqtype_supports_collect_vars(rx::Reaction) = true -function MT.collect_vars!(unknowns::OrderedSet{SymbolicT}, parameters::OrderedSet{SymbolicT}, +function MT.collect_vars!( + unknowns::OrderedSet{SymbolicT}, parameters::OrderedSet{SymbolicT}, rx::Reaction, iv::Union{SymbolicT, Nothing}, ::Type{op} = Symbolics.Operator; - depth = 0) where {op} + depth = 0 + ) where {op} MT.collect_vars!(unknowns, parameters, rx.rate, iv, op; depth) for items in (rx.substrates, rx.products, rx.substoich, rx.prodstoich) @@ -427,7 +433,7 @@ function MT.get_variables!(deps::Set, rx::Reaction, variables) # parametric stoichiometry means may have a parameter as a substrate any(isequal(s), variables) && push!(deps, s) end - deps + return deps end # determine which species a reaction modifies @@ -435,14 +441,14 @@ function MT.modified_unknowns!(munknowns, rx::Reaction, sts::Set) for (species, stoich) in rx.netstoich (species in sts) && push!(munknowns, species) end - munknowns + return munknowns end function MT.modified_unknowns!(munknowns, rx::Reaction, sts::AbstractVector) for (species, stoich) in rx.netstoich any(isequal(species), sts) && push!(munknowns, species) end - munknowns + return munknowns end ### `Reaction`-specific Functions ### @@ -469,7 +475,7 @@ function isbcbalanced(rx::Reaction) end end - true + return true end ### Reaction Metadata Implementation ### @@ -556,7 +562,7 @@ function SymbolicUtils.setmetadata(rx::Reaction, key::Symbol, val) else mdvec[idx] = key => val end - nothing + return nothing end ### Catalyst Defined Reaction Metadata ### @@ -763,9 +769,13 @@ function unit_validation_report(rx::Reaction; info::String = "") # Symbolic exponents on unitful bases have indeterminate units. if _has_symbolic_unitful_pow(rx.rate) - push!(issues, UnitValidationIssue(:symbolic_exponent, - string(rx), nothing, nothing, - "Symbolic exponent on unitful base is not supported for unit validation")) + push!( + issues, UnitValidationIssue( + :symbolic_exponent, + string(rx), nothing, nothing, + "Symbolic exponent on unitful base is not supported for unit validation" + ) + ) return UnitValidationReport(false, issues) end @@ -775,8 +785,12 @@ function unit_validation_report(rx::Reaction; info::String = "") for i in 2:length(rx.substrates) if !_units_match(catalyst_get_unit(rx.substrates[i]), subunits) valid = false - push!(issues, UnitValidationIssue(:reaction_species_unit_mismatch, string(rx), subunits, - catalyst_get_unit(rx.substrates[i]), "substrates have differing units")) + push!( + issues, UnitValidationIssue( + :reaction_species_unit_mismatch, string(rx), subunits, + catalyst_get_unit(rx.substrates[i]), "substrates have differing units" + ) + ) end end @@ -784,18 +798,26 @@ function unit_validation_report(rx::Reaction; info::String = "") for i in 2:length(rx.products) if !_units_match(catalyst_get_unit(rx.products[i]), produnits) valid = false - push!(issues, UnitValidationIssue(:reaction_species_unit_mismatch, string(rx), produnits, - catalyst_get_unit(rx.products[i]), "products have differing units")) + push!( + issues, UnitValidationIssue( + :reaction_species_unit_mismatch, string(rx), produnits, + catalyst_get_unit(rx.products[i]), "products have differing units" + ) + ) end end if (subunits !== nothing) && (produnits !== nothing) && !_units_match(subunits, produnits) valid = false - push!(issues, UnitValidationIssue(:reaction_side_unit_mismatch, string(rx), subunits, - produnits, "Substrate units are not consistent with product units.")) + push!( + issues, UnitValidationIssue( + :reaction_side_unit_mismatch, string(rx), subunits, + produnits, "Substrate units are not consistent with product units." + ) + ) end - UnitValidationReport(valid, issues) + return UnitValidationReport(valid, issues) end """ diff --git a/src/reactionsystem.jl b/src/reactionsystem.jl index 8db6feb94c..7d885e30db 100644 --- a/src/reactionsystem.jl +++ b/src/reactionsystem.jl @@ -27,45 +27,49 @@ struct ReactionComplex{V <: Integer} <: AbstractVector{ReactionComplexElement{V} """The stoichiometric coefficients of all species participating in this complex.""" speciesstoichs::Vector{V} - function ReactionComplex{V}(speciesids::Vector{Int}, - speciesstoichs::Vector{V}) where {V <: Integer} - new{V}(speciesids, speciesstoichs) + function ReactionComplex{V}( + speciesids::Vector{Int}, + speciesstoichs::Vector{V} + ) where {V <: Integer} + return new{V}(speciesids, speciesstoichs) end end # Special constructor. -function ReactionComplex(speciesids::Vector{Int}, - speciesstoichs::Vector{V}) where {V <: Integer} +function ReactionComplex( + speciesids::Vector{Int}, + speciesstoichs::Vector{V} + ) where {V <: Integer} (length(speciesids) == length(speciesstoichs)) || error("Creating a complex with different number of species ids and associated stoichiometries.") - ReactionComplex{V}(speciesids, speciesstoichs) + return ReactionComplex{V}(speciesids, speciesstoichs) end # Defines base function overloads for `ReactionComplex`. function (==)(a::ReactionComplex{V}, b::ReactionComplex{V}) where {V <: Integer} - (a.speciesids == b.speciesids) && + return (a.speciesids == b.speciesids) && (a.speciesstoichs == b.speciesstoichs) end function hash(rc::ReactionComplex, h::UInt) - Base.hash(rc.speciesids, Base.hash(rc.speciesstoichs, h)) + return Base.hash(rc.speciesids, Base.hash(rc.speciesstoichs, h)) end Base.size(rc::ReactionComplex) = size(rc.speciesids) Base.length(rc::ReactionComplex) = length(rc.speciesids) function Base.getindex(rc::ReactionComplex, i...) - ReactionComplexElement(getindex(rc.speciesids, i...), getindex(rc.speciesstoichs, i...)) + return ReactionComplexElement(getindex(rc.speciesids, i...), getindex(rc.speciesstoichs, i...)) end function Base.setindex!(rc::ReactionComplex, t::ReactionComplexElement, i...) setindex!(rc.speciesids, t.speciesid, i...) setindex!(rc.speciesstoichs, t.speciesstoich, i...) - rc + return rc end function Base.isless(a::ReactionComplexElement, b::ReactionComplexElement) - isless(a.speciesid, b.speciesid) + return isless(a.speciesid, b.speciesid) end Base.Sort.defalg(::ReactionComplex) = Base.DEFAULT_UNSTABLE @@ -121,7 +125,7 @@ end # Defines base function overloads for `NetworkProperties`. function Base.show(io::IO, nps::NetworkProperties) - if (nps.conservationmat !== nothing) + return if (nps.conservationmat !== nothing) println(io, "Conserved Equations: ") foreach(eq -> println(io, eq), nps.conservedeqs) println() @@ -130,7 +134,7 @@ end Base.isempty(nps::NetworkProperties) = getfield(nps, :isempty) function Base.setproperty!(nps::NetworkProperties, sym::Symbol, x) (sym !== :isempty) && setfield!(nps, :isempty, false) - setfield!(nps, sym, x) + return setfield!(nps, sym, x) end # Resets computed properties. @@ -163,11 +167,11 @@ function reset!(nps::NetworkProperties{I, V}) where {I, V} # this needs to be last due to setproperty! setting it to false nps.isempty = true - nothing + return nothing end function initialized_conserved(nps::NetworkProperties) - nps.conservedconst !== __UNINITIALIZED_CONSERVED_CONSTS + return nps.conservedconst !== __UNINITIALIZED_CONSERVED_CONSTS end ### ReactionSystem Constructor Functions ### @@ -179,17 +183,17 @@ eqsortby(eq::CatalystEqType) = eq isa Reaction ? 1 : 2 # its condition and affect functions to their respective vectors (`ps` and `us`). # Uses MT.collect_vars! for consistency with other variable discovery in the codebase. function find_event_vars!(ps, us, events::Vector, t) - foreach(event -> find_event_vars!(ps, us, event, t), events) + return foreach(event -> find_event_vars!(ps, us, event, t), events) end # For a single event, adds quantities from its condition and affect expression(s) to `ps` and `us`. # Two dispatches required: events can be given as a MTK callback structure or a Pair of symbolic expressions. function find_event_vars!(ps, us, event::Pair, t) MT.collect_vars!(us, ps, event[1], t) # condition - MT.collect_vars!(us, ps, event[2], t) # affect + return MT.collect_vars!(us, ps, event[2], t) # affect end function find_event_vars!(ps, us, event::MT.AbstractCallback, t) MT.collect_vars!(us, ps, event.conditions, t) - MT.collect_vars!(us, ps, event.affect.affect, t) + return MT.collect_vars!(us, ps, event.affect.affect, t) end # Loops through all jumps, adding all unknowns and parameters found to their respective vectors. @@ -198,6 +202,7 @@ function find_jump_vars!(ps, us, jumps::Vector, t) for jump in jumps MT.collect_vars!(us, ps, jump, t) end + return end # Loops through all tstop expressions, adding encountered unknowns and parameters @@ -207,6 +212,7 @@ function find_tstop_vars!(ps, us, tstops::Vector, t) for ts in tstops MT.collect_vars!(us, ps, unwrap(ts), t) end + return end # Validates that tstop expressions only contain parameters and constants (no unknowns or @@ -236,7 +242,7 @@ function check_tstops(tstops, unknowns, iv) end end end - nothing + return nothing end ### ReactionSystem Structure ### @@ -255,7 +261,8 @@ const reactionsystem_fields = ( :eqs, :rxs, :iv, :sivs, :unknowns, :species, :ps, :var_to_name, :observed, :name, :systems, :bindings, :initial_conditions, :networkproperties, :combinatoric_ratelaws, :continuous_events, - :discrete_events, :tstops, :brownians, :poissonians, :jumps, :metadata, :complete, :parent) + :discrete_events, :tstops, :brownians, :poissonians, :jumps, :metadata, :complete, :parent, +) """ $(TYPEDEF) @@ -379,10 +386,12 @@ struct ReactionSystem{V <: NetworkProperties} <: MT.AbstractSystem parent::Union{Nothing, ReactionSystem} # 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, + function ReactionSystem( + eqs, rxs, iv, sivs, unknowns, spcs, ps, var_to_name, observed, name, systems, bindings, initial_conditions, nps, cls, cevs, devs, tstops, brownians, poissonians, jumps, metadata, complete = false, parent = nothing; - checks::Bool = true, unit_checks::Bool = false) + checks::Bool = true, unit_checks::Bool = false + ) # Structural checks (fast, always on by default). if checks && isempty(sivs) @@ -403,9 +412,10 @@ struct ReactionSystem{V <: NetworkProperties} <: MT.AbstractSystem rs = new{typeof(nps)}( eqs, rxs, iv, sivs, unknowns, spcs, ps, var_to_name, observed, name, systems, bindings, initial_conditions, nps, cls, cevs, - devs, tstops, brownians, poissonians, jumps, metadata, complete, parent) + devs, tstops, brownians, poissonians, jumps, metadata, complete, parent + ) unit_checks && assert_valid_units(rs; info = string("ReactionSystem constructor for ", name)) - rs + return rs end end @@ -422,7 +432,8 @@ end # use the two-argument constructor for auto-discovery from equations. # `unit_checks = true` validates species unit consistency, reaction rate units, and # equation unit balance at construction time. Default is `false`. -function ReactionSystem(eqs, iv, unknowns, ps, brownians = SymbolicT[]; +function ReactionSystem( + eqs, iv, unknowns, ps, brownians = SymbolicT[]; poissonians = SymbolicT[], jumps = JumpType[], observed = Equation[], @@ -440,7 +451,8 @@ function ReactionSystem(eqs, iv, unknowns, ps, brownians = SymbolicT[]; discrete_events = nothing, tstops = [], metadata = MT.MetadataT(), - disable_forbidden_symbol_check = false) + disable_forbidden_symbol_check = false + ) # Error checks name === nothing && @@ -539,11 +551,12 @@ function ReactionSystem(eqs, iv, unknowns, ps, brownians = SymbolicT[]; # got promoted to Num by array promotion in `[param, 5.0]`). tstops′ = Any[Symbolics.value(ts) for ts in tstops] - ReactionSystem( + return ReactionSystem( eqs′, rxs, iv′, sivs′, unknowns′, spcs, ps′, var_to_name, observed, name, systems, bindings, initial_conditions, nps, combinatoric_ratelaws, continuous_events, discrete_events, tstops′, brownians′, poissonians′, jumps′, metadata; - checks, unit_checks) + checks, unit_checks + ) end # Handles that events can be a single event or a vector. @@ -569,18 +582,18 @@ function make_metadata(metadata) end metadata = meta end - metadata = MT.refreshed_metadata(metadata) + return metadata = MT.refreshed_metadata(metadata) end # Two-argument constructor (reactions/equations and time variable). # Calls the `make_ReactionSystem_internal`, which in turn calls the four-argument constructor. function ReactionSystem(rxs::Vector, iv = Catalyst.DEFAULT_IV; kwargs...) - make_ReactionSystem_internal(rxs, iv, [], []; kwargs...) + return make_ReactionSystem_internal(rxs, iv, [], []; kwargs...) end # One-argument constructor. Creates an empty `ReactionSystem` from a time independent variable only. function ReactionSystem(iv; kwargs...) - ReactionSystem(Reaction[], iv, [], []; kwargs...) + return ReactionSystem(Reaction[], iv, [], []; kwargs...) end # Called internally (whether DSL-based or programmatic model creation is used). @@ -589,9 +602,11 @@ end # the model creation) and creates the corresponding vectors. # While species are ordered before variables in the unknowns vector, this ordering is not imposed here, # but carried out at a later stage. -function make_ReactionSystem_internal(rxs_and_eqs::Vector, iv, us_in, ps_in, brownians = SymbolicT[]; +function make_ReactionSystem_internal( + rxs_and_eqs::Vector, iv, us_in, ps_in, brownians = SymbolicT[]; poissonians = SymbolicT[], spatial_ivs = nothing, continuous_events = [], - discrete_events = [], tstops = [], observed = [], jumps = JumpType[], kwargs...) + discrete_events = [], tstops = [], observed = [], jumps = JumpType[], kwargs... + ) # Error if any observables have been declared a species or variable obs_vars = Set(obs_eq.lhs for obs_eq in observed) @@ -665,7 +680,7 @@ function make_ReactionSystem_internal(rxs_and_eqs::Vector, iv, us_in, ps_in, bro if iscall(p) && operation(p) === getindex par = arguments(p)[begin] if MT.symbolic_has_known_size(par) && - all(par[i] in ps for i in eachindex(par)) + all(par[i] in ps for i in eachindex(par)) push!(new_ps, par) else push!(new_ps, p) @@ -678,8 +693,10 @@ function make_ReactionSystem_internal(rxs_and_eqs::Vector, iv, us_in, ps_in, bro # Passes the processed input into the next `ReactionSystem` call. # Note: brownians are passed as the 5th positional argument. - ReactionSystem(fulleqs, t, usv, psv, brownians; poissonians, spatial_ivs, - continuous_events, discrete_events, tstops, observed, jumps, kwargs...) + return ReactionSystem( + fulleqs, t, usv, psv, brownians; poissonians, spatial_ivs, + continuous_events, discrete_events, tstops, observed, jumps, kwargs... + ) end ### Base Function Dispatches ### @@ -703,19 +720,19 @@ end function symbolic_affect_matches(aff1, aff2) (isnothing(aff1) && isnothing(aff2)) && return true (isnothing(aff1) || isnothing(aff2)) && return false - issetequal(aff1.affect, aff2.affect) && issetequal(aff1.discrete_parameters, aff2.discrete_parameters) + return issetequal(aff1.affect, aff2.affect) && issetequal(aff1.discrete_parameters, aff2.discrete_parameters) end # Compare two SymbolicContinuousCallbacks by their conditions and affects. function continuous_event_matches(evt1, evt2) - issetequal(evt1.conditions, evt2.conditions) && + return issetequal(evt1.conditions, evt2.conditions) && symbolic_affect_matches(evt1.affect, evt2.affect) && symbolic_affect_matches(evt1.affect_neg, evt2.affect_neg) end # Compare two SymbolicDiscreteCallbacks by their conditions and affects. function discrete_event_matches(evt1, evt2) - isequal(evt1.conditions, evt2.conditions) && symbolic_affect_matches(evt1.affect, evt2.affect) + return isequal(evt1.conditions, evt2.conditions) && symbolic_affect_matches(evt1.affect, evt2.affect) end # Convenience wrappers for continuous and discrete event comparison. @@ -726,8 +743,10 @@ function debug_comparer(fun, prop1, prop2, propname; debug = false) if fun(prop1, prop2) return true else - debug && println("Comparison was false for property: ", propname, - "\n Found: ", prop1, " vs ", prop2) + debug && println( + "Comparison was false for property: ", propname, + "\n Found: ", prop1, " vs ", prop2 + ) return false end end @@ -750,15 +769,19 @@ Notes: - Pass `debug = true` to print out the field that caused the two systems to be considered different. """ -function isequivalent(rn1::ReactionSystem, rn2::ReactionSystem; ignorenames = true, - debug = false) +function isequivalent( + rn1::ReactionSystem, rn2::ReactionSystem; ignorenames = true, + debug = false + ) # metadata type fields if !ignorenames debug_comparer(==, nameof(rn1), nameof(rn2), "name"; debug) || return false end - debug_comparer(==, get_combinatoric_ratelaws(rn1), get_combinatoric_ratelaws(rn2), - "combinatoric_ratelaws"; debug) || return false + debug_comparer( + ==, get_combinatoric_ratelaws(rn1), get_combinatoric_ratelaws(rn2), + "combinatoric_ratelaws"; debug + ) || return false debug_comparer(==, MT.iscomplete(rn1), MT.iscomplete(rn2), "complete"; debug) || return false @@ -773,19 +796,25 @@ function isequivalent(rn1::ReactionSystem, rn2::ReactionSystem; ignorenames = tr debug_comparer(issetequal, MT.get_bindings(rn1), MT.get_bindings(rn2), "bindings"; debug) || return false debug_comparer( - issetequal, MT.get_initial_conditions(rn1), MT.get_initial_conditions(rn2), "defaults"; debug) || + issetequal, MT.get_initial_conditions(rn1), MT.get_initial_conditions(rn2), "defaults"; debug + ) || return false # equations and reactions debug_comparer( - issetequal, MT.get_observed(rn1), MT.get_observed(rn2), "observed"; debug) || + issetequal, MT.get_observed(rn1), MT.get_observed(rn2), "observed"; debug + ) || return false debug_comparer(issetequal, get_eqs(rn1), get_eqs(rn2), "eqs"; debug) || return false # Use custom event comparison functions to work around MTK issue #3907 - debug_comparer(continuous_events_equal, MT.get_continuous_events(rn1), - MT.get_continuous_events(rn2), "cevents"; debug) || return false - debug_comparer(discrete_events_equal, MT.get_discrete_events(rn1), - MT.get_discrete_events(rn2), "devents"; debug) || return false + debug_comparer( + continuous_events_equal, MT.get_continuous_events(rn1), + MT.get_continuous_events(rn2), "cevents"; debug + ) || return false + debug_comparer( + discrete_events_equal, MT.get_discrete_events(rn1), + MT.get_discrete_events(rn2), "devents"; debug + ) || return false debug_comparer(issetequal, MT.get_tstops(rn1), MT.get_tstops(rn2), "tstops"; debug) || return false @@ -805,12 +834,14 @@ function isequivalent(rn1::ReactionSystem, rn2::ReactionSystem; ignorenames = tr # Use isequivalent recursively for subsystems instead of issetequal (which uses ==). # This is needed because == on ReactionSystems uses object identity. if !systems_are_equivalent(get_systems(rn1), get_systems(rn2); ignorenames, debug) - debug && println("Comparison was false for property: systems", - "\n Found: ", get_systems(rn1), " vs ", get_systems(rn2)) + debug && println( + "Comparison was false for property: systems", + "\n Found: ", get_systems(rn1), " vs ", get_systems(rn2) + ) return false end - true + return true end # Helper for isequivalent: checks if two collections of ReactionSystems are equivalent. @@ -888,7 +919,7 @@ during calls to `convert` of problem constructors. function combinatoric_ratelaws(sys::ReactionSystem) crl = get_combinatoric_ratelaws(sys) subsys = Iterators.filter(s -> s isa ReactionSystem, get_systems(sys)) - mapreduce(combinatoric_ratelaws, |, subsys; init = crl) + return mapreduce(combinatoric_ratelaws, |, subsys; init = crl) end # Gets sub systems that are also reaction systems. @@ -899,14 +930,14 @@ function filter_nonrxsys(network) for sys in systems (sys isa ReactionSystem) && push!(rxsystems, sys) end - rxsystems + return rxsystems end # Special species but which take a set of states and names spaces them according to another # `ReactionSystem`. # Used by `species(network)`. function species(network::ReactionSystem, sts) - [MT.renamespace(network, st) for st in sts] + return [MT.renamespace(network, st) for st in sts] end """ @@ -924,7 +955,7 @@ function species(network) sts = get_species(network) systems = filter_nonrxsys(network) isempty(systems) && return sts - unique([sts; reduce(vcat, map(sys -> species(sys, species(sys)), systems))]) + return unique([sts; reduce(vcat, map(sys -> species(sys, species(sys)), systems))]) end """ @@ -938,7 +969,7 @@ function numspecies(network) for sys in get_systems(network) (sys isa ReactionSystem) && (numspcs += numspecies(sys)) end - numspcs + return numspcs end """ @@ -951,7 +982,7 @@ Notes: - Allocates a new array to store the non-species variables. """ function nonspecies(network) - unknowns(network)[(numspecies(network) + 1):end] + return unknowns(network)[(numspecies(network) + 1):end] end """ @@ -965,7 +996,7 @@ function speciesmap(network) if isempty(nps.speciesmap) nps.speciesmap = Dict(S => i for (i, S) in enumerate(species(network))) end - nps.speciesmap + return nps.speciesmap end # get the non-bc, independent unknown variables and independent species, preserving their @@ -979,7 +1010,7 @@ function get_indep_sts(rs::ReactionSystem, remove_conserved = false) else filter(s -> !isbc(s), sts) end - indepsts, filter(isspecies, indepsts) + return indepsts, filter(isspecies, indepsts) end """ @@ -992,7 +1023,7 @@ function numparams(network) for sys in get_systems(network) nps += numparams(sys) end - nps + return nps end """ @@ -1003,14 +1034,14 @@ parameters that appear within the system to their index within `parameters(network)`. """ function paramsmap(network) - Dict(p => i for (i, p) in enumerate(parameters(network))) + return Dict(p => i for (i, p) in enumerate(parameters(network))) end # used in the next function (`reactions(network)`). function namespace_reactions(network::ReactionSystem) rxs = reactions(network) isempty(rxs) && return Reaction[] - map(rx -> namespace_equation(rx, network), rxs) + return map(rx -> namespace_equation(rx, network), rxs) end """ @@ -1025,7 +1056,7 @@ function reactions(network) rxs = get_rxs(network) systems = filter_nonrxsys(network) isempty(systems) && (return rxs) - [rxs; reduce(vcat, namespace_reactions.(systems); init = Reaction[])] + return [rxs; reduce(vcat, namespace_reactions.(systems); init = Reaction[])] end """ @@ -1039,7 +1070,7 @@ function numreactions(network) for sys in get_systems(network) (sys isa ReactionSystem) && (nr += numreactions(sys)) end - nr + return nr end """ @@ -1049,7 +1080,7 @@ Check if the given `network` has any non-reaction equations such as ODEs or alge equations. """ function has_nonreactions(network) - numreactions(network) != length(equations(network)) + return numreactions(network) != length(equations(network)) end """ @@ -1061,7 +1092,7 @@ Notes: - Allocates a new array to store the non-species variables. """ function nonreactions(network) - equations(network)[(numreactions(network) + 1):end] + return equations(network)[(numreactions(network) + 1):end] end """ @@ -1071,7 +1102,7 @@ Given a [`ReactionSystem`](@ref), returns a vector of the symbolic reaction rates for each reaction. """ function reactionrates(rn) - [r.rate for r in reactions(rn)] + return [r.rate for r in reactions(rn)] end """ @@ -1089,8 +1120,10 @@ function MT.equations(sys::ReactionSystem) eqs = get_eqs(sys) systems = get_systems(sys) if !isempty(systems) - eqs = CatalystEqType[eqs; - reduce(vcat, MT.namespace_equations.(systems); init = CatalystEqType[])] + eqs = CatalystEqType[ + eqs; + reduce(vcat, MT.namespace_equations.(systems); init = CatalystEqType[]) + ] return sort!(eqs; by = eqsortby) end return eqs @@ -1122,7 +1155,7 @@ function MT.complete(sys::ReactionSystem; flatten = true, kwargs...) end sys = newsys end - isdefined(sys, :complete) ? (@set! sys.complete = true) : sys + return isdefined(sys, :complete) ? (@set! sys.complete = true) : sys end ### Network Matrix Representations ### @@ -1138,8 +1171,10 @@ Note: - Note that constant species are not considered substrates, but just components that modify the associated rate law. """ -function substoichmat(::Type{SparseMatrixCSC{T, Int}}, - rn::ReactionSystem) where {T <: Number} +function substoichmat( + ::Type{SparseMatrixCSC{T, Int}}, + rn::ReactionSystem + ) where {T <: Number} Is = Int[] Js = Int[] Vs = T[] @@ -1153,7 +1188,7 @@ function substoichmat(::Type{SparseMatrixCSC{T, Int}}, push!(Vs, stoich[i]) end end - sparse(Is, Js, Vs, numspecies(rn), numreactions(rn)) + return sparse(Is, Js, Vs, numspecies(rn), numreactions(rn)) end function substoichmat(::Type{Matrix{T}}, rn::ReactionSystem) where {T <: Number} @@ -1166,7 +1201,7 @@ function substoichmat(::Type{Matrix{T}}, rn::ReactionSystem) where {T <: Number} smat[smap[sub], k] = stoich[i] end end - smat + return smat end function substoichmat(rn::ReactionSystem; sparse::Bool = false) @@ -1174,7 +1209,7 @@ function substoichmat(rn::ReactionSystem; sparse::Bool = false) T = reduce(promote_type, eltype(rx.substoich) for rx in reactions(rn)) (T == Any) && error("Stoichiometry matrices with symbolic stoichiometry are not supported") - sparse ? substoichmat(SparseMatrixCSC{T, Int}, rn) : substoichmat(Matrix{T}, rn) + return sparse ? substoichmat(SparseMatrixCSC{T, Int}, rn) : substoichmat(Matrix{T}, rn) end """ @@ -1188,8 +1223,10 @@ Note: - Note that constant species are not treated as products, but just components that modify the associated rate law. """ -function prodstoichmat(::Type{SparseMatrixCSC{T, Int}}, - rn::ReactionSystem) where {T <: Number} +function prodstoichmat( + ::Type{SparseMatrixCSC{T, Int}}, + rn::ReactionSystem + ) where {T <: Number} Is = Int[] Js = Int[] Vs = T[] @@ -1203,7 +1240,7 @@ function prodstoichmat(::Type{SparseMatrixCSC{T, Int}}, push!(Vs, stoich[i]) end end - sparse(Is, Js, Vs, numspecies(rn), numreactions(rn)) + return sparse(Is, Js, Vs, numspecies(rn), numreactions(rn)) end function prodstoichmat(::Type{Matrix{T}}, rn::ReactionSystem) where {T <: Number} @@ -1216,7 +1253,7 @@ function prodstoichmat(::Type{Matrix{T}}, rn::ReactionSystem) where {T <: Number pmat[smap[prod], k] = stoich[i] end end - pmat + return pmat end function prodstoichmat(rn::ReactionSystem; sparse = false) @@ -1226,7 +1263,7 @@ function prodstoichmat(rn::ReactionSystem; sparse = false) T = reduce(promote_type, eltype(rx.prodstoich) for rx in reactions(rn)) (T == Any) && error("Stoichiometry matrices with symbolic stoichiometry are not supported") - sparse ? prodstoichmat(SparseMatrixCSC{T, Int}, rn) : prodstoichmat(Matrix{T}, rn) + return sparse ? prodstoichmat(SparseMatrixCSC{T, Int}, rn) : prodstoichmat(Matrix{T}, rn) end # Used in `netstoichmat` function. @@ -1244,8 +1281,10 @@ Notes: - Note that constant species are not treated as reactants, but just components that modify the associated rate law. As such they do not contribute to the net stoichiometry matrix. """ -function netstoichmat(::Type{SparseMatrixCSC{T, Int}}, - rn::ReactionSystem) where {T <: Number} +function netstoichmat( + ::Type{SparseMatrixCSC{T, Int}}, + rn::ReactionSystem + ) where {T <: Number} Is = Int[] Js = Int[] Vs = Vector{T}() @@ -1258,7 +1297,7 @@ function netstoichmat(::Type{SparseMatrixCSC{T, Int}}, push!(Vs, coef) end end - sparse(Is, Js, Vs, numspecies(rn), numreactions(rn)) + return sparse(Is, Js, Vs, numspecies(rn), numreactions(rn)) end function netstoichmat(::Type{Matrix{T}}, rn::ReactionSystem) where {T <: Number} @@ -1270,7 +1309,7 @@ function netstoichmat(::Type{Matrix{T}}, rn::ReactionSystem) where {T <: Number} nmat[smap[spec], k] = coef end end - nmat + return nmat end function netstoichmat(rn::ReactionSystem; sparse = false) @@ -1299,7 +1338,7 @@ function netstoichmat(rn::ReactionSystem; sparse = false) nps.netstoichmat = nsmat end - nsmat + return nsmat end ### General `ReactionSystem`-specific Functions ### @@ -1309,7 +1348,7 @@ end # certain functionalities which would break if the `ReactionSystem` structure is updated without # also updating these functionalities. function reactionsystem_uptodate_check() - if fieldnames(ReactionSystem) != reactionsystem_fields + return if fieldnames(ReactionSystem) != reactionsystem_fields @warn "The `ReactionSystem` structure have been modified without this being taken into account in the functionality you are attempting to use. Please report this at https://github.com/SciML/Catalyst.jl/issues. Proceed with caution, as there might be errors in whichever functionality you are attempting to use." end end @@ -1322,7 +1361,7 @@ properties need to be recalculated for some reason. """ function reset_networkproperties!(rn::ReactionSystem) reset!(get_networkproperties(rn)) - nothing + return nothing end """ @@ -1367,7 +1406,7 @@ end See documentation for [`dependents`](@ref). """ function dependants(rx, network) - dependents(rx, network) + return dependents(rx, network) end """ @@ -1426,7 +1465,7 @@ end function set_default_metadata(rs::ReactionSystem; default_reaction_metadata = []) # Updates reaction metadata for reactions in this specific system. function eqtransform(eq) - eq isa Reaction ? set_default_metadata(eq, default_reaction_metadata) : eq + return eq isa Reaction ? set_default_metadata(eq, default_reaction_metadata) : eq end updated_equations = map(eqtransform, get_eqs(rs)) @set! rs.eqs = updated_equations @@ -1443,7 +1482,8 @@ function set_default_metadata(rs::ReactionSystem; default_reaction_metadata = [] ns_sps = Iterators.filter(Catalyst.isspecies, ns_syms) ns_vs = Iterators.filter( sym -> !Catalyst.isspecies(sym) && - !MT.isparameter(sym), ns_syms) + !MT.isparameter(sym), ns_syms + ) # Adds parameters, species, and variables to the `ReactionSystem`. @set! rs.ps = union(get_ps(rs), ns_ps) sps_new = union(get_species(rs), ns_sps) @@ -1466,7 +1506,8 @@ end # For a `Reaction`, adds missing default metadata values. Equations are passed back unmodified. function set_default_metadata(rx::Reaction, default_metadata) missing_metadata = filter( - md -> !in(md[1], entry[1] for entry in rx.metadata), default_metadata) + md -> !in(md[1], entry[1] for entry in rx.metadata), default_metadata + ) updated_metadata = vcat(rx.metadata, missing_metadata) updated_metadata = convert(Vector{Pair{Symbol, Any}}, updated_metadata) return @set rx.metadata = updated_metadata @@ -1486,7 +1527,8 @@ Arguments: """ function set_default_noise_scaling(rs::ReactionSystem, noise_scaling) return remake_ReactionSystem_internal( - rs, default_reaction_metadata = [:noise_scaling => noise_scaling]) + rs, default_reaction_metadata = [:noise_scaling => noise_scaling] + ) end ### ReactionSystem Composing & Hierarchical Modelling ### @@ -1498,7 +1540,7 @@ Construct an empty [`ReactionSystem`](@ref). `iv` is the independent variable, usually time, and `name` is the name to give the `ReactionSystem`. """ function make_empty_network(; iv = DEFAULT_IV, name = gensym(:ReactionSystem)) - ReactionSystem(Reaction[], iv, SymbolicT[], SymbolicT[]; name = name) + return ReactionSystem(Reaction[], iv, SymbolicT[], SymbolicT[]; name = name) end # Checks if a system is an allowed subsystem. @@ -1531,7 +1573,8 @@ function MT.flatten(rs::ReactionSystem; name = nameof(rs)) # Note: brownians are 5th positional arg; poissonians and jumps are kwargs. # MT.brownians(rs), MT.poissonians(rs), and MT.jumps(rs) are recursive accessors # that collect from all subsystems. - ReactionSystem(equations(rs), get_iv(rs), unknowns(rs), parameters(rs), MT.brownians(rs); + return ReactionSystem( + equations(rs), get_iv(rs), unknowns(rs), parameters(rs), MT.brownians(rs); poissonians = MT.poissonians(rs), jumps = MT.jumps(rs), observed = MT.observed(rs), @@ -1544,14 +1587,15 @@ function MT.flatten(rs::ReactionSystem; name = nameof(rs)) continuous_events = MT.continuous_events(rs), discrete_events = MT.discrete_events(rs), tstops = MT.symbolic_tstops(rs), - metadata = MT.get_metadata(rs)) + metadata = MT.get_metadata(rs) + ) end function complete_check(sys, method) if MT.iscomplete(sys) error("$method with one or more `ReactionSystem`s requires systems to not be marked complete, but system: $(MT.get_name(sys)) is marked complete.") end - nothing + return nothing end """ @@ -1608,8 +1652,10 @@ Notes: - Returns a new `ReactionSystem` and does not modify `rs`. - By default, the new `ReactionSystem` will have the same name as `sys`. """ -function MT.extend(sys::ReactionSystem, rs::ReactionSystem; - name::Symbol = nameof(sys)) +function MT.extend( + sys::ReactionSystem, rs::ReactionSystem; + name::Symbol = nameof(sys) + ) complete_check(sys, "MT.extend") complete_check(rs, "MT.extend") @@ -1630,7 +1676,7 @@ function MT.extend(sys::ReactionSystem, rs::ReactionSystem; # ReactionSystem specific properties combinatoric_ratelaws = Catalyst.get_combinatoric_ratelaws(sys) | - Catalyst.get_combinatoric_ratelaws(rs) + Catalyst.get_combinatoric_ratelaws(rs) sivs = union(get_sivs(sys), get_sivs(rs)) # Union brownians, poissonians, and jumps from both systems @@ -1638,7 +1684,8 @@ function MT.extend(sys::ReactionSystem, rs::ReactionSystem; new_poissonians = union(MT.get_poissonians(rs), MT.get_poissonians(sys)) new_jumps = union(MT.get_jumps(rs), MT.get_jumps(sys)) - ReactionSystem(eqs, t, sts, ps, collect(new_brownians); + return ReactionSystem( + eqs, t, sts, ps, collect(new_brownians); poissonians = collect(new_poissonians), jumps = collect(new_jumps), observed = obs, @@ -1651,7 +1698,8 @@ function MT.extend(sys::ReactionSystem, rs::ReactionSystem; spatial_ivs = sivs, continuous_events, discrete_events, - tstops) + tstops + ) end ### Units Handling ### @@ -1712,8 +1760,12 @@ function unit_validation_report(rs::ReactionSystem; info::String = "") su = catalyst_get_unit(spec) if !_units_match(su, specunits) validated = false - push!(issues, UnitValidationIssue(:species_unit_mismatch, string(spec), - specunits, su, "Species unit mismatch")) + push!( + issues, UnitValidationIssue( + :species_unit_mismatch, string(spec), + specunits, su, "Species unit mismatch" + ) + ) end end timeunits = catalyst_get_unit(get_iv(rs)) @@ -1733,17 +1785,25 @@ function unit_validation_report(rs::ReactionSystem; info::String = "") # Symbolic stoichiometry cannot be used in dimensional analysis. if !rx.only_use_rate && any(s -> !(s isa Number), rx.substoich) validated = false - push!(issues, UnitValidationIssue(:symbolic_stoichiometry, - string(rx), nothing, nothing, - "Symbolic stoichiometry is not supported for unit validation")) + push!( + issues, UnitValidationIssue( + :symbolic_stoichiometry, + string(rx), nothing, nothing, + "Symbolic stoichiometry is not supported for unit validation" + ) + ) continue end # Symbolic exponents on unitful bases have indeterminate units. if _has_symbolic_unitful_pow(rx.rate) validated = false - push!(issues, UnitValidationIssue(:symbolic_exponent, - string(rx), nothing, nothing, - "Symbolic exponent on unitful base is not supported for unit validation")) + push!( + issues, UnitValidationIssue( + :symbolic_exponent, + string(rx), nothing, nothing, + "Symbolic exponent on unitful base is not supported for unit validation" + ) + ) continue end rxunits = catalyst_get_unit(rx.rate) @@ -1754,8 +1814,12 @@ function unit_validation_report(rs::ReactionSystem; info::String = "") end if !_units_match(rxunits, rateunits) validated = false - push!(issues, UnitValidationIssue(:reaction_rate_unit_mismatch, - string(rx), rateunits, rxunits, "Reaction rate unit mismatch")) + push!( + issues, UnitValidationIssue( + :reaction_rate_unit_mismatch, + string(rx), rateunits, rxunits, "Reaction rate unit mismatch" + ) + ) end end end @@ -1770,7 +1834,7 @@ function unit_validation_report(rs::ReactionSystem; info::String = "") validated &= _validate_equation(eq; noise_units, issues, warn = false) end - UnitValidationReport(validated, issues) + return UnitValidationReport(validated, issues) end """ diff --git a/src/reactionsystem_conversions.jl b/src/reactionsystem_conversions.jl index cba5a24b5b..0d97d7d8b7 100644 --- a/src/reactionsystem_conversions.jl +++ b/src/reactionsystem_conversions.jl @@ -40,7 +40,7 @@ function oderatelaw(rx; combinatoric_ratelaw = true, expand_catalyst_funs = true end combinatoric_ratelaw && (!isequal(coef, one(coef))) && (rl /= coef) end - rl + return rl end # Function returning `true` for species which shouldn't change from the reactions, @@ -50,7 +50,7 @@ drop_dynamics(s) = isconstant(s) || isbc(s) || (!isspecies(s)) # Compute signed stoichiometry term: stoich * expr, optimized for common cases. # Used in both ODE RHS assembly and noise coefficient computation. function _signed_stoich_term(stoich, expr) - if stoich isa SymbolicT + return if stoich isa SymbolicT stoich * expr else signed_expr = (stoich > zero(stoich)) ? expr : -expr @@ -58,9 +58,11 @@ function _signed_stoich_term(stoich, expr) end end -function assemble_oderhs(rs, ispcs; combinatoric_ratelaws = true, remove_conserved = false, +function assemble_oderhs( + rs, ispcs; combinatoric_ratelaws = true, remove_conserved = false, physical_scales = nothing, expand_catalyst_funs = true, - use_jump_ratelaws = false) + use_jump_ratelaws = false + ) nps = get_networkproperties(rs) species_to_idx = Dict(x => i for (i, x) in enumerate(ispcs)) rhsvec = Any[0 for _ in ispcs] @@ -72,15 +74,21 @@ function assemble_oderhs(rs, ispcs; combinatoric_ratelaws = true, remove_conserv for (rxidx, rx) in enumerate(get_rxs(rs)) # check this reaction should contribute to drift (ODE and SDE reactions do) - !((physical_scales === nothing) || - (physical_scales[rxidx] in (PhysicalScale.ODE, PhysicalScale.SDE))) && continue + !( + (physical_scales === nothing) || + (physical_scales[rxidx] in (PhysicalScale.ODE, PhysicalScale.SDE)) + ) && continue rl = if use_jump_ratelaws - jumpratelaw(rx; combinatoric_ratelaw = combinatoric_ratelaws, - expand_catalyst_funs) + jumpratelaw( + rx; combinatoric_ratelaw = combinatoric_ratelaws, + expand_catalyst_funs + ) else - oderatelaw(rx; combinatoric_ratelaw = combinatoric_ratelaws, - expand_catalyst_funs) + oderatelaw( + rx; combinatoric_ratelaw = combinatoric_ratelaws, + expand_catalyst_funs + ) end remove_conserved && (rl = substitute(rl, depspec_submap)) for (spec, stoich) in rx.netstoich @@ -104,28 +112,34 @@ function assemble_oderhs(rs, ispcs; combinatoric_ratelaws = true, remove_conserv else Δspec = isone(abs(stoich)) ? rl : abs(stoich) * rl rhsvec[i] = (stoich > zero(stoich)) ? (rhsvec[i] + Δspec) : - (rhsvec[i] - Δspec) + (rhsvec[i] - Δspec) end end end end - rhsvec + return rhsvec end -function assemble_drift(rs, ispcs; combinatoric_ratelaws = true, as_odes = true, +function assemble_drift( + rs, ispcs; combinatoric_ratelaws = true, as_odes = true, include_zero_odes = true, remove_conserved = false, physical_scales = nothing, - expand_catalyst_funs = true, use_jump_ratelaws = false) - rhsvec = assemble_oderhs(rs, ispcs; combinatoric_ratelaws, remove_conserved, - physical_scales, expand_catalyst_funs, use_jump_ratelaws) + expand_catalyst_funs = true, use_jump_ratelaws = false + ) + rhsvec = assemble_oderhs( + rs, ispcs; combinatoric_ratelaws, remove_conserved, + physical_scales, expand_catalyst_funs, use_jump_ratelaws + ) if as_odes D = Differential(get_iv(rs)) - eqs = [Equation(D(x), rhs) - for (x, rhs) in zip(ispcs, rhsvec) if (include_zero_odes || (!_iszero(rhs)))] + eqs = [ + Equation(D(x), rhs) + for (x, rhs) in zip(ispcs, rhsvec) if (include_zero_odes || (!_iszero(rhs))) + ] else eqs = [Equation(0, rhs) for rhs in rhsvec if (include_zero_odes || (!_iszero(rhs)))] end - eqs + return eqs end """ @@ -137,9 +151,11 @@ The noise coefficient is `stoich * sqrt(|ratelaw|) * [noise_scaling]`. This is a shared helper used by both `assemble_diffusion` (legacy noise matrix path) and `add_noise_to_rhs!` (Brownian-based path) to avoid code duplication. """ -function foreach_noise_coeff(f, rx, species_to_idx, nps, depspec_submap; +function foreach_noise_coeff( + f, rx, species_to_idx, nps, depspec_submap; combinatoric_ratelaws = true, remove_conserved = false, - expand_catalyst_funs = true, use_jump_ratelaws = false) + expand_catalyst_funs = true, use_jump_ratelaws = false + ) rl = if use_jump_ratelaws jumpratelaw(rx; combinatoric_ratelaw = combinatoric_ratelaws, expand_catalyst_funs) else @@ -153,19 +169,24 @@ function foreach_noise_coeff(f, rx, species_to_idx, nps, depspec_submap; remove_conserved && (spec in nps.depspecs) && continue drop_dynamics(spec) && continue if !haskey(species_to_idx, spec) - error("Species $spec appears in reaction $rx but is not in the independent species list. " * - "This indicates a problem with the reaction system structure.") + error( + "Species $spec appears in reaction $rx but is not in the independent species list. " * + "This indicates a problem with the reaction system structure." + ) end i = species_to_idx[spec] coef = _signed_stoich_term(stoich, rlsqrt) f(i, coef) end + return end # this doesn't work with constraint equations currently -function assemble_diffusion(rs, sts, ispcs; combinatoric_ratelaws = true, +function assemble_diffusion( + rs, sts, ispcs; combinatoric_ratelaws = true, remove_conserved = false, expand_catalyst_funs = true, - use_jump_ratelaws = false) + use_jump_ratelaws = false + ) # as BC species should ultimately get an equation, we include them in the noise matrix num_bcsts = count(isbc, get_unknowns(rs)) @@ -181,13 +202,15 @@ function assemble_diffusion(rs, sts, ispcs; combinatoric_ratelaws = true, end for (j, rx) in enumerate(get_rxs(rs)) - foreach_noise_coeff(rx, species_to_idx, nps, depspec_submap; - combinatoric_ratelaws, remove_conserved, expand_catalyst_funs, - use_jump_ratelaws) do i, coef + foreach_noise_coeff( + rx, species_to_idx, nps, depspec_submap; + combinatoric_ratelaws, remove_conserved, expand_catalyst_funs, + use_jump_ratelaws + ) do i, coef eqs[i, j] = coef end end - eqs + return eqs end ### Brownian Noise Helpers ### @@ -210,7 +233,7 @@ function create_sde_brownians(scales) push!(brownian_vars, B) push!(brownian_map, rx_idx => B) end - brownian_vars, brownian_map + return brownian_vars, brownian_map end """ @@ -220,9 +243,11 @@ Mutate the RHS vector `rhsvec` to add Brownian noise terms for each SDE-scale reaction. For each such reaction, adds `stoich * sqrt(|ratelaw|) * [noise_scaling] * B_j` to the corresponding species' RHS entry. """ -function add_noise_to_rhs!(rhsvec, rs, ispcs, brownian_map; +function add_noise_to_rhs!( + rhsvec, rs, ispcs, brownian_map; combinatoric_ratelaws = true, remove_conserved = false, - expand_catalyst_funs = true, use_jump_ratelaws = false) + expand_catalyst_funs = true, use_jump_ratelaws = false + ) nps = get_networkproperties(rs) species_to_idx = Dict(x => i for (i, x) in enumerate(ispcs)) depspec_submap = if remove_conserved @@ -233,12 +258,15 @@ function add_noise_to_rhs!(rhsvec, rs, ispcs, brownian_map; for (rx_idx, B_j) in brownian_map rx = get_rxs(rs)[rx_idx] - foreach_noise_coeff(rx, species_to_idx, nps, depspec_submap; - combinatoric_ratelaws, remove_conserved, expand_catalyst_funs, - use_jump_ratelaws) do i, coef + foreach_noise_coeff( + rx, species_to_idx, nps, depspec_submap; + combinatoric_ratelaws, remove_conserved, expand_catalyst_funs, + use_jump_ratelaws + ) do i, coef rhsvec[i] += coef * B_j end end + return end ### Jumps Assembly ### @@ -279,7 +307,7 @@ function jumpratelaw(rx; combinatoric_ratelaw = true, expand_catalyst_funs = tru s = substrates[i] if stoich isa SymbolicT rl *= combinatoric_ratelaw ? binomial(s, stoich) : - factorial(s) / factorial(s - stoich) + factorial(s) / factorial(s - stoich) else rl *= s isone(stoich) && continue @@ -291,7 +319,7 @@ function jumpratelaw(rx; combinatoric_ratelaw = true, expand_catalyst_funs = tru end combinatoric_ratelaw && !isequal(coef, one(coef)) && (rl /= coef) end - rl + return rl end # if haveivdep=false then time dependent rates will still be classified as mass action @@ -327,9 +355,11 @@ Notes: symbolic variables/terms or floating point numbers for stoichiometric coefficients. """ -function ismassaction(rx, rs; rxvars = get_variables(rx.rate), +function ismassaction( + rx, rs; rxvars = get_variables(rx.rate), haveivdep::Union{Nothing, Bool} = nothing, - unknownset = Set(get_unknowns(rs)), ivset = nothing) + unknownset = Set(get_unknowns(rs)), ivset = nothing + ) # we define non-integer (i.e. float or symbolic) stoich to be non-mass action ((eltype(rx.substoich) <: Integer) && (eltype(rx.prodstoich) <: Integer)) || @@ -389,8 +419,10 @@ end isempty(net_stoch) && error("$rx has no net stoichiometry change once accounting for constant and boundary condition species. This is not supported.") - MassActionJump(Num(rate), reactant_stoch, net_stoch, scale_rates = false, - useiszero = false) + return MassActionJump( + Num(rate), reactant_stoch, net_stoch, scale_rates = false, + useiszero = false + ) end # recursively visit each neighbor's rooted tree and mark everything in it as vrj @@ -403,7 +435,7 @@ function dfs_mark!(isvrjvec, visited, depgraph, i) dfs_mark!(isvrjvec, visited, depgraph, nhbr) end end - nothing + return nothing end # get_depgraph(rs)[i] is the list of reactions with rates depending on species changed by @@ -412,7 +444,7 @@ function get_depgraph(rs) eqs = reactions(rs) jdeps = asgraph(rs; eqs) vdeps = variable_dependencies(rs; eqs) - eqeq_dependencies(jdeps, vdeps).fadjlist + return eqeq_dependencies(jdeps, vdeps).fadjlist end # note that reactions that are not constant rate are treated as vrjs here @@ -452,11 +484,13 @@ function classify_vrjs(rs, physcales) end end - isvrjvec + return isvrjvec end -function assemble_jumps(rs; combinatoric_ratelaws = true, physical_scales = nothing, - expand_catalyst_funs = true, save_positions = (true, true)) +function assemble_jumps( + rs; combinatoric_ratelaws = true, physical_scales = nothing, + expand_catalyst_funs = true, save_positions = (true, true) + ) meqs = MassActionJump[] ceqs = ConstantRateJump[] veqs = VariableRateJump[] @@ -488,8 +522,10 @@ function assemble_jumps(rs; combinatoric_ratelaws = true, physical_scales = noth if (!isvrj) && ismassaction(rx, rs; rxvars, haveivdep = false, unknownset) push!(meqs, makemajump(rx; combinatoric_ratelaw = combinatoric_ratelaws)) else - rl = jumpratelaw(rx; combinatoric_ratelaw = combinatoric_ratelaws, - expand_catalyst_funs) + rl = jumpratelaw( + rx; combinatoric_ratelaw = combinatoric_ratelaws, + expand_catalyst_funs + ) affect = Vector{Equation}() for (spec, stoich) in rx.netstoich # don't change species that are constant or BCs @@ -502,15 +538,17 @@ function assemble_jumps(rs; combinatoric_ratelaws = true, physical_scales = noth end end end - reduce(vcat, (meqs, ceqs, veqs); init = JumpType[]) + return reduce(vcat, (meqs, ceqs, veqs); init = JumpType[]) end ### Equation Coupling ### # merge constraint components with the ReactionSystem components # also handles removing BC and constant species -function addconstraints!(eqs, rs::ReactionSystem, ists, ispcs; remove_conserved = false, - compute_cl_initeqs = false, include_cl_as_eqs = false) +function addconstraints!( + eqs, rs::ReactionSystem, ists, ispcs; remove_conserved = false, + compute_cl_initeqs = false, include_cl_as_eqs = false + ) # if there are BC species, put them after the independent species rssts = get_unknowns(rs) sts = any(isbc, rssts) ? vcat(ists, filter(isbc, rssts)) : ists @@ -548,24 +586,24 @@ function addconstraints!(eqs, rs::ReactionSystem, ists, ispcs; remove_conserved if !isempty(ceqs) if remove_conserved @info """ - Be careful mixing ODEs or algebraic equations and elimination of - conservation laws. Catalyst does not check that the conserved equations - still hold for the final coupled system of equations. Consider using - `remove_conserved = false` and instead calling - ModelingToolkitBase.mtkcompile to simplify any generated system. - """ + Be careful mixing ODEs or algebraic equations and elimination of + conservation laws. Catalyst does not check that the conserved equations + still hold for the final coupled system of equations. Consider using + `remove_conserved = false` and instead calling + ModelingToolkitBase.mtkcompile to simplify any generated system. + """ end append!(eqs, ceqs) end - eqs, sts, ps, obs, ics, initeqs + return eqs, sts, ps, obs, ics, initeqs end ### Utility ### # Throws an error when attempting to convert a spatial system to an unsupported type. function spatial_convert_err(rs::ReactionSystem, systype) - isspatial(rs) && error("Conversion to $systype is not supported for spatial networks.") + return isspatial(rs) && error("Conversion to $systype is not supported for spatial networks.") end # Finds and differentials in an expression, and sets these to 0. @@ -618,7 +656,8 @@ reaction's assigned [`PhysicalScale`](@ref). 3. `default_scale` kwarg (fallback for `Auto`) 4. If still `Auto` after all three → error """ -function hybrid_model(rs::ReactionSystem; +function hybrid_model( + rs::ReactionSystem; name = nameof(rs), physical_scales = nothing, default_scale = PhysicalScale.Auto, @@ -631,7 +670,8 @@ function hybrid_model(rs::ReactionSystem; checks = false, initial_conditions = Dict(), use_jump_ratelaws = false, - kwargs...) + kwargs... + ) # Error checks. iscomplete(rs) || error(COMPLETENESS_ERROR) @@ -682,15 +722,19 @@ function hybrid_model(rs::ReactionSystem; brownian_vars = SymbolicT[] if has_continuous - rhsvec = assemble_oderhs(flatrs, ispcs; combinatoric_ratelaws, remove_conserved, - physical_scales = scales, expand_catalyst_funs, use_jump_ratelaws) + rhsvec = assemble_oderhs( + flatrs, ispcs; combinatoric_ratelaws, remove_conserved, + physical_scales = scales, expand_catalyst_funs, use_jump_ratelaws + ) # Add Brownian noise terms for SDE-scale reactions. if has_rxn_sde rxn_brownian_vars, brownian_map = create_sde_brownians(scales) - add_noise_to_rhs!(rhsvec, flatrs, ispcs, brownian_map; + add_noise_to_rhs!( + rhsvec, flatrs, ispcs, brownian_map; combinatoric_ratelaws, remove_conserved, expand_catalyst_funs, - use_jump_ratelaws) + use_jump_ratelaws + ) # Merge reaction-generated brownians with user-provided brownians. brownian_vars = unique(vcat(rxn_brownian_vars, user_brownians)) else @@ -700,16 +744,20 @@ function hybrid_model(rs::ReactionSystem; # Convert RHS vector to D(x) ~ rhs equations. D = Differential(get_iv(flatrs)) - eqs = [Equation(D(x), rhs) - for (x, rhs) in zip(ispcs, rhsvec) - if (include_zero_odes || (!_iszero(rhs)))] + eqs = [ + Equation(D(x), rhs) + for (x, rhs) in zip(ispcs, rhsvec) + if (include_zero_odes || (!_iszero(rhs))) + ] end # --- Build jumps (Jump + VariableRateJump reactions only) --- rxn_jumps = JumpType[] if has_rxn_jump - rxn_jumps = assemble_jumps(flatrs; combinatoric_ratelaws, expand_catalyst_funs, - physical_scales = scales, save_positions) + rxn_jumps = assemble_jumps( + flatrs; combinatoric_ratelaws, expand_catalyst_funs, + physical_scales = scales, save_positions + ) end # Merge reaction-generated jumps with user-provided jumps. jumps = vcat(rxn_jumps, user_jumps) @@ -729,7 +777,8 @@ function hybrid_model(rs::ReactionSystem; # --- Construct unified System --- # Note: brownians is a positional arg (5th) in the System constructor. - MT.System(eqs, get_iv(flatrs), us, ps, brownian_vars; + return MT.System( + eqs, get_iv(flatrs), us, ps, brownian_vars; poissonians = user_poissonians, jumps, observed = obs, @@ -741,7 +790,8 @@ function hybrid_model(rs::ReactionSystem; discrete_events = MT.get_discrete_events(flatrs), tstops = MT.get_tstops(flatrs), metadata = MT.get_metadata(rs), - kwargs...) + kwargs... + ) end """ @@ -767,35 +817,45 @@ Keyword args and default values: (binomials) instead of the ODE rate law (powers). This gives the mathematically correct propensities from the CME when species populations are integers. """ -function ode_model(rs::ReactionSystem; name = nameof(rs), +function ode_model( + rs::ReactionSystem; name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), include_zero_odes = true, remove_conserved = false, checks = false, initial_conditions = Dict(), expand_catalyst_funs = true, use_jump_ratelaws = false, - kwargs...) + kwargs... + ) # Error if ReactionSystem has coupled brownians or jumps. flatrs = Catalyst.flatten(rs) if !isempty(MT.brownians(flatrs)) - error("""Cannot convert ReactionSystem with coupled brownian noise to a pure ODE system. - Found brownians: $(MT.brownians(flatrs)) - Use `SDEProblem` or `HybridProblem` instead.""") + error( + """Cannot convert ReactionSystem with coupled brownian noise to a pure ODE system. + Found brownians: $(MT.brownians(flatrs)) + Use `SDEProblem` or `HybridProblem` instead.""" + ) end if !isempty(MT.jumps(flatrs)) - error("""Cannot convert ReactionSystem with coupled jumps to a pure ODE system. - Found $(length(MT.jumps(flatrs))) jump(s). - Use `JumpProblem` or `HybridProblem` instead.""") + error( + """Cannot convert ReactionSystem with coupled jumps to a pure ODE system. + Found $(length(MT.jumps(flatrs))) jump(s). + Use `JumpProblem` or `HybridProblem` instead.""" + ) end if !isempty(MT.poissonians(flatrs)) - error("""Cannot convert ReactionSystem with poissonians to a pure ODE system. - Found poissonians: $(MT.poissonians(flatrs)) - Use `JumpProblem` or `HybridProblem` instead.""") + error( + """Cannot convert ReactionSystem with poissonians to a pure ODE system. + Found poissonians: $(MT.poissonians(flatrs)) + Use `JumpProblem` or `HybridProblem` instead.""" + ) end - hybrid_model(rs; + return hybrid_model( + rs; _override_all_scales = PhysicalScale.ODE, name, combinatoric_ratelaws, include_zero_odes, remove_conserved, checks, initial_conditions, - expand_catalyst_funs, use_jump_ratelaws, kwargs...) + expand_catalyst_funs, use_jump_ratelaws, kwargs... + ) end function is_autonomous_error(iv) @@ -809,7 +869,7 @@ end # Returns `nothing` for non-species or already-BC nodes (no replacement by `replacenode`). function _mark_species_bc(node) ((node isa SymbolicT) && isspecies(node) && !isbc(node)) || return nothing - MT.setmetadata(node, VariableBCSpecies, true) + return MT.setmetadata(node, VariableBCSpecies, true) end """ @@ -825,11 +885,13 @@ Keyword arguments override the defaults extracted from `sys`: - `checks`: defaults to `false` - `disable_forbidden_symbol_check`: defaults to `false` """ -function system_to_reactionsystem(sys::MT.AbstractSystem; +function system_to_reactionsystem( + sys::MT.AbstractSystem; name = nameof(sys), combinatoric_ratelaws = true, checks = false, - disable_forbidden_symbol_check = false) + disable_forbidden_symbol_check = false + ) eqs = equations(sys) iv = get_iv(sys) us = unknowns(sys) @@ -849,12 +911,17 @@ function system_to_reactionsystem(sys::MT.AbstractSystem; # The ReactionSystem constructor rejects differentials of non-BC species, but ODE/SDE # equations naturally contain D(species). BC metadata is added to both the unknowns # vector and inside equation expressions (which hold separate symbolic references). - us = [isspecies(u) && !isbc(u) ? MT.setmetadata(u, VariableBCSpecies, true) : u - for u in us] - eqs = [replacenode(eq.lhs, _mark_species_bc) ~ replacenode(eq.rhs, _mark_species_bc) - for eq in eqs] - - ReactionSystem(eqs, iv, us, ps, browns; + us = [ + isspecies(u) && !isbc(u) ? MT.setmetadata(u, VariableBCSpecies, true) : u + for u in us + ] + eqs = [ + replacenode(eq.lhs, _mark_species_bc) ~ replacenode(eq.rhs, _mark_species_bc) + for eq in eqs + ] + + return ReactionSystem( + eqs, iv, us, ps, browns; poissonians = poiss, jumps = jmps, observed = obs, @@ -867,7 +934,8 @@ function system_to_reactionsystem(sys::MT.AbstractSystem; tstops = tstps, metadata = meta, checks, - disable_forbidden_symbol_check) + disable_forbidden_symbol_check + ) end """ @@ -894,11 +962,13 @@ Keyword args and default values: explicitly include the conservation laws as system equations, rather than eliminating them as observables. Primarily used by internal Catalyst functions. """ -function ss_ode_model(rs::ReactionSystem; name = nameof(rs), +function ss_ode_model( + rs::ReactionSystem; name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), remove_conserved = false, checks = false, initial_conditions = Dict(), all_differentials_permitted = false, expand_catalyst_funs = true, - include_cl_as_eqs = false, kwargs...) + include_cl_as_eqs = false, kwargs... + ) # Error checks. iscomplete(rs) || error(COMPLETENESS_ERROR) spatial_convert_err(rs::ReactionSystem, System) @@ -908,10 +978,14 @@ function ss_ode_model(rs::ReactionSystem; name = nameof(rs), fullrs = Catalyst.flatten(rs) remove_conserved && conservationlaws(fullrs) ists, ispcs = get_indep_sts(fullrs, (remove_conserved && !include_cl_as_eqs)) - eqs = assemble_drift(fullrs, ispcs; combinatoric_ratelaws, remove_conserved, - as_odes = false, include_zero_odes = false, expand_catalyst_funs) - eqs, us, ps, obs, ics, initeqs = addconstraints!(eqs, fullrs, ists, ispcs; - remove_conserved, compute_cl_initeqs = !include_cl_as_eqs, include_cl_as_eqs) + eqs = assemble_drift( + fullrs, ispcs; combinatoric_ratelaws, remove_conserved, + as_odes = false, include_zero_odes = false, expand_catalyst_funs + ) + eqs, us, ps, obs, ics, initeqs = addconstraints!( + eqs, fullrs, ists, ispcs; + remove_conserved, compute_cl_initeqs = !include_cl_as_eqs, include_cl_as_eqs + ) # Comoutes the correct initial conditions and bindings. initial_conditions, bindings = MT.convert_bindings_for_time_independent_system(rs) @@ -921,14 +995,16 @@ function ss_ode_model(rs::ReactionSystem; name = nameof(rs), # Next, sets all differential terms to `0`. all_differentials_permitted || nonlinear_convert_differentials_check(rs) eqs = Equation[remove_diffs(eq.lhs) ~ remove_diffs(eq.rhs) for eq in eqs] - System(eqs, us, ps; + return System( + eqs, us, ps; name, observed = obs, initialization_eqs = initeqs, bindings, initial_conditions, checks, metadata = MT.get_metadata(rs), - kwargs...) + kwargs... + ) end # Ideally, when `ReactionSystem`s are converted to nonlinear systems, any coupled ODEs should be @@ -944,11 +1020,12 @@ function nonlinear_convert_differentials_check(rs::ReactionSystem) # If the content of the differential is not a variable (and nothing more). # If either of this is a case, throws the warning. if hasnode(Symbolics.is_derivative, eq.rhs) || - !Symbolics.is_derivative(eq.lhs) || - !isequal(Symbolics.operation(eq.lhs), Differential(get_iv(rs))) || - (length(arguments(eq.lhs)) != 1) || - !any(isequal(arguments(eq.lhs)[1]), nonspecies(rs)) - error("You are attempting to convert a `ReactionSystem` coupled with differential equations to a nonlinear system. However, some of these differentials are not of the form `D(x) ~ ...` where: + !Symbolics.is_derivative(eq.lhs) || + !isequal(Symbolics.operation(eq.lhs), Differential(get_iv(rs))) || + (length(arguments(eq.lhs)) != 1) || + !any(isequal(arguments(eq.lhs)[1]), nonspecies(rs)) + error( + "You are attempting to convert a `ReactionSystem` coupled with differential equations to a nonlinear system. However, some of these differentials are not of the form `D(x) ~ ...` where: (1) The left-hand side is a differential of a single variable with respect to the time independent variable, and (2) The right-hand side does not contain any differentials. This is generally not permitted. @@ -958,6 +1035,7 @@ function nonlinear_convert_differentials_check(rs::ReactionSystem) ) end end + return end """ @@ -986,27 +1064,33 @@ Keyword args and default values: jump/stochastic rate law (binomials) instead of the ODE rate law (powers). This gives the mathematically correct CLE derived from the CME when species populations are integers. """ -function sde_model(rs::ReactionSystem; +function sde_model( + rs::ReactionSystem; name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), include_zero_odes = true, checks = false, remove_conserved = false, initial_conditions = Dict(), expand_catalyst_funs = true, use_legacy_noise = true, use_jump_ratelaws = false, - kwargs...) + kwargs... + ) # Flatten once upfront and check for constraints. flatrs = Catalyst.flatten(rs) # Error if ReactionSystem has coupled jumps or poissonians (SDE + jumps hybrid not supported yet). if !isempty(MT.jumps(flatrs)) - error("""Cannot convert ReactionSystem with coupled jumps to a pure SDE system. - Found $(length(MT.jumps(flatrs))) jump(s). - Use `HybridProblem` instead. Note: SDE+Jump hybrids require special handling.""") + error( + """Cannot convert ReactionSystem with coupled jumps to a pure SDE system. + Found $(length(MT.jumps(flatrs))) jump(s). + Use `HybridProblem` instead. Note: SDE+Jump hybrids require special handling.""" + ) end if !isempty(MT.poissonians(flatrs)) - error("""Cannot convert ReactionSystem with poissonians to a pure SDE system. - Found $(length(MT.poissonians(flatrs))) poissonian(s). - Use `HybridProblem` instead.""") + error( + """Cannot convert ReactionSystem with poissonians to a pure SDE system. + Found $(length(MT.poissonians(flatrs))) poissonian(s). + Use `HybridProblem` instead.""" + ) end has_constraints = has_alg_equations(flatrs) || any(isbc, get_unknowns(flatrs)) @@ -1022,13 +1106,18 @@ function sde_model(rs::ReactionSystem; remove_conserved && conservationlaws(flatrs) ists, ispcs = get_indep_sts(flatrs, remove_conserved) - eqs = assemble_drift(flatrs, ispcs; combinatoric_ratelaws, include_zero_odes, - remove_conserved, expand_catalyst_funs, use_jump_ratelaws) - noiseeqs = assemble_diffusion(flatrs, ists, ispcs; combinatoric_ratelaws, - remove_conserved, expand_catalyst_funs, use_jump_ratelaws) + eqs = assemble_drift( + flatrs, ispcs; combinatoric_ratelaws, include_zero_odes, + remove_conserved, expand_catalyst_funs, use_jump_ratelaws + ) + noiseeqs = assemble_diffusion( + flatrs, ists, ispcs; combinatoric_ratelaws, + remove_conserved, expand_catalyst_funs, use_jump_ratelaws + ) eqs, us, ps, obs, ics = addconstraints!(eqs, flatrs, ists, ispcs; remove_conserved) - return MT.System(eqs, get_iv(flatrs), us, ps; + return MT.System( + eqs, get_iv(flatrs), us, ps; noise_eqs = noiseeqs, observed = obs, name, @@ -1038,14 +1127,17 @@ function sde_model(rs::ReactionSystem; discrete_events = MT.get_discrete_events(flatrs), tstops = MT.get_tstops(flatrs), metadata = MT.get_metadata(rs), - kwargs...) + kwargs... + ) else # New path: Brownians via hybrid_model (requires mtkcompile for SDEProblem). - return hybrid_model(flatrs; + return hybrid_model( + flatrs; _override_all_scales = PhysicalScale.SDE, name, combinatoric_ratelaws, include_zero_odes, remove_conserved, checks, initial_conditions, - expand_catalyst_funs, use_jump_ratelaws, kwargs...) + expand_catalyst_funs, use_jump_ratelaws, kwargs... + ) end end @@ -1077,7 +1169,7 @@ function merge_physical_scales(rxs, physical_scales, default) end end - scales + return scales end # Overload for when physical_scales is already a fully-resolved vector of scales. @@ -1088,7 +1180,7 @@ function merge_physical_scales(rxs, physical_scales::AbstractVector{<:PhysicalSc for (idx, s) in enumerate(scales) s == PhysicalScale.Auto && (scales[idx] = default) end - scales + return scales end # Returns (has_ode, has_sde, has_jump) for the resolved scales. @@ -1096,7 +1188,7 @@ function detect_scale_types(scales) has_ode = any(==(PhysicalScale.ODE), scales) has_sde = any(==(PhysicalScale.SDE), scales) has_jump = any(s -> s in (PhysicalScale.Jump, PhysicalScale.VariableRateJump), scales) - (has_ode, has_sde, has_jump) + return (has_ode, has_sde, has_jump) end """ @@ -1123,11 +1215,13 @@ Keyword args and default values: `VariableRateJump` to save the solution before and/or after the jump occurs. Defaults to true for both. """ -function jump_model(rs::ReactionSystem; name = nameof(rs), +function jump_model( + rs::ReactionSystem; name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), remove_conserved = nothing, checks = false, initial_conditions = Dict(), expand_catalyst_funs = true, save_positions = (true, true), - physical_scales = nothing, kwargs...) + physical_scales = nothing, kwargs... + ) (remove_conserved !== nothing) && throw(ArgumentError("Catalyst does not support removing conserved species when converting to jump Systems.")) @@ -1137,17 +1231,21 @@ function jump_model(rs::ReactionSystem; name = nameof(rs), # Error if ReactionSystem has unprocessed poissonians (jump_model cannot handle them). if !isempty(MT.poissonians(flatrs)) - error("""Cannot convert ReactionSystem with unprocessed poissonians to a pure Jump system via jump_model. - Use `HybridProblem` instead, which will process poissonians via mtkcompile.""") + error( + """Cannot convert ReactionSystem with unprocessed poissonians to a pure Jump system via jump_model. + Use `HybridProblem` instead, which will process poissonians via mtkcompile.""" + ) end # Error on non-reaction ODE/algebraic/SDE equations (pure Jump only supports reactions). # This also catches brownians since they appear in SDE equations. non_rxn_eqs = filter(eq -> !(eq isa Reaction), equations(flatrs)) if !isempty(non_rxn_eqs) - error("""Cannot convert ReactionSystem with ODE, SDE, or algebraic equations to a pure Jump system. - Found $(length(non_rxn_eqs)) non-reaction equation(s). - Use `HybridProblem` instead for mixed ODE+Jump or SDE+Jump systems.""") + error( + """Cannot convert ReactionSystem with ODE, SDE, or algebraic equations to a pure Jump system. + Found $(length(non_rxn_eqs)) non-reaction equation(s). + Use `HybridProblem` instead for mixed ODE+Jump or SDE+Jump systems.""" + ) end jump_scales = map(reactions(flatrs)) do rx @@ -1162,25 +1260,31 @@ function jump_model(rs::ReactionSystem; name = nameof(rs), end end - hybrid_model(flatrs; + return hybrid_model( + flatrs; physical_scales = jump_scales, default_scale = PhysicalScale.Jump, name, combinatoric_ratelaws, checks, initial_conditions, - expand_catalyst_funs, save_positions, kwargs...) + expand_catalyst_funs, save_positions, kwargs... + ) end ### Problems ### # ODEProblem from AbstractReactionNetwork -function DiffEqBase.ODEProblem(rs::ReactionSystem, u0, tspan, +function DiffEqBase.ODEProblem( + rs::ReactionSystem, u0, tspan, p = DiffEqBase.NullParameters(), args...; check_length = false, name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), include_zero_odes = true, remove_conserved = false, checks = false, expand_catalyst_funs = true, mtkcompile = false, - use_jump_ratelaws = false, kwargs...) - osys = ode_model(rs; name, combinatoric_ratelaws, include_zero_odes, checks, - remove_conserved, expand_catalyst_funs, use_jump_ratelaws) + use_jump_ratelaws = false, kwargs... + ) + osys = ode_model( + rs; name, combinatoric_ratelaws, include_zero_odes, checks, + remove_conserved, expand_catalyst_funs, use_jump_ratelaws + ) # Handles potential differential algebraic equations (which requires `mtkcompile`). if mtkcompile @@ -1191,7 +1295,7 @@ function DiffEqBase.ODEProblem(rs::ReactionSystem, u0, tspan, osys = complete(osys) end - prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any,Any}(u0), Dict{Any,Any}(p)) + prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any, Any}(u0), Dict{Any, Any}(p)) return ODEProblem(osys, prob_cond, tspan, args...; check_length, kwargs...) end @@ -1224,32 +1328,42 @@ Keyword args and default values: with their rational function representation when converting to another system type. Set to `false`` to disable. """ -function DiffEqBase.NonlinearProblem(rs::ReactionSystem, u0, +function DiffEqBase.NonlinearProblem( + rs::ReactionSystem, u0, p = DiffEqBase.NullParameters(), args...; name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), - remove_conserved = false, checks = false, check_length = false, expand_catalyst_funs = true, - mtkcompile = false, all_differentials_permitted = false, kwargs...) - nlsys = ss_ode_model(rs; name, combinatoric_ratelaws, checks, all_differentials_permitted, - remove_conserved, expand_catalyst_funs) + remove_conserved = false, checks = false, check_length = false, expand_catalyst_funs = true, + mtkcompile = false, all_differentials_permitted = false, kwargs... + ) + nlsys = ss_ode_model( + rs; name, combinatoric_ratelaws, checks, all_differentials_permitted, + remove_conserved, expand_catalyst_funs + ) nlsys = mtkcompile ? MT.mtkcompile(nlsys) : complete(nlsys) - prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any,Any}(u0), Dict{Any,Any}(p)) - return NonlinearProblem(nlsys, prob_cond, args...; check_length, - kwargs...) + prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any, Any}(u0), Dict{Any, Any}(p)) + return NonlinearProblem( + nlsys, prob_cond, args...; check_length, + kwargs... + ) end # SDEProblem from AbstractReactionNetwork -function DiffEqBase.SDEProblem(rs::ReactionSystem, u0, tspan, +function DiffEqBase.SDEProblem( + rs::ReactionSystem, u0, tspan, p = DiffEqBase.NullParameters(), args...; name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), include_zero_odes = true, checks = false, check_length = false, remove_conserved = false, mtkcompile = false, expand_catalyst_funs = true, use_legacy_noise = true, - use_jump_ratelaws = false, kwargs...) + use_jump_ratelaws = false, kwargs... + ) # Flatten once upfront and pass to sde_model. flatrs = Catalyst.flatten(rs) - sde_sys = sde_model(flatrs; name, combinatoric_ratelaws, expand_catalyst_funs, - include_zero_odes, checks, remove_conserved, use_legacy_noise, use_jump_ratelaws) + sde_sys = sde_model( + flatrs; name, combinatoric_ratelaws, expand_catalyst_funs, + include_zero_odes, checks, remove_conserved, use_legacy_noise, use_jump_ratelaws + ) # Determine if we need mtkcompile: # - If user brownians are present, sde_model routes through hybrid_model which requires mtkcompile @@ -1259,12 +1373,12 @@ function DiffEqBase.SDEProblem(rs::ReactionSystem, u0, tspan, has_constraints = has_alg_equations(flatrs) || any(isbc, get_unknowns(flatrs)) has_user_brownians = !isempty(MT.brownians(flatrs)) needs_mtkcompile = mtkcompile || - has_alg_equations(flatrs) || - !use_legacy_noise || - has_user_brownians || - has_constraints + has_alg_equations(flatrs) || + !use_legacy_noise || + has_user_brownians || + has_constraints - prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any,Any}(u0), Dict{Any,Any}(p)) + prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any, Any}(u0), Dict{Any, Any}(p)) if needs_mtkcompile if !mtkcompile && has_alg_equations(flatrs) @@ -1276,8 +1390,10 @@ function DiffEqBase.SDEProblem(rs::ReactionSystem, u0, tspan, # Legacy path: complete + noise_rate_prototype sde_sys = complete(sde_sys) p_matrix = zeros(length(get_unknowns(sde_sys)), numreactions(flatrs)) - return SDEProblem(sde_sys, prob_cond, tspan, args...; check_length, - noise_rate_prototype = p_matrix, kwargs...) + return SDEProblem( + sde_sys, prob_cond, tspan, args...; check_length, + noise_rate_prototype = p_matrix, kwargs... + ) end end @@ -1314,19 +1430,25 @@ end # JumpProblem from ReactionSystem # Note: For hybrid ODE+Jump systems, use HybridProblem instead. -function JumpProcesses.JumpProblem(rs::ReactionSystem, u0, tspan, +function JumpProcesses.JumpProblem( + rs::ReactionSystem, u0, tspan, p = DiffEqBase.NullParameters(); name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), expand_catalyst_funs = true, save_positions = (true, true), checks = false, - kwargs...) + kwargs... + ) # Pure jump system - use HybridProblem for hybrid ODE+SDE+Jump systems. - jsys = complete(jump_model(rs; name, combinatoric_ratelaws, checks, - expand_catalyst_funs, save_positions)) + jsys = complete( + jump_model( + rs; name, combinatoric_ratelaws, checks, + expand_catalyst_funs, save_positions + ) + ) # Use Dict{Any,Any} to prevent type promotion during merge (MTK converts to this anyway). - op = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any,Any}(u0), Dict{Any,Any}(p)) + op = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any, Any}(u0), Dict{Any, Any}(p)) # Auto-detect integer u0 and set u0_eltype if not already provided. if !haskey(kwargs, :u0_eltype) @@ -1405,7 +1527,8 @@ prob_sde_jump = HybridProblem(rn_sde_jump, [:S => 100.0, :P => 0.0], (0.0, 10.0) sol = solve(prob_sde_jump, SRIW1()) ``` """ -function HybridProblem(rs::ReactionSystem, u0, tspan, +function HybridProblem( + rs::ReactionSystem, u0, tspan, p = DiffEqBase.NullParameters(); name = nameof(rs), physical_scales = nothing, @@ -1416,7 +1539,8 @@ function HybridProblem(rs::ReactionSystem, u0, tspan, checks = false, mtkcompile = false, use_jump_ratelaws = false, - kwargs...) + kwargs... + ) # Determine which scale types are present. flatrs = Catalyst.flatten(rs) resolved_scales = merge_physical_scales(reactions(flatrs), physical_scales, default_scale) @@ -1432,13 +1556,15 @@ function HybridProblem(rs::ReactionSystem, u0, tspan, has_jump = has_jump || user_has_jump || user_has_poissonian # Build the unified System from the flattened ReactionSystem. - sys = hybrid_model(flatrs; name, physical_scales, default_scale, + sys = hybrid_model( + flatrs; name, physical_scales, default_scale, combinatoric_ratelaws, expand_catalyst_funs, save_positions, checks, - use_jump_ratelaws) + use_jump_ratelaws + ) # Build problem conditions (u0 + p merged). # Use Dict{Any,Any} to prevent type promotion during merge (MTK converts to this anyway). - prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any,Any}(u0), Dict{Any,Any}(p)) + prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any, Any}(u0), Dict{Any, Any}(p)) if has_jump # Any jumps present → JumpProblem (wrapping ODEProblem or SDEProblem as needed) @@ -1477,14 +1603,18 @@ function HybridProblem(rs::ReactionSystem, u0, tspan, end # SteadyStateProblem from AbstractReactionNetwork -function DiffEqBase.SteadyStateProblem(rs::ReactionSystem, u0, +function DiffEqBase.SteadyStateProblem( + rs::ReactionSystem, u0, p = DiffEqBase.NullParameters(), args...; check_length = false, name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), remove_conserved = false, include_zero_odes = true, checks = false, - expand_catalyst_funs = true, mtkcompile = false, kwargs...) - osys = ode_model(rs; name, combinatoric_ratelaws, include_zero_odes, checks, - remove_conserved, expand_catalyst_funs) + expand_catalyst_funs = true, mtkcompile = false, kwargs... + ) + osys = ode_model( + rs; name, combinatoric_ratelaws, include_zero_odes, checks, + remove_conserved, expand_catalyst_funs + ) # Handles potential differential algebraic equations (which requires `mtkcompile`). if mtkcompile @@ -1495,7 +1625,7 @@ function DiffEqBase.SteadyStateProblem(rs::ReactionSystem, u0, osys = complete(osys) end - prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any,Any}(u0), Dict{Any,Any}(p)) + prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any, Any}(u0), Dict{Any, Any}(p)) return SteadyStateProblem(osys, prob_cond, args...; check_length, kwargs...) end @@ -1516,7 +1646,7 @@ function _symbol_to_var(sys, sym) throw(ArgumentError("System $(nameof(sys)): variable $sym does not exist")) end end - var + return var end """ @@ -1572,11 +1702,11 @@ function symmap_to_varmap(sys, symmap::Tuple) end function symmap_to_varmap(sys, symmap::AbstractArray{Pair{Symbol, T}}) where {T} - [_symbol_to_var(sys, sym) => val for (sym, val) in symmap] + return [_symbol_to_var(sys, sym) => val for (sym, val) in symmap] end function symmap_to_varmap(sys, symmap::Dict{Symbol, T}) where {T} - Dict(_symbol_to_var(sys, sym) => val for (sym, val) in symmap) + return Dict(_symbol_to_var(sys, sym) => val for (sym, val) in symmap) end # don't permute any other types and let varmap_to_vars handle erroring @@ -1592,7 +1722,7 @@ Convert the given system of polynomial equations to multivariate polynomial repr For example, this can be used in HomotopyContinuation.jl functions. """ function to_multivariate_poly(polyeqs::AbstractVector{Symbolics.SymbolicT}) - @assert length(polyeqs)>=1 "At least one expression must be passed to `multivariate_poly`." + @assert length(polyeqs) >= 1 "At least one expression must be passed to `multivariate_poly`." poly_to_bs = Dict{SymbolicUtils.PolyVarT, Symbolics.SymbolicT}() bs_to_poly = Dict{Symbolics.SymbolicT, SymbolicUtils.PolyVarT}() @@ -1604,5 +1734,5 @@ function to_multivariate_poly(polyeqs::AbstractVector{Symbolics.SymbolicT}) end end - ps + return ps end diff --git a/src/reactionsystem_serialisation/serialisation_support.jl b/src/reactionsystem_serialisation/serialisation_support.jl index 1c13f2bb6a..ee9fc8c84a 100644 --- a/src/reactionsystem_serialisation/serialisation_support.jl +++ b/src/reactionsystem_serialisation/serialisation_support.jl @@ -31,8 +31,10 @@ get_substring_end(str, idx1, offset) = String(collect(str)[idx1:(end + offset)]) ### Field Serialisation Support Functions ### # Function which handles the addition of a single component to the file string. -function push_field(file_text::String, rn::ReactionSystem, - annotate::Bool, top_level::Bool, comp_funcs::Tuple) +function push_field( + file_text::String, rn::ReactionSystem, + annotate::Bool, top_level::Bool, comp_funcs::Tuple + ) has_component, get_comp_string, get_comp_annotation = comp_funcs has_component(rn) || (return (file_text, false)) @@ -69,13 +71,15 @@ end # Converts a numeric expression (e.g. p*X + 2Y) to a string (e.g. "p*X + 2Y"). Also ensures that for # any variables (e.g. X(t)) the call part is stripped, and only variable name (e.g. X) is written. -function expression_2_string(expr; - strip_call_dict = make_strip_call_dict(Symbolics.get_variables(expr))) +function expression_2_string( + expr; + strip_call_dict = make_strip_call_dict(Symbolics.get_variables(expr)) + ) strip_called_expr = substitute(expr, strip_call_dict; filterer = sub_inside_filter) return repr(strip_called_expr) end function sub_inside_filter(ex) - return if operation(ex) isa Union{Differential,Pre} + return if operation(ex) isa Union{Differential, Pre} true else SymbolicUtils.default_substitute_filter(ex) @@ -97,8 +101,10 @@ function syms_2_declaration_string(syms; multiline_format = false) decs_string = (multiline_format ? " begin" : "") for sym in syms delimiter = (multiline_format ? "\n\t" : " ") - @string_append! decs_string delimiter sym_2_declaration_string(sym; - multiline_format) + @string_append! decs_string delimiter sym_2_declaration_string( + sym; + multiline_format + ) end multiline_format && (@string_append! decs_string "\nend") return decs_string @@ -218,24 +224,28 @@ end # List of all recognised metadata (we should add as many as possible), and th keyword used to declare # them in code. -const RECOGNISED_METADATA = Dict([Catalyst.ParameterConstantSpecies => "isconstantspecies" - Catalyst.VariableBCSpecies => "isbcspecies" - Catalyst.VariableSpecies => "isspecies" - Catalyst.EdgeParameter => "edgeparameter" - Catalyst.CompoundSpecies => "iscompound" - Catalyst.CompoundComponents => "components" - Catalyst.CompoundCoefficients => "coefficients" - MT.VariableDescription => "description" - MT.VariableBounds => "bounds" - MT.VariableUnit => "unit" - MT.VariableConnectType => "connect" - #MT.VariableNoiseType => "noise" - MT.VariableInput => "input" - MT.VariableOutput => "output" - MT.VariableIrreducible => "irreducible" - MT.VariableStatePriority => "state_priority" - MT.VariableMisc => "misc" - MT.TimeDomain => "timedomain"]) +const RECOGNISED_METADATA = Dict( + [ + Catalyst.ParameterConstantSpecies => "isconstantspecies" + Catalyst.VariableBCSpecies => "isbcspecies" + Catalyst.VariableSpecies => "isspecies" + Catalyst.EdgeParameter => "edgeparameter" + Catalyst.CompoundSpecies => "iscompound" + Catalyst.CompoundComponents => "components" + Catalyst.CompoundCoefficients => "coefficients" + MT.VariableDescription => "description" + MT.VariableBounds => "bounds" + MT.VariableUnit => "unit" + MT.VariableConnectType => "connect" + #MT.VariableNoiseType => "noise" + MT.VariableInput => "input" + MT.VariableOutput => "output" + MT.VariableIrreducible => "irreducible" + MT.VariableStatePriority => "state_priority" + MT.VariableMisc => "misc" + MT.TimeDomain => "timedomain" + ] +) # List of metadata that does not need to be explicitly declared to be added (or which is handled separately). const SKIPPED_METADATA = [ @@ -243,13 +253,14 @@ const SKIPPED_METADATA = [ MT.MTKVariableTypeCtx, MT.SymScope, Symbolics.VariableDefaultValue, - Symbolics.VariableSource] + Symbolics.VariableSource, +] # List of metadata keys in an `AbstractSystem` which shouldn't be serialized. const SKIPPED_SYSTEM_METADATA = [MT.MutableCacheKey] function system_has_serializable_metadata(sys::MT.AbstractSystem) - any(!in(SKIPPED_SYSTEM_METADATA), keys(MT.get_metadata(sys))) + return any(!in(SKIPPED_SYSTEM_METADATA), keys(MT.get_metadata(sys))) end function system_metadata_to_string(meta::Base.ImmutableDict) diff --git a/src/reactionsystem_serialisation/serialise_fields.jl b/src/reactionsystem_serialisation/serialise_fields.jl index 5c6d5cf6d4..2809a39d66 100644 --- a/src/reactionsystem_serialisation/serialise_fields.jl +++ b/src/reactionsystem_serialisation/serialise_fields.jl @@ -43,8 +43,10 @@ const SIVS_FS = (seri_has_sivs, get_sivs_string, get_sivs_annotation) # Function which handles the addition of species, variable, and parameter declarations to the file # text. These must be handled as a unity in case there are default value dependencies between these. -function handle_us_n_ps(file_text::String, rn::ReactionSystem, annotate::Bool, - top_level::Bool) +function handle_us_n_ps( + file_text::String, rn::ReactionSystem, annotate::Bool, + top_level::Bool + ) # Fetches the system's parameters, species, and variables. Computes the `has_` `Bool`s. ps_all = filter(!_is_discrete, get_ps(rn)) discs_all = filter(_is_discrete, get_ps(rn)) @@ -178,7 +180,7 @@ end function seri_has_discretes(rn::ReactionSystem) return any(_is_discrete(p) for p in get_ps(rn)) end -# Temporary function, repalce with better after reply from Aayush. +# Temporary function, replace with better after reply from Aayush. _is_discrete(param) = occursin('(', "$(param)") # Extract a string which declares the system's discretes. Uses multiline declaration (a @@ -261,7 +263,7 @@ function get_reactions_string(rn::ReactionSystem) # Creates the string corresponding to the code which generates the system's reactions. rxs_string = "rxs = [" for rx in get_rxs(rn) - @string_append! rxs_string "\n\t"*reaction_string(rx, strip_call_dict) "," + @string_append! rxs_string "\n\t" * reaction_string(rx, strip_call_dict) "," end # Updates the string (including removing the last `,`) and returns it. @@ -453,7 +455,8 @@ function get_continuous_events_string(rn::ReactionSystem) continuous_events_string = "continuous_events = [" for continuous_event in MT.get_continuous_events(rn) @string_append! continuous_events_string "\n\t" continuous_event_string( - continuous_event, strip_call_dict) "," + continuous_event, strip_call_dict + ) "," end # Updates the string (including removing the last `,`) and returns it. @@ -517,7 +520,8 @@ function get_discrete_events_string(rn::ReactionSystem) discrete_events_string = "discrete_events = [" for discrete_event in MT.get_discrete_events(rn) @string_append! discrete_events_string "\n\t" discrete_event_string( - discrete_event, strip_call_dict) "," + discrete_event, strip_call_dict + ) "," end # Updates the string (including removing the last `,`) and returns it. @@ -568,12 +572,12 @@ end # Extract a string which declares the system's brownian types. function get_brownian_type_string(rn::ReactionSystem) - get_unsupported_comp_string("brownian types") + return get_unsupported_comp_string("brownian types") end # Creates an annotation for the system's brownian types. function get_brownian_type_annotation(rn::ReactionSystem) - get_unsupported_comp_annotation("Brownian types:") + return get_unsupported_comp_annotation("Brownian types:") end # Combines the 3 brownian types-related functions in a constant tuple. @@ -588,12 +592,12 @@ end # Extract a string which declares the system's jump types. function get_jump_type_string(rn::ReactionSystem) - get_unsupported_comp_string("jump types") + return get_unsupported_comp_string("jump types") end # Creates an annotation for the system's jump types. function get_jump_type_annotation(rn::ReactionSystem) - get_unsupported_comp_annotation("Jump types:") + return get_unsupported_comp_annotation("Jump types:") end # Combines the 3 jump types-related functions in a constant tuple. @@ -604,8 +608,10 @@ const JUMP_TYPE_FS = (seri_has_jump_type, get_jump_type_string, get_jump_type_an # Specific `push_field` function, which is used for the system field (where the annotation option # must be passed to the `get_component_string` function). Since non-ReactionSystem systems cannot be # written to file, this function throws an error if any such systems are encountered. -function push_systems_field(file_text::String, rn::ReactionSystem, annotate::Bool, - top_level::Bool) +function push_systems_field( + file_text::String, rn::ReactionSystem, annotate::Bool, + top_level::Bool + ) # Checks whether there are any subsystems, and if these are ReactionSystems. seri_has_systems(rn) || (return (file_text, false)) if any(!(system isa ReactionSystem) for system in MT.get_systems(rn)) diff --git a/src/reactionsystem_serialisation/serialise_reactionsystem.jl b/src/reactionsystem_serialisation/serialise_reactionsystem.jl index 884cd1b7b4..622acfc8d2 100644 --- a/src/reactionsystem_serialisation/serialise_reactionsystem.jl +++ b/src/reactionsystem_serialisation/serialise_reactionsystem.jl @@ -30,14 +30,18 @@ Notes: - Reaction systems with components that have units cannot currently be saved. - The `ReactionSystem` is saved using *programmatic* (not DSL) format for model creation. """ -function save_reactionsystem(filename::String, rn::ReactionSystem; - annotate = true, safety_check = true) +function save_reactionsystem( + filename::String, rn::ReactionSystem; + annotate = true, safety_check = true + ) # Error and warning checks. reactionsystem_uptodate_check() if !isempty(MT.get_tstops(rn)) || (!isempty(get_systems(rn)) && !isempty(MT.symbolic_tstops(rn))) - error("Serialization of ReactionSystems with tstops is not yet supported. " * - "Please remove tstops before saving, or track " * - "https://github.com/SciML/Catalyst.jl/issues for updates.") + error( + "Serialization of ReactionSystems with tstops is not yet supported. " * + "Please remove tstops before saving, or track " * + "https://github.com/SciML/Catalyst.jl/issues for updates." + ) end if !isempty(get_networkproperties(rn)) @warn "The serialised network has cached network properties (e.g. computed conservation laws). This will not be saved as part of the network, and must be recomputed when it is loaded." @@ -74,20 +78,29 @@ function get_full_system_string(rn::ReactionSystem, annotate::Bool, top_level::B file_text, _ = push_field(file_text, rn, annotate, top_level, IV_FS) file_text, has_sivs = push_field(file_text, rn, annotate, top_level, SIVS_FS) file_text, has_parameters, has_discretes, has_species, has_variables = handle_us_n_ps( - file_text, rn, annotate, top_level) + file_text, rn, annotate, top_level + ) file_text, has_reactions = push_field(file_text, rn, annotate, top_level, REACTIONS_FS) file_text, has_equations = push_field(file_text, rn, annotate, top_level, EQUATIONS_FS) file_text, has_observed = push_field(file_text, rn, annotate, top_level, OBSERVED_FS) file_text, has_bindings = push_field(file_text, rn, annotate, top_level, BINDINGS_FS) file_text, has_initial_conditions = push_field(file_text, rn, annotate, top_level, INITIAL_CONDITIONS_FS) - file_text, has_continuous_events = push_field(file_text, rn, annotate, - top_level, CONTINUOUS_EVENTS_FS) - file_text, has_discrete_events = push_field(file_text, rn, annotate, - top_level, DISCRETE_EVENTS_FS) - file_text, has_brownians = push_field(file_text, rn, annotate, - top_level, BROWNIAN_TYPE_FS) - file_text, has_jumps = push_field(file_text, rn, annotate, - top_level, JUMP_TYPE_FS) + file_text, has_continuous_events = push_field( + file_text, rn, annotate, + top_level, CONTINUOUS_EVENTS_FS + ) + file_text, has_discrete_events = push_field( + file_text, rn, annotate, + top_level, DISCRETE_EVENTS_FS + ) + file_text, has_brownians = push_field( + file_text, rn, annotate, + top_level, BROWNIAN_TYPE_FS + ) + file_text, has_jumps = push_field( + file_text, rn, annotate, + top_level, JUMP_TYPE_FS + ) file_text, has_systems = push_systems_field(file_text, rn, annotate, top_level) # Finalise the system. Creates the final `ReactionSystem` call. @@ -96,7 +109,8 @@ function get_full_system_string(rn::ReactionSystem, annotate::Bool, top_level::B rn, annotate, top_level, has_sivs, has_species, has_variables, has_parameters, has_discretes, has_reactions, has_equations, has_observed, has_bindings, has_initial_conditions, has_continuous_events, - has_discrete_events, has_brownians, has_jumps, has_systems) + has_discrete_events, has_brownians, has_jumps, has_systems + ) annotate || (@string_prepend! "\n" file_text) annotate && top_level && @string_prepend! "\n# Serialised using Catalyst version v$(Catalyst.VERSION)." file_text @@ -108,10 +122,12 @@ end # Creates a ReactionSystem call for creating the model. Adds all the correct inputs to it. The input # `has_` `Bool`s described which inputs are used. If the model is `complete`, this is handled here. -function make_reaction_system_call(rs::ReactionSystem, annotate, top_level, has_sivs, +function make_reaction_system_call( + rs::ReactionSystem, annotate, top_level, has_sivs, has_species, has_variables, has_parameters, has_discretes, has_reactions, has_equations, - has_observed, has_bindings, has_initial_conditions, has_continuous_events, has_discrete_events, has_brownians, - has_jumps, has_systems) + has_observed, has_bindings, has_initial_conditions, has_continuous_events, has_discrete_events, has_brownians, + has_jumps, has_systems + ) # Gets the independent variable input. iv = x_2_string(get_iv(rs)) diff --git a/src/registered_functions.jl b/src/registered_functions.jl index 25bf660e08..58cb4e50fc 100644 --- a/src/registered_functions.jl +++ b/src/registered_functions.jl @@ -57,8 +57,10 @@ hillar(X, Y, v, K, n) = v * (X^n) / (X^n + Y^n + K^n) @register_derivative hillar(X, Y, v, K, n) 2 -v * n * (Y^(n - 1)) * (X^n) / (X^n + Y^n + K^n)^2 @register_derivative hillar(X, Y, v, K, n) 3 (X^n) / (X^n + Y^n + K^n) @register_derivative hillar(X, Y, v, K, n) 4 -v * n * (K^(n - 1)) * (X^n) / (X^n + Y^n + K^n)^2 -@register_derivative hillar(X, Y, v, K, n) 5 v * (X^n) * (log(X) * (Y^n + K^n) - (Y^n) * log(Y) - - (K^n) * log(K)) / (X^n + Y^n + K^n)^2 +@register_derivative hillar(X, Y, v, K, n) 5 v * (X^n) * ( + log(X) * (Y^n + K^n) - (Y^n) * log(Y) - + (K^n) * log(K) +) / (X^n + Y^n + K^n)^2 # Tuple storing all registered function (for use in various functionalities). @@ -103,14 +105,16 @@ function expand_catalyst_function(expr) return args[2] * (args[3]^args[4]) / ((args[1])^args[4] + (args[3])^args[4]) elseif operation(expr) == Catalyst.hillar return args[3] * (args[1]^args[5]) / - ((args[1])^args[5] + (args[2])^args[5] + (args[4])^args[5]) + ((args[1])^args[5] + (args[2])^args[5] + (args[4])^args[5]) end end # If applied to a Reaction, return a reaction with its rate modified. function expand_registered_functions(rx::Reaction) - Reaction(expand_registered_functions(rx.rate), rx.substrates, rx.products, - rx.substoich, rx.prodstoich, rx.netstoich, rx.only_use_rate, rx.metadata) + return Reaction( + expand_registered_functions(rx.rate), rx.substrates, rx.products, + rx.substoich, rx.prodstoich, rx.netstoich, rx.only_use_rate, rx.metadata + ) end # If applied to a Equation, returns it with it applied to lhs and rhs. @@ -124,8 +128,10 @@ function expand_registered_functions(ce::MT.SymbolicContinuousCallback) affect_eqs = isnothing(ce.affect) ? nothing : expand_registered_functions(ce.affect.affect) affect_neg_eqs = isnothing(ce.affect_neg) ? nothing : expand_registered_functions(ce.affect_neg.affect) disc_ps = isnothing(ce.affect) ? MT.SymbolicT[] : ce.affect.discrete_parameters - return MT.SymbolicContinuousCallback(conditions, affect_eqs; - affect_neg = affect_neg_eqs, discrete_parameters = disc_ps) + return MT.SymbolicContinuousCallback( + conditions, affect_eqs; + affect_neg = affect_neg_eqs, discrete_parameters = disc_ps + ) end # If applied to a discrete event, returns it applied to condition and affect. diff --git a/src/spatial_reaction_systems/discrete_space_jump_systems.jl b/src/spatial_reaction_systems/discrete_space_jump_systems.jl index cf8d750bea..6d17eef61d 100644 --- a/src/spatial_reaction_systems/discrete_space_jump_systems.jl +++ b/src/spatial_reaction_systems/discrete_space_jump_systems.jl @@ -1,11 +1,13 @@ ### JumpProblem ### # Builds a spatial JumpProblem from a DiscreteProblem containing a `DiscreteSpaceReactionSystem`. -function JumpProcesses.JumpProblem(dsrs::DiscreteSpaceReactionSystem, u0_in, tspan, +function JumpProcesses.JumpProblem( + dsrs::DiscreteSpaceReactionSystem, u0_in, tspan, p_in = DiffEqBase.NullParameters(), args...; aggregator = JumpProcesses.NSM(), combinatoric_ratelaws = get_combinatoric_ratelaws(reactionsystem(dsrs)), - name = nameof(reactionsystem(dsrs)), kwargs...) - + name = nameof(reactionsystem(dsrs)), kwargs... + ) + # Creates a `DiscreteProblem` from the input, which is uses as the internal problem. dprob = DiffEqBase.DiscreteProblem(dsrs, u0_in, tspan, p_in) @@ -16,18 +18,24 @@ function JumpProcesses.JumpProblem(dsrs::DiscreteSpaceReactionSystem, u0_in, tsp # The non-spatial DiscreteProblem have a u0 matrix with entries for all combinations of species and vertices. hopping_constants = make_hopping_constants(dprob, dsrs) sma_jumps = make_spatial_majumps(dprob, dsrs) - non_spat_dprob = DiscreteProblem(reshape(dprob.u0, num_species(dsrs), num_verts(dsrs)), - dprob.tspan, first.(dprob.p[1])) + non_spat_dprob = DiscreteProblem( + reshape(dprob.u0, num_species(dsrs), num_verts(dsrs)), + dprob.tspan, first.(dprob.p[1]) + ) # Creates and returns a spatial JumpProblem (masked spaces are not supported by these). spatial_system = has_masked_dspace(dsrs) ? get_dspace_graph(dsrs) : dspace(dsrs) - return JumpProblem(non_spat_dprob, aggregator, sma_jumps; - hopping_constants, spatial_system, name, kwargs...) + return JumpProblem( + non_spat_dprob, aggregator, sma_jumps; + hopping_constants, spatial_system, name, kwargs... + ) end # Builds a spatial DiscreteProblem from a Lattice Reaction System. -function DiffEqBase.DiscreteProblem(dsrs::DiscreteSpaceReactionSystem, u0_in, tspan, - p_in = DiffEqBase.NullParameters(), args...; kwargs...) +function DiffEqBase.DiscreteProblem( + dsrs::DiscreteSpaceReactionSystem, u0_in, tspan, + p_in = DiffEqBase.NullParameters(), args...; kwargs... + ) if !is_transport_system(dsrs) error("Currently discrete space Jump simulations only supported when all spatial reactions are transport reactions.") end @@ -45,8 +53,10 @@ function DiffEqBase.DiscreteProblem(dsrs::DiscreteSpaceReactionSystem, u0_in, ts # edge_ps values are sparse matrices. Here, index (i,j) is a parameter's value in the edge from vertex i to vertex j. # Uniform vertex/edge parameters store only a single value (a length 1 vector, or size 1x1 sparse matrix). vert_ps, - edge_ps = dspace_process_p(p_in, vertex_parameters(dsrs), - edge_parameters(dsrs), dsrs) + edge_ps = dspace_process_p( + p_in, vertex_parameters(dsrs), + edge_parameters(dsrs), dsrs + ) # Returns a DiscreteProblem (which basically just stores the processed input). return DiscreteProblem(u0, tspan, [vert_ps; edge_ps], args...; kwargs...) @@ -57,8 +67,10 @@ function make_hopping_constants(dprob::DiscreteProblem, dsrs::DiscreteSpaceReact # Creates the all_diff_rates vector, containing for each species, its transport rate across all edges. # If the transport rate is uniform for one species, the vector has a single element, else one for each edge. spatial_rates_dict = Dict(compute_all_transport_rates(Dict(dprob.p), dsrs)) - all_diff_rates = [haskey(spatial_rates_dict, s) ? spatial_rates_dict[s] : [0.0] - for s in species(dsrs)] + all_diff_rates = [ + haskey(spatial_rates_dict, s) ? spatial_rates_dict[s] : [0.0] + for s in species(dsrs) + ] # Creates an array (of the same size as the hopping constant array) containing all edges. # First the array is a NxM matrix (number of species x number of vertices). Each element is a @@ -72,9 +84,13 @@ function make_hopping_constants(dprob::DiscreteProblem, dsrs::DiscreteSpaceReact # Creates the hopping constants array. It has the same shape as the edge array, but each # element is that species transportation rate along that edge - hopping_constants = [[Catalyst.get_edge_value(all_diff_rates[s_idx], e) - for e in edge_array[s_idx, src_idx]] - for s_idx in 1:num_species(dsrs), src_idx in 1:num_verts(dsrs)] + hopping_constants = [ + [ + Catalyst.get_edge_value(all_diff_rates[s_idx], e) + for e in edge_array[s_idx, src_idx] + ] + for s_idx in 1:num_species(dsrs), src_idx in 1:num_verts(dsrs) + ] return hopping_constants end @@ -83,8 +99,10 @@ end # Not sure if there is any form of performance improvement from that though. Likely not the case. function make_spatial_majumps(dprob, dsrs::DiscreteSpaceReactionSystem) # Creates a vector, storing which reactions have spatial components. - is_spatials = [has_spatial_vertex_component(rx.rate, dprob.p) - for rx in reactions(reactionsystem(dsrs))] + is_spatials = [ + has_spatial_vertex_component(rx.rate, dprob.p) + for rx in reactions(reactionsystem(dsrs)) + ] # Creates templates for the rates (uniform and spatial) and the stoichiometries. # We cannot fetch reactant_stoich and net_stoich from a (non-spatial) MassActionJump. @@ -108,8 +126,10 @@ function make_spatial_majumps(dprob, dsrs::DiscreteSpaceReactionSystem) # Loops through reactions with spatial rates, computes their rates and stoichiometries. for (is_spat, rx) in zip(is_spatials, reactions(reactionsystem(dsrs))) is_spat || continue - s_rates[cur_rx - length(u_rates), :] .= compute_vertex_value(rx.rate, dsrs; - ps = dprob.p) + s_rates[cur_rx - length(u_rates), :] .= compute_vertex_value( + rx.rate, dsrs; + ps = dprob.p + ) substoich_map = Pair.(rx.substrates, rx.substoich) reactant_stoich[cur_rx] = int_map(substoich_map, reactionsystem(dsrs)) net_stoich[cur_rx] = int_map(rx.netstoich, reactionsystem(dsrs)) diff --git a/src/spatial_reaction_systems/discrete_space_reaction_systems.jl b/src/spatial_reaction_systems/discrete_space_reaction_systems.jl index d1807bd847..b011ec3396 100644 --- a/src/spatial_reaction_systems/discrete_space_reaction_systems.jl +++ b/src/spatial_reaction_systems/discrete_space_reaction_systems.jl @@ -102,9 +102,11 @@ struct DiscreteSpaceReactionSystem{Q, R, S, T} <: MT.AbstractSystem "The name of the discrete space reaction system. Typically taken directly from the base `ReactionSystem`." name::Symbol - function DiscreteSpaceReactionSystem(rs::ReactionSystem{Q}, spatial_reactions::Vector{R}, + function DiscreteSpaceReactionSystem( + rs::ReactionSystem{Q}, spatial_reactions::Vector{R}, dspace::S, num_verts::Int64, num_edges::Int64, edge_iterator::T; - name::Symbol = MT.nameof(rs)) where {Q, R, S, T} + name::Symbol = MT.nameof(rs) + ) where {Q, R, S, T} # Error checks. if !(R <: AbstractSpatialReaction) throw(ArgumentError("The second argument must be a vector of AbstractSpatialReaction subtypes.")) @@ -133,8 +135,12 @@ struct DiscreteSpaceReactionSystem{Q, R, S, T} <: MT.AbstractSystem if isempty(spatial_reactions) spat_species = SymbolicT[] else - spat_species = unique(reduce(vcat, - [spatial_species(sr) for sr in spatial_reactions])) + spat_species = unique( + reduce( + vcat, + [spatial_species(sr) for sr in spatial_reactions] + ) + ) end num_species = length(unique([species(rs); spat_species])) @@ -144,7 +150,8 @@ struct DiscreteSpaceReactionSystem{Q, R, S, T} <: MT.AbstractSystem srs_edge_parameters = SymbolicT[] else srs_edge_parameters = setdiff( - reduce(vcat, [parameters(sr) for sr in spatial_reactions]), parameters(rs)) + reduce(vcat, [parameters(sr) for sr in spatial_reactions]), parameters(rs) + ) end edge_parameters = unique([rs_edge_parameters; srs_edge_parameters]) vertex_parameters = filter(!isedgeparameter, parameters(rs)) @@ -155,7 +162,8 @@ struct DiscreteSpaceReactionSystem{Q, R, S, T} <: MT.AbstractSystem # Checks that all spatial reactions are valid for this reaction system. foreach( sr -> check_spatial_reaction_validity(rs, sr; edge_parameters = edge_parameters), - spatial_reactions) + spatial_reactions + ) # Additional error checks. if any(is_array_symvar(symvar) for symvar in [ps; species(rs)]) @@ -164,7 +172,8 @@ struct DiscreteSpaceReactionSystem{Q, R, S, T} <: MT.AbstractSystem return new{Q, R, S, T}( rs, spatial_reactions, dspace, num_verts, num_edges, num_species, - spat_species, ps, vertex_parameters, edge_parameters, edge_iterator) + spat_species, ps, vertex_parameters, edge_parameters, edge_iterator + ) end end @@ -180,13 +189,15 @@ function DiscreteSpaceReactionSystem(rs, srs, dspace::DiGraph; kwargs...) end # Creates a DiscreteSpaceReactionSystem from a (undirected) Graph dspace (graph grid). function DiscreteSpaceReactionSystem(rs, srs, dspace::SimpleGraph; kwargs...) - DiscreteSpaceReactionSystem(rs, srs, DiGraph(dspace); kwargs...) + return DiscreteSpaceReactionSystem(rs, srs, DiGraph(dspace); kwargs...) end # Creates a DiscreteSpaceReactionSystem from a CartesianGrid dspace (cartesian grid) or a Boolean Array # dspace (masked grid). These two are quite similar, so much code can be reused in a single interface. -function DiscreteSpaceReactionSystem(rs, srs, dspace::GridLattice{N, T}; - diagonal_connections = false, kwargs...) where {N, T} +function DiscreteSpaceReactionSystem( + rs, srs, dspace::GridLattice{N, T}; + diagonal_connections = false, kwargs... + ) where {N, T} # Error checks. (N > 3) && error("Grids of higher dimension than 3 is currently not supported.") @@ -207,8 +218,10 @@ function DiscreteSpaceReactionSystem(rs, srs, dspace::GridLattice{N, T}; g_size = grid_size(dspace) edge_iterator = Vector{Pair{Int64, Int64}}(undef, num_edges) for (fspat_idx, grid_idx) in enumerate(fspat_to_grid_idx) - for neighbour_grid_idx in get_neighbours(dspace, grid_idx, g_size; - diagonal_connections) + for neighbour_grid_idx in get_neighbours( + dspace, grid_idx, g_size; + diagonal_connections + ) cur_vert += 1 edge_iterator[cur_vert] = fspat_idx => grid_to_fspat_idx[neighbour_grid_idx...] end @@ -227,14 +240,16 @@ count_verts(grid::Array{Bool, N}) where {N} = count(grid) # Counts and edges on a Cartesian grid. The formula counts the number of internal, side, edge, and # corner vertices (on the grid). `l,m,n = grid_dims(grid),1,1` ensures that "extra" dimensions get # length 1. The formula holds even if one or more of l, m, and n are 1. -function count_edges(grid::CartesianGridRej{N, T}; - diagonal_connections = false) where {N, T} +function count_edges( + grid::CartesianGridRej{N, T}; + diagonal_connections = false + ) where {N, T} l, m, n = grid_size(grid)..., 1, 1 (ni, ns, ne, nc) = diagonal_connections ? (26, 17, 11, 7) : (6, 5, 4, 3) num_edges = ni * (l - 2) * (m - 2) * (n - 2) + # Edges from internal vertices. - ns * (2(l - 2) * (m - 2) + 2(l - 2) * (n - 2) + 2(m - 2) * (n - 2)) + # Edges from side vertices. - ne * (4(l - 2) + 4(m - 2) + 4(n - 2)) + # Edges from edge vertices. - nc * 8 # Edges from corner vertices. + ns * (2(l - 2) * (m - 2) + 2(l - 2) * (n - 2) + 2(m - 2) * (n - 2)) + # Edges from side vertices. + ne * (4(l - 2) + 4(m - 2) + 4(n - 2)) + # Edges from edge vertices. + nc * 8 # Edges from corner vertices. return num_edges end @@ -245,8 +260,12 @@ function count_edges(grid::Array{Bool, N}; diagonal_connections = false) where { num_edges = 0 for grid_idx in get_grid_indices(grid) grid[grid_idx] || continue - num_edges += length(get_neighbours(grid, Tuple(grid_idx), g_size; - diagonal_connections)) + num_edges += length( + get_neighbours( + grid, Tuple(grid_idx), g_size; + diagonal_connections + ) + ) end return num_edges end @@ -272,24 +291,30 @@ function get_index_converters(grid::GridLattice{N, T}, num_verts) where {N, T} end # For a vertex's grid index, and a dspace, returns the grid indices of all its (valid) neighbours. -function get_neighbours(grid::GridLattice{N, T}, grid_idx, g_size; - diagonal_connections = false) where {N, T} +function get_neighbours( + grid::GridLattice{N, T}, grid_idx, g_size; + diagonal_connections = false + ) where {N, T} # Depending on the grid's dimension, find all potential neighbours. if grid_dims(grid) == 1 potential_neighbours = [grid_idx .+ (i) for i in -1:1] elseif grid_dims(grid) == 2 potential_neighbours = [grid_idx .+ (i, j) for i in -1:1 for j in -1:1] else - potential_neighbours = [grid_idx .+ (i, j, k) for i in -1:1 for j in -1:1 - for k in -1:1] + potential_neighbours = [ + grid_idx .+ (i, j, k) for i in -1:1 for j in -1:1 + for k in -1:1 + ] end # Depending on whether diagonal connections are used or not, find valid neighbours. if diagonal_connections filter!(n_idx -> n_idx !== grid_idx, potential_neighbours) else - filter!(n_idx -> count(n_idx .== grid_idx) == (length(g_size) - 1), - potential_neighbours) + filter!( + n_idx -> count(n_idx .== grid_idx) == (length(g_size) - 1), + potential_neighbours + ) end # Removes neighbours outside of the grid, and returns the full list. @@ -308,7 +333,7 @@ end # Gets an iterator over a grid's grid indices. Separate function so we can handle the two grid types # separately (i.e. not calling `CartesianIndices(ones(grid_size(grid)))` unnecessarily for masked grids). function get_grid_indices(grid::CartesianGridRej{N, T}) where {N, T} - CartesianIndices(ones(grid_size(grid))) + return CartesianIndices(ones(grid_size(grid))) end get_grid_indices(grid::Array{Bool, N}) where {N} = CartesianIndices(grid) @@ -408,7 +433,7 @@ Returns `true` if `dsrs` was created using a cartesian grid discrete space (e.g. Otherwise, returns `false`. """ function has_cartesian_dspace(dsrs::DiscreteSpaceReactionSystem) - dspace(dsrs) isa CartesianGridRej{N, T} where {N, T} + return dspace(dsrs) isa CartesianGridRej{N, T} where {N, T} end """ @@ -468,15 +493,17 @@ Returns dsrs's discrete space as a graph. Currently does not work for Cartesian """ function get_dspace_graph(dsrs::DiscreteSpaceReactionSystem) has_graph_dspace(dsrs) && return dspace(dsrs) - return Graphs.SimpleGraphFromIterator(Graphs.SimpleEdge(e[1], e[2]) - for e in edge_iterator(dsrs)) + return Graphs.SimpleGraphFromIterator( + Graphs.SimpleEdge(e[1], e[2]) + for e in edge_iterator(dsrs) + ) end ### Catalyst-based Getters ### # Get all species. function species(dsrs::DiscreteSpaceReactionSystem) - unique([species(reactionsystem(dsrs)); spatial_species(dsrs)]) + return unique([species(reactionsystem(dsrs)); spatial_species(dsrs)]) end # Generic ones (simply forwards call to the non-spatial system). @@ -495,13 +522,13 @@ MT.get_metadata(dsrs::DiscreteSpaceReactionSystem) = MT.get_metadata(reactionsys # Lattice reaction systems should not be combined with compositional modelling. # Maybe these should be allowed anyway? Still feel a bit weird function MT.get_eqs(dsrs::DiscreteSpaceReactionSystem) - MT.get_eqs(reactionsystem(dsrs)) + return MT.get_eqs(reactionsystem(dsrs)) end function MT.get_unknowns(dsrs::DiscreteSpaceReactionSystem) - MT.get_unknowns(reactionsystem(dsrs)) + return MT.get_unknowns(reactionsystem(dsrs)) end function MT.get_ps(dsrs::DiscreteSpaceReactionSystem) - MT.get_ps(reactionsystem(dsrs)) + return MT.get_ps(reactionsystem(dsrs)) end # Technically should not be used, but has to be declared for the `show` function to work. @@ -511,7 +538,7 @@ end # Other non-relevant getters. function MT.independent_variables(dsrs::DiscreteSpaceReactionSystem) - MT.independent_variables(reactionsystem(dsrs)) + return MT.independent_variables(reactionsystem(dsrs)) end ### Edge Parameter Value Generators ### @@ -573,8 +600,10 @@ function make_edge_p_values(dsrs::DiscreteSpaceReactionSystem, make_edge_p_value # If not, then the sparse matrix simply becomes empty in that position. values[e[1], e[2]] = eps() - values[e[1], e[2]] = make_edge_p_value(fspat_to_grid_idx[e[1]], - fspat_to_grid_idx[e[2]]) + values[e[1], e[2]] = make_edge_p_value( + fspat_to_grid_idx[e[1]], + fspat_to_grid_idx[e[2]] + ) end return values @@ -623,9 +652,11 @@ D_vals = make_directed_edge_values(dsrs, (0.1, 0.1), (0.1, 0.0)) ``` Here, since we have a 2d grid, we only provide the first two Tuples to `make_directed_edge_values`. """ -function make_directed_edge_values(dsrs::DiscreteSpaceReactionSystem, x_vals::Tuple{T, T}, +function make_directed_edge_values( + dsrs::DiscreteSpaceReactionSystem, x_vals::Tuple{T, T}, y_vals::Union{Nothing, Tuple{T, T}} = nothing, - z_vals::Union{Nothing, Tuple{T, T}} = nothing) where {T} + z_vals::Union{Nothing, Tuple{T, T}} = nothing + ) where {T} # Error checks. if has_graph_dspace(dsrs) error("The `make_directed_edge_values` function is only meant for discrete spaces with (Cartesian or masked) grid structures. It cannot be applied to graph discrete spaces.") diff --git a/src/spatial_reaction_systems/discrete_space_sim_struct_interfacing.jl b/src/spatial_reaction_systems/discrete_space_sim_struct_interfacing.jl index f956584ba1..777b862e3f 100644 --- a/src/spatial_reaction_systems/discrete_space_sim_struct_interfacing.jl +++ b/src/spatial_reaction_systems/discrete_space_sim_struct_interfacing.jl @@ -46,13 +46,13 @@ function spat_setp!(sim_struct, p, dsrs::DiscreteSpaceReactionSystem, p_vals) # Reshapes the values to a vector of the correct form, and calls spat_setp! on the input structure. p_vals_reshaped = vertex_value_form(p_vals, dsrs, p) - spat_setp!(sim_struct, p_idx, p_vals_reshaped, num_verts(dsrs)) + return spat_setp!(sim_struct, p_idx, p_vals_reshaped, num_verts(dsrs)) end # Note: currently, `spat_setp!(oprob::ODEProblem, ...`) and `spat_setp!(SciMLBase.AbstractODEIntegrator, ...`) # are identical and could be merged to a single function. function spat_setp!(oprob::ODEProblem, p_idx::Int64, p_vals, num_verts) - if length(p_vals) == 1 + return if length(p_vals) == 1 foreach(idx -> (oprob.p[p_idx][idx] = p_vals[1]), 1:num_verts) elseif length(p_vals) == length(oprob.p[p_idx]) foreach(idx -> (oprob.p[p_idx][idx] = p_vals[idx]), 1:num_verts) @@ -65,7 +65,7 @@ function spat_setp!(jprob::JumpProblem, p_idx::Int64, p_vals, num_verts) error("The `spat_setp!` function is currently not supported for `JumpProblem`s.") end function spat_setp!(oint::SciMLBase.AbstractODEIntegrator, p_idx::Int64, p_vals, num_verts) - if length(p_vals) == 1 + return if length(p_vals) == 1 foreach(idx -> (oint.p[p_idx][idx] = p_vals[1]), 1:num_verts) elseif length(p_vals) == length(oint.p[p_idx]) foreach(idx -> (oint.p[p_idx][idx] = p_vals[idx]), 1:num_verts) @@ -120,7 +120,7 @@ spat_getp(oprob, :k1, dsrs) # Retrieves the value of `k1`. function spat_getp(sim_struct, p, dsrs::DiscreteSpaceReactionSystem) edge_param_check(p, dsrs) p_idx, _ = get_p_idxs(p, dsrs) - spat_getp(sim_struct, p_idx, extract_dspace(dsrs), num_verts(dsrs)) + return spat_getp(sim_struct, p_idx, extract_dspace(dsrs), num_verts(dsrs)) end # Retrieves the discrete space values for problem or integrator structures. @@ -186,34 +186,38 @@ function spat_setu!(sim_struct, sp, dsrs::DiscreteSpaceReactionSystem, u) # Reshapes the values to a vector of the correct form, and calls spat_setu! on the input structure. u_reshaped = vertex_value_form(u, dsrs, sp) - spat_setu!(sim_struct, sp_idx, sp_tot, u_reshaped, num_verts(dsrs)) + return spat_setu!(sim_struct, sp_idx, sp_tot, u_reshaped, num_verts(dsrs)) end function spat_setu!(oprob::ODEProblem, sp_idx::Int64, sp_tot::Int64, u, num_verts) - if length(u) == 1 + return if length(u) == 1 foreach(idx -> (oprob.u0[sp_idx + (idx - 1) * sp_tot] = u[1]), 1:num_verts) else foreach(idx -> (oprob.u0[sp_idx + (idx - 1) * sp_tot] = u[idx]), 1:num_verts) end end function spat_setu!(jprob::JumpProblem, sp_idx::Int64, sp_tot::Int64, u, num_verts) - if length(u) == 1 + return if length(u) == 1 foreach(idx -> (jprob.prob.u0[sp_idx, idx] = u[1]), 1:num_verts) else foreach(idx -> (jprob.prob.u0[sp_idx, idx] = u[idx]), 1:num_verts) end end -function spat_setu!(oint::SciMLBase.AbstractODEIntegrator, sp_idx::Int64, sp_tot::Int64, - u, num_verts) - if length(u) == 1 +function spat_setu!( + oint::SciMLBase.AbstractODEIntegrator, sp_idx::Int64, sp_tot::Int64, + u, num_verts + ) + return if length(u) == 1 foreach(idx -> (oint.u[sp_idx + (idx - 1) * sp_tot] = u[1]), 1:num_verts) else foreach(idx -> (oint.u[sp_idx + (idx - 1) * sp_tot] = u[idx]), 1:num_verts) end end -function spat_setu!(jint::JumpProcesses.SSAIntegrator, sp_idx::Int64, sp_tot::Int64, - u, num_verts) - if length(u) == 1 +function spat_setu!( + jint::JumpProcesses.SSAIntegrator, sp_idx::Int64, sp_tot::Int64, + u, num_verts + ) + return if length(u) == 1 foreach(idx -> (jint.u[sp_idx, idx] = u[1]), 1:num_verts) else foreach(idx -> (jint.u[sp_idx, idx] = u[idx]), 1:num_verts) @@ -260,7 +264,7 @@ spat_getu(oprob, :X1, dsrs) # Retrieves the value of `X1`. """ function spat_getu(sim_struct, sp, dsrs::DiscreteSpaceReactionSystem) sp_idx, sp_tot = get_sp_idxs(sp, dsrs) - spat_getu(sim_struct, sp_idx, sp_tot, extract_dspace(dsrs)) + return spat_getu(sim_struct, sp_idx, sp_tot, extract_dspace(dsrs)) end # Retrieves the discrete space values for problem or integrator structures. @@ -330,7 +334,7 @@ spat_getu(osol, :X1, dsrs; t = 0.0:10.0) # Returns the value of X1 at times 0.0, function spat_getu(sol::ODESolution, sp, dsrs::DiscreteSpaceReactionSystem; t = nothing) (t isa Number) && error("The input `t` to `spat_getu` must be a `AbstractVector`.") sp_idx, sp_tot = get_sp_idxs(sp, dsrs) - spat_getu(sol, extract_dspace(dsrs), t, sp_idx, sp_tot) + return spat_getu(sol, extract_dspace(dsrs), t, sp_idx, sp_tot) end # Function which handles the input in the case where `t` is `nothing` (i.e. return `sp`s value @@ -350,8 +354,10 @@ end # Function which handles the input in the case where `t` is a range of values (i.e. return `sp`s # value at all designated time points. -function spat_getu(sol::ODESolution, dspace, t::AbstractVector{T}, sp_idx::Int64, - sp_tot::Int64) where {T <: Number} +function spat_getu( + sol::ODESolution, dspace, t::AbstractVector{T}, sp_idx::Int64, + sp_tot::Int64 + ) where {T <: Number} # Checks that an appropriate `t` is provided (however, DiffEq does permit out-of-range `t`s). if (minimum(t) < sol.t[1]) || (maximum(t) > sol.t[end]) error("The range of the t values provided for sampling, ($(minimum(t)),$(maximum(t))) is not fully within the range of the simulation time span ($(sol.t[1]),$(sol.t[end])).") @@ -413,18 +419,20 @@ rebuild_spat_internals!(oprob) """ function rebuild_spat_internals!(oprob::ODEProblem) lt_ofun = SciMLBase.unwrapped_f(oprob.f.f) - rebuild_spat_internals!(lt_ofun, oprob.p, lt_ofun.dsrs) + return rebuild_spat_internals!(lt_ofun, oprob.p, lt_ofun.dsrs) end # Function for rebuilding a `DiscreteSpaceReactionSystem` integrator after it has been updated. function rebuild_spat_internals!(integrator::SciMLBase.AbstractODEIntegrator) lt_ofun = SciMLBase.unwrapped_f(integrator.f.f) - rebuild_spat_internals!(lt_ofun, integrator.p, lt_ofun.dsrs) + return rebuild_spat_internals!(lt_ofun, integrator.p, lt_ofun.dsrs) end # Function which rebuilds a `LatticeTransportODEFunction` functor for a new parameter set. -function rebuild_spat_internals!(lt_ofun::LatticeTransportODEFunction, ps_new, - dsrs::DiscreteSpaceReactionSystem) +function rebuild_spat_internals!( + lt_ofun::LatticeTransportODEFunction, ps_new, + dsrs::DiscreteSpaceReactionSystem + ) # Computes Jacobian properties. jac = !isnothing(lt_ofun.jac_transport) sparse = lt_ofun.sparse @@ -433,8 +441,10 @@ function rebuild_spat_internals!(lt_ofun::LatticeTransportODEFunction, ps_new, ps_new = [(length(p) == 1) ? p[1] : p for p in deepcopy(ps_new)] ps_new = [p => p_val for (p, p_val) in zip(parameters(dsrs), deepcopy(ps_new))] vert_ps, - edge_ps = dspace_process_p(ps_new, vertex_parameters(dsrs), - edge_parameters(dsrs), dsrs) + edge_ps = dspace_process_p( + ps_new, vertex_parameters(dsrs), + edge_parameters(dsrs), dsrs + ) ps_new = [vert_ps; edge_ps] # Creates the new transport rates and transport Jacobian part. @@ -477,7 +487,7 @@ function replace_vec!(vec1, vec2) vec1[i] = v end foreach(idx -> deleteat!(vec1, idx), l1:-1:(l2 + 1)) - foreach(val -> push!(vec1, val), vec2[(l1 + 1):l2]) + return foreach(val -> push!(vec1, val), vec2[(l1 + 1):l2]) end # Currently not implemented. @@ -541,26 +551,26 @@ end function check_dspace_format(dspace::CartesianGridRej, u) (u isa AbstractArray) || error("The input u should be an AbstractArray. It is a $(typeof(u)).") - (size(u) == dspace.dims) || + return (size(u) == dspace.dims) || error("The input u should have size $(dspace.dims), but has size $(size(u)).") end function check_dspace_format(dspace::AbstractSparseArray, u) (u isa AbstractArray) || error("The input u should be an AbstractArray. It is a $(typeof(u)).") - (size(u) == size(dspace)) || + return (size(u) == size(dspace)) || error("The input u should have size $(size(dspace)), but has size $(size(u)).") end function check_dspace_format(dspace::DiGraph, u) (u isa AbstractArray) || error("The input u should be an AbstractVector. It is a $(typeof(u)).") - (length(u) == nv(dspace)) || + return (length(u) == nv(dspace)) || error("The input u should have length $(nv(dspace)), but has length $(length(u)).") end # Throws an error when interfacing with an edge parameter. function edge_param_check(p, dsrs) (p isa Symbol) && (p = _symbol_to_var(dsrs, p)) - if isedgeparameter(p) + return if isedgeparameter(p) throw(ArgumentError("The `spat_getp` and `spat_setp!` functions currently does not support edge parameter updating. If you require this functionality, please raise an issue on the Catalyst GitHub page and we can add this feature.")) end end diff --git a/src/spatial_reaction_systems/spatial_ODE_systems.jl b/src/spatial_reaction_systems/spatial_ODE_systems.jl index bbf13ee614..b12bdf3b91 100644 --- a/src/spatial_reaction_systems/spatial_ODE_systems.jl +++ b/src/spatial_reaction_systems/spatial_ODE_systems.jl @@ -64,22 +64,28 @@ struct LatticeTransportODEFunction{P, Q, R, S, T} """Remove when we add this as problem metadata""" dsrs::DiscreteSpaceReactionSystem - function LatticeTransportODEFunction(ofunc::P, ps::Vector{<:Pair}, + function LatticeTransportODEFunction( + ofunc::P, ps::Vector{<:Pair}, dsrs::DiscreteSpaceReactionSystem, sparse::Bool, jac_transport::Union{Nothing, Matrix{S}, SparseMatrixCSC{S, Int64}}, - transport_rates::Vector{Pair{Int64, SparseMatrixCSC{S, Int64}}}) where {P, S} + transport_rates::Vector{Pair{Int64, SparseMatrixCSC{S, Int64}}} + ) where {P, S} # Computes `LatticeTransportODEFunction` functor fields. heterogeneous_vert_p_idxs = make_heterogeneous_vert_p_idxs(ps, dsrs) mtk_ps, p_setters = make_mtk_ps_structs(ps, dsrs, heterogeneous_vert_p_idxs) t_rate_idx_types, - leaving_rates = make_t_types_and_leaving_rates(transport_rates, - dsrs) + leaving_rates = make_t_types_and_leaving_rates( + transport_rates, + dsrs + ) # Creates and returns the `LatticeTransportODEFunction` functor. - new{P, typeof(mtk_ps), typeof(p_setters), S, typeof(jac_transport)}(ofunc, + return new{P, typeof(mtk_ps), typeof(p_setters), S, typeof(jac_transport)}( + ofunc, num_verts(dsrs), num_species(dsrs), heterogeneous_vert_p_idxs, mtk_ps, p_setters, transport_rates, t_rate_idx_types, leaving_rates, Catalyst.edge_iterator(dsrs), - jac_transport, sparse, dsrs) + jac_transport, sparse, dsrs + ) end end @@ -88,8 +94,10 @@ end # Creates a vector with the heterogeneous vertex parameters' indexes in the full parameter vector. function make_heterogeneous_vert_p_idxs(ps, dsrs) p_dict = Dict(ps) - return findall((p_dict[p] isa Vector) && (length(p_dict[p]) > 1) - for p in parameters(dsrs)) + return findall( + (p_dict[p] isa Vector) && (length(p_dict[p]) > 1) + for p in parameters(dsrs) + ) end # Creates the MTKParameters structure and `p_setters` vector (which are used to manage @@ -99,8 +107,10 @@ function make_mtk_ps_structs(ps, dsrs, heterogeneous_vert_p_idxs) nonspatial_osys = complete(ode_model(reactionsystem(dsrs))) p_init = [p => p_dict[p][1] for p in parameters(nonspatial_osys)] mtk_ps = MT.MTKParameters(nonspatial_osys, p_init) - p_setters = [MT.setp(nonspatial_osys, p) - for p in parameters(dsrs)[heterogeneous_vert_p_idxs]] + p_setters = [ + MT.setp(nonspatial_osys, p) + for p in parameters(dsrs)[heterogeneous_vert_p_idxs] + ] return mtk_ps, p_setters end @@ -111,8 +121,10 @@ function make_t_types_and_leaving_rates(transport_rates, dsrs) for (s_idx, tr_pair) in enumerate(transport_rates) for e in Catalyst.edge_iterator(dsrs) # Updates the exit rate for species s_idx from vertex e.src. - leaving_rates[s_idx, e[1]] += get_transport_rate(tr_pair[2], e, - t_rate_idx_types[s_idx]) + leaving_rates[s_idx, e[1]] += get_transport_rate( + tr_pair[2], e, + t_rate_idx_types[s_idx] + ) end end return t_rate_idx_types, leaving_rates @@ -146,9 +158,10 @@ function (lt_ofun::LatticeTransportODEFunction)(du::AbstractVector, u, p, t) idx_src = get_index(e[1], s, lt_ofun.num_species) idx_dst = get_index(e[2], s, lt_ofun.num_species) du[idx_dst] += get_transport_rate(rates, e, lt_ofun.t_rate_idx_types[s_idx]) * - u[idx_src] + u[idx_src] end end + return end # Defines the functor's effect when applied as a Jacobian. @@ -168,18 +181,20 @@ function (lt_ofun::LatticeTransportODEFunction)(J::AbstractMatrix, u, p, t) end # Updates for the spatial reactions (adds the Jacobian values from the transportation reactions). - J .+= lt_ofun.jac_transport + return J .+= lt_ofun.jac_transport end ### ODEProblem ### # Creates an ODEProblem from a DiscreteSpaceReactionSystem. -function DiffEqBase.ODEProblem(dsrs::DiscreteSpaceReactionSystem, u0_in, tspan, +function DiffEqBase.ODEProblem( + dsrs::DiscreteSpaceReactionSystem, u0_in, tspan, p_in = DiffEqBase.NullParameters(), args...; jac = false, sparse = false, name = nameof(dsrs), include_zero_odes = true, combinatoric_ratelaws = get_combinatoric_ratelaws(reactionsystem(dsrs)), - remove_conserved = false, checks = false, kwargs...) + remove_conserved = false, checks = false, kwargs... + ) if !is_transport_system(dsrs) error("Currently discrete space ODE simulations are only supported when all spatial reactions are `TransportReaction`s.") end @@ -198,12 +213,16 @@ function DiffEqBase.ODEProblem(dsrs::DiscreteSpaceReactionSystem, u0_in, tspan, # Uniform vertex/edge parameters store only a single value (a length 1 vector, or size 1x1 sparse matrix). # In the `ODEProblem` vert_ps and edge_ps are merged (but for building the ODEFunction, they are separate). vert_ps, - edge_ps = dspace_process_p(p_in, vertex_parameters(dsrs), - edge_parameters(dsrs), dsrs) + edge_ps = dspace_process_p( + p_in, vertex_parameters(dsrs), + edge_parameters(dsrs), dsrs + ) # Creates the ODEFunction. - ofun = build_odefunction(dsrs, vert_ps, edge_ps, jac, sparse, name, include_zero_odes, - combinatoric_ratelaws, remove_conserved, checks) + ofun = build_odefunction( + dsrs, vert_ps, edge_ps, jac, sparse, name, include_zero_odes, + combinatoric_ratelaws, remove_conserved, checks + ) # Combines `vert_ps` and `edge_ps` to a single vector with values only (not a map). Creates ODEProblem. pval_dict = Dict([vert_ps; edge_ps]) @@ -212,18 +231,24 @@ function DiffEqBase.ODEProblem(dsrs::DiscreteSpaceReactionSystem, u0_in, tspan, end # Builds an ODEFunction for a spatial ODEProblem. -function build_odefunction(dsrs::DiscreteSpaceReactionSystem, vert_ps::Vector{Pair{R, Vector{T}}}, +function build_odefunction( + dsrs::DiscreteSpaceReactionSystem, vert_ps::Vector{Pair{R, Vector{T}}}, edge_ps::Vector{Pair{S, SparseMatrixCSC{T, Int64}}}, jac::Bool, sparse::Bool, name, include_zero_odes, combinatoric_ratelaws, - remove_conserved, checks) where {R, S, T} + remove_conserved, checks + ) where {R, S, T} # Error check. if remove_conserved throw(ArgumentError("Removal of conserved quantities is currently not supported for `DiscreteSpaceReactionSystem`s")) end # Prepares the inputs to the `LatticeTransportODEFunction` functor. - osys = complete(ode_model(reactionsystem(dsrs); - name, combinatoric_ratelaws, include_zero_odes, checks)) + osys = complete( + ode_model( + reactionsystem(dsrs); + name, combinatoric_ratelaws, include_zero_odes, checks + ) + ) ofunc_dense = ODEFunction(osys; jac = true, sparse = false) ofunc_sparse = ODEFunction(osys; jac = true, sparse = true) transport_rates = make_sidxs_to_transrate_map(vert_ps, edge_ps, dsrs) @@ -233,16 +258,20 @@ function build_odefunction(dsrs::DiscreteSpaceReactionSystem, vert_ps::Vector{Pa jac_transport = nothing jac_prototype = nothing else - jac_sparse = build_jac_prototype(ofunc_sparse.jac_prototype, transport_rates, dsrs; - set_nonzero = jac) + jac_sparse = build_jac_prototype( + ofunc_sparse.jac_prototype, transport_rates, dsrs; + set_nonzero = jac + ) jac_dense = Matrix(jac_sparse) jac_transport = (jac ? (sparse ? jac_sparse : jac_dense) : nothing) jac_prototype = (sparse ? jac_sparse : nothing) end # Creates the `LatticeTransportODEFunction` functor (if `jac`, sets it as the Jacobian as well). - f = LatticeTransportODEFunction(ofunc_dense, [vert_ps; edge_ps], dsrs, sparse, - jac_transport, transport_rates) + f = LatticeTransportODEFunction( + ofunc_dense, [vert_ps; edge_ps], dsrs, sparse, + jac_transport, transport_rates + ) J = (jac ? f : nothing) # Extracts the `Symbol` form for parameters (but not species). Creates and returns the `ODEFunction`. @@ -253,14 +282,18 @@ end # Builds a Jacobian prototype. # If requested, populate it with the constant values of the Jacobian's transportation part. -function build_jac_prototype(ns_jac_prototype::SparseMatrixCSC{Float64, Int64}, +function build_jac_prototype( + ns_jac_prototype::SparseMatrixCSC{Float64, Int64}, transport_rates::Vector{Pair{Int64, SparseMatrixCSC{T, Int64}}}, - dsrs::DiscreteSpaceReactionSystem; set_nonzero = false) where {T} + dsrs::DiscreteSpaceReactionSystem; set_nonzero = false + ) where {T} # Finds the indices of both the transport species, # and the species with transport only (that is, with no non-spatial dynamics but with spatial dynamics). trans_species = [tr[1] for tr in transport_rates] - trans_only_species = filter(s_idx -> !Base.isstored(ns_jac_prototype, s_idx, s_idx), - trans_species) + trans_only_species = filter( + s_idx -> !Base.isstored(ns_jac_prototype, s_idx, s_idx), + trans_species + ) # Finds the indices of all terms in the non-spatial jacobian. ns_jac_prototype_idxs = findnz(ns_jac_prototype) @@ -270,7 +303,7 @@ function build_jac_prototype(ns_jac_prototype::SparseMatrixCSC{Float64, Int64}, # Prepares vectors to store i and j indices of Jacobian entries. idx = 1 num_entries = num_verts(dsrs) * length(ns_i_idxs) + - num_edges(dsrs) * (length(trans_only_species) + length(trans_species)) + num_edges(dsrs) * (length(trans_only_species) + length(trans_species)) i_idxs = Vector{Int}(undef, num_entries) j_idxs = Vector{Int}(undef, num_entries) @@ -324,4 +357,5 @@ function set_jac_transport_values!(jac_prototype, transport_rates, dsrs) # Term due to species arriving to destination vertex. jac_prototype[idx_src, idx_dst] += val end + return end diff --git a/src/spatial_reaction_systems/spatial_reactions.jl b/src/spatial_reaction_systems/spatial_reactions.jl index 89757a82f9..1a8f5fbf5b 100644 --- a/src/spatial_reaction_systems/spatial_reactions.jl +++ b/src/spatial_reaction_systems/spatial_reactions.jl @@ -20,7 +20,7 @@ function isedgeparameter(x, default = false) if iscall(x) && operation(x) === getindex x = first(arguments(x)) end - Symbolics.getmetadata(x, EdgeParameter, default) + return Symbolics.getmetadata(x, EdgeParameter, default) end ### Transport Reaction Structures ### @@ -38,13 +38,13 @@ struct TransportReaction <: AbstractSpatialReaction if any(!MT.isparameter(var) for var in MT.get_variables(rate)) error("TransportReaction rate contains variables: $(filter(var -> !MT.isparameter(var), MT.get_variables(rate))). The rate must consist of parameters only.") end - new(rate, species.val) + return new(rate, species.val) end end # Macro for creating a `TransportReaction`. macro transport_reaction(rateex::ExprValues, species::ExprValues) - make_transport_reaction(striplines(rateex), species) + return make_transport_reaction(striplines(rateex), species) end function make_transport_reaction(rateex, species) # Handle interpolation of variables @@ -69,7 +69,7 @@ function make_transport_reaction(rateex, species) insert!(pexprs.args, idx, :([edgeparameter = true])) end - quote + return quote $pexprs $iv $sexprs @@ -84,8 +84,10 @@ MT.parameters(tr::TransportReaction) = collect(Symbolics.get_variables(tr.rate)) spatial_species(tr::TransportReaction) = [tr.species] # Checks that a `TransportReaction` is valid for a given reaction system. -function check_spatial_reaction_validity(rs::ReactionSystem, tr::TransportReaction; - edge_parameters = []) +function check_spatial_reaction_validity( + rs::ReactionSystem, tr::TransportReaction; + edge_parameters = [] + ) # Checks that the species exist in the reaction system. # (ODE simulation code becomes difficult if this is not required, # as non-spatial jacobian and f function generated from rs are of the wrong size). @@ -103,14 +105,18 @@ function check_spatial_reaction_validity(rs::ReactionSystem, tr::TransportReacti if any(isequal(tr.species, s) && !isequivalent(tr.species, s) for s in species(rs)) error("A transport reaction used a species, $(tr.species), with metadata not matching its discrete space reaction system. Please fetch this species from the reaction system and use it during transport reaction creation.") end - if any(isequal(rs_p, tr_p) && !isequivalent(rs_p, tr_p) - for rs_p in parameters(rs), tr_p in Symbolics.get_variables(tr.rate)) + if any( + isequal(rs_p, tr_p) && !isequivalent(rs_p, tr_p) + for rs_p in parameters(rs), tr_p in Symbolics.get_variables(tr.rate) + ) error("A transport reaction used a parameter with metadata not matching its discrete space reaction system. Please fetch this parameter from the reaction system and use it during transport reaction creation.") end # Checks that no edge parameter occurs among rates of non-spatial reactions. - if any(!isempty(intersect(Symbolics.get_variables(r.rate), edge_parameters)) - for r in reactions(rs)) + return if any( + !isempty(intersect(Symbolics.get_variables(r.rate), edge_parameters)) + for r in reactions(rs) + ) error("Edge parameter(s) were found as a rate of a non-spatial reaction.") end end @@ -124,11 +130,15 @@ end const ep_metadata = Catalyst.EdgeParameter => true function isequivalent(sym1, sym2; ignored_metadata = [MT.SymScope]) isequal(sym1, sym2) || (return false) - if any((md1 != ep_metadata) && (md1[1] ∉ ignored_metadata) && (md1 ∉ sym2.metadata) - for md1 in sym1.metadata) + if any( + (md1 != ep_metadata) && (md1[1] ∉ ignored_metadata) && (md1 ∉ sym2.metadata) + for md1 in sym1.metadata + ) return false - elseif any((md2 != ep_metadata) && (md2[1] ∉ ignored_metadata) && (md2 ∉ sym1.metadata) - for md2 in sym2.metadata) + elseif any( + (md2 != ep_metadata) && (md2[1] ∉ ignored_metadata) && (md2 ∉ sym1.metadata) + for md2 in sym2.metadata + ) return false elseif typeof(sym1) != typeof(sym2) return false @@ -151,7 +161,7 @@ end # Implements custom `hash`. function hash(tr::TransportReaction, h::UInt) h = Base.hash(tr.rate, h) - Base.hash(tr.species, h) + return Base.hash(tr.species, h) end ### Utility ### diff --git a/src/spatial_reaction_systems/utility.jl b/src/spatial_reaction_systems/utility.jl index e1a16b1999..94bc704232 100644 --- a/src/spatial_reaction_systems/utility.jl +++ b/src/spatial_reaction_systems/utility.jl @@ -32,8 +32,10 @@ end # From a parameter input, split it into vertex parameters and edge parameters. # Store these in the desired internal format. -function dspace_process_p(ps_in, ps_vertex_syms::Vector, - ps_edge_syms::Vector, dsrs::DiscreteSpaceReactionSystem) +function dspace_process_p( + ps_in, ps_vertex_syms::Vector, + ps_edge_syms::Vector, dsrs::DiscreteSpaceReactionSystem + ) # p values can be given in various forms. This converts it to a Vector{Pair{Symbolics,...}} form. # Top-level vector: Maps each parameter to its value(s). # Second-level: Contains either a vector (vertex parameters) or a sparse matrix (edge parameters). @@ -110,8 +112,10 @@ function vertex_value_form(values, dsrs::DiscreteSpaceReactionSystem, sym::Symbo end # Converts values to the correct vector form for a Cartesian grid space. -function vertex_value_form(values::AbstractArray, num_verts::Int64, - dspace::CartesianGridRej{N, T}, sym::SymbolicT) where {N, T} +function vertex_value_form( + values::AbstractArray, num_verts::Int64, + dspace::CartesianGridRej{N, T}, sym::SymbolicT + ) where {N, T} if size(values) != dspace.dims throw(ArgumentError("The values for $sym did not have the same format as the space. Expected a $(dspace.dims) array, got one of size $(size(values))")) end @@ -122,8 +126,10 @@ function vertex_value_form(values::AbstractArray, num_verts::Int64, end # Converts values to the correct vector form for a masked grid space. -function vertex_value_form(values::AbstractArray, num_verts::Int64, - dspace::Array{Bool, T}, sym::SymbolicT) where {T} +function vertex_value_form( + values::AbstractArray, num_verts::Int64, + dspace::Array{Bool, T}, sym::SymbolicT + ) where {T} if size(values) != size(dspace) throw(ArgumentError("The values for $sym did not have the same format as the space. Expected a $(size(dspace)) array, got one of size $(size(values))")) end @@ -173,18 +179,22 @@ end # The species is represented by its index (in species(dsrs). # If the rate is uniform across all edges, the transportation rate will be a size (1,1) sparse matrix. # Else, the rate will be a size (num_verts,num_verts) sparse matrix. -function make_sidxs_to_transrate_map(vert_ps::Vector{Pair{R, Vector{T}}}, +function make_sidxs_to_transrate_map( + vert_ps::Vector{Pair{R, Vector{T}}}, edge_ps::Vector{Pair{S, SparseMatrixCSC{T, Int64}}}, - dsrs::DiscreteSpaceReactionSystem) where {R, S, T} + dsrs::DiscreteSpaceReactionSystem + ) where {R, S, T} # Creates a dictionary with each parameter's value(s). p_val_dict = Dict(vcat(vert_ps, edge_ps)) # First, compute a map from species in their symbolics form to their values. # Next, convert to map from species index to values. transport_rates_speciesmap = compute_all_transport_rates(p_val_dict, dsrs) - return Pair{Int64, SparseMatrixCSC{T, Int64}}[speciesmap(reactionsystem(dsrs))[spat_rates[1]] => spat_rates[2] - for spat_rates in - transport_rates_speciesmap] + return Pair{Int64, SparseMatrixCSC{T, Int64}}[ + speciesmap(reactionsystem(dsrs))[spat_rates[1]] => spat_rates[2] + for spat_rates in + transport_rates_speciesmap + ] end # Computes the transport rates for all species with transportation rates. Output is a map @@ -192,8 +202,10 @@ end function compute_all_transport_rates(p_val_dict, dsrs::DiscreteSpaceReactionSystem) # For all species with transportation, compute their transportation rate (across all edges). # This is a vector, pairing each species to these rates. - unsorted_rates = [s => compute_transport_rates(s, p_val_dict, dsrs) - for s in spatial_species(dsrs)] + unsorted_rates = [ + s => compute_transport_rates(s, p_val_dict, dsrs) + for s in spatial_species(dsrs) + ] # Sorts all the species => rate pairs according to their species index in species(dsrs). return sort(unsorted_rates; by = rate -> findfirst(isequal(rate[1]), species(dsrs))) @@ -207,8 +219,13 @@ function compute_transport_rates(s::SymbolicT, p_val_dict, dsrs::DiscreteSpaceRe # Find parameters involved in the rate and create a function evaluating the rate law. rate_law = get_transport_rate_law(s, dsrs) relevant_ps = Symbolics.get_variables(rate_law) - rate_law_func = drop_expr(@RuntimeGeneratedFunction(build_function( - rate_law, relevant_ps...))) + rate_law_func = drop_expr( + @RuntimeGeneratedFunction( + build_function( + rate_law, relevant_ps... + ) + ) + ) # If all these parameters are spatially uniform, the rates become a size (1,1) sparse matrix. # Else, the rates become a size (num_verts,num_verts) sparse matrix. @@ -238,7 +255,7 @@ end # Converts a vector of vectors to a single, long, vector. # These are used when the initial condition is converted to a single vector (from vector of vector form). function expand_component_values(values::Vector{<:Vector}, num_verts::Int64) - vcat([get_vertex_value.(values, vert) for vert in 1:num_verts]...) + return vcat([get_vertex_value.(values, vert) for vert in 1:num_verts]...) end # Gets the index in the u array of species s in vertex vert (when there are num_species species). @@ -252,8 +269,10 @@ end function get_edge_value(values::Vector{T}, edge::Pair{Int64, Int64}) where {T} return (length(values) == 1) ? values[1] : values[edge[1]] end -function get_edge_value(values::SparseMatrixCSC{T, Int64}, - edge::Pair{Int64, Int64}) where {T} +function get_edge_value( + values::SparseMatrixCSC{T, Int64}, + edge::Pair{Int64, Int64} + ) where {T} return (size(values) == (1, 1)) ? values[1, 1] : values[edge[1], edge[2]] end @@ -263,18 +282,23 @@ function get_vertex_value(values::Vector{T}, vert_idx::Int64) where {T} end # Finds the transport rate of a parameter along a specific edge. -function get_transport_rate(transport_rate::SparseMatrixCSC{T, Int64}, - edge::Pair{Int64, Int64}, t_rate_idx_types::Bool) where {T} +function get_transport_rate( + transport_rate::SparseMatrixCSC{T, Int64}, + edge::Pair{Int64, Int64}, t_rate_idx_types::Bool + ) where {T} return t_rate_idx_types ? transport_rate[1, 1] : transport_rate[edge[1], edge[2]] end # For a `LatticeTransportODEFunction`, update its stored parameters (in `mtk_ps`) so that they # the heterogeneous parameters' values correspond to the values in the specified vertex. -function update_mtk_ps!(lt_ofun::LatticeTransportODEFunction, all_ps::Vector{T}, - vert::Int64) where {T} +function update_mtk_ps!( + lt_ofun::LatticeTransportODEFunction, all_ps::Vector{T}, + vert::Int64 + ) where {T} for (setp, idx) in zip(lt_ofun.p_setters, lt_ofun.heterogeneous_vert_p_idxs) setp(lt_ofun.mtk_ps, all_ps[idx][vert]) end + return end # For an expression, compute its values using the provided state and parameter vectors. @@ -298,8 +322,10 @@ function compute_vertex_value(exp, dsrs::DiscreteSpaceReactionSystem; u = [], ps if all(length(value_dict[sym]) == 1 for sym in relevant_syms) return [exp_func([value_dict[sym][1] for sym in relevant_syms]...)] end - return [exp_func([get_vertex_value(value_dict[sym], vert_idx) for sym in relevant_syms]...) - for vert_idx in 1:num_verts(dsrs)] + return [ + exp_func([get_vertex_value(value_dict[sym], vert_idx) for sym in relevant_syms]...) + for vert_idx in 1:num_verts(dsrs) + ] end ### System Property Checks ### diff --git a/src/steady_state_stability.jl b/src/steady_state_stability.jl index e9a57838b8..3d031863f5 100644 --- a/src/steady_state_stability.jl +++ b/src/steady_state_stability.jl @@ -45,8 +45,10 @@ Notes: whether a computed eigenvalue is far away enough from 0 to be reliably used. This selected threshold can be changed through the `tol` argument. """ -function steady_state_stability(u::Vector, rs::ReactionSystem, ps; - tol = 10 * sqrt(eps(ss_val_type(u))), ss_jac = steady_state_jac(rs; u0 = u)) +function steady_state_stability( + u::Vector, rs::ReactionSystem, ps; + tol = 10 * sqrt(eps(ss_val_type(u))), ss_jac = steady_state_jac(rs; u0 = u) + ) # Warning checks. if !isautonomous(rs) error("Attempting to compute stability for a non-autonomous system (e.g. where some rate depend on $(get_iv(rs))). This is not possible.") @@ -108,8 +110,10 @@ Notes: - In practice, this function returns an `ODEProblem` (with a computed Jacobian) set up in such a way that it can be used by the `steady_state_stability` function. """ -function steady_state_jac(rs::ReactionSystem; u0 = [sp => 0.0 for sp in unknowns(rs)], - combinatoric_ratelaws = get_combinatoric_ratelaws(rs)) +function steady_state_jac( + rs::ReactionSystem; u0 = [sp => 0.0 for sp in unknowns(rs)], + combinatoric_ratelaws = get_combinatoric_ratelaws(rs) + ) # If u0 is a vector of values, must be converted to something MTK understands. # Converts u0 to values MTK understands, and checks that potential conservation laws are accounted for. @@ -118,8 +122,10 @@ function steady_state_jac(rs::ReactionSystem; u0 = [sp => 0.0 for sp in unknowns # Creates an `ODEProblem` with a Jacobian. Dummy values for `u0` and `ps` must be provided. ps = [p => 0.0 for p in parameters(rs)] - return ODEProblem(rs, u0, 0, ps; jac = true, combinatoric_ratelaws, - remove_conserved = true) + return ODEProblem( + rs, u0, 0, ps; jac = true, combinatoric_ratelaws, + remove_conserved = true + ) end # Converts a `u` vector from a vector of values to a map. diff --git a/src/unit_helpers.jl b/src/unit_helpers.jl index d735264444..171e240309 100644 --- a/src/unit_helpers.jl +++ b/src/unit_helpers.jl @@ -80,6 +80,7 @@ function Base.showerror(io::IO, err::UnitValidationError) (issue.rhs_unit === nothing) || print(io, "\nrhs unit: ", issue.rhs_unit) isempty(issue.detail) || print(io, "\ndetail: ", issue.detail) end + return end # Emit @warn messages for a vector of UnitValidationIssues. @@ -87,41 +88,77 @@ end function _warn_unit_issues(issues::Vector{UnitValidationIssue}) for issue in issues if issue.kind == :species_unit_mismatch - @warn(string("Species are expected to have units of ", issue.lhs_unit, - " however, species ", issue.context, " has units ", issue.rhs_unit, ".")) + @warn( + string( + "Species are expected to have units of ", issue.lhs_unit, + " however, species ", issue.context, " has units ", issue.rhs_unit, "." + ) + ) elseif issue.kind == :reaction_species_unit_mismatch @warn(string("In ", issue.context, " the ", issue.detail, ".")) elseif issue.kind == :reaction_rate_unit_mismatch - @warn(string( - "Reaction rate laws are expected to have units of ", issue.lhs_unit, - " however, ", issue.context, " has units of ", issue.rhs_unit, ".")) + @warn( + string( + "Reaction rate laws are expected to have units of ", issue.lhs_unit, + " however, ", issue.context, " has units of ", issue.rhs_unit, "." + ) + ) elseif issue.kind == :reaction_side_unit_mismatch - @warn(string("in ", issue.context, - " the substrate units are not consistent with the product units.")) + @warn( + string( + "in ", issue.context, + " the substrate units are not consistent with the product units." + ) + ) elseif issue.kind == :additive_term_unit_mismatch - @warn(string(issue.context, ": additive terms have mismatched units [", - issue.lhs_unit, "] and [", issue.rhs_unit, "].")) + @warn( + string( + issue.context, ": additive terms have mismatched units [", + issue.lhs_unit, "] and [", issue.rhs_unit, "]." + ) + ) elseif issue.kind == :equation_unit_mismatch - @warn(string("Equation unit mismatch in ", issue.context, - ": lhs has units ", issue.lhs_unit, ", rhs has units ", issue.rhs_unit, ".")) + @warn( + string( + "Equation unit mismatch in ", issue.context, + ": lhs has units ", issue.lhs_unit, ", rhs has units ", issue.rhs_unit, "." + ) + ) elseif issue.kind == :comparison_unit_mismatch - @warn(string(issue.context, ": comparison operands have mismatched units [", - issue.lhs_unit, "] and [", issue.rhs_unit, "].")) + @warn( + string( + issue.context, ": comparison operands have mismatched units [", + issue.lhs_unit, "] and [", issue.rhs_unit, "]." + ) + ) elseif issue.kind == :conditional_condition_unit_mismatch - @warn(string(issue.context, ": ifelse condition must be unitless, got [", - issue.rhs_unit, "].")) + @warn( + string( + issue.context, ": ifelse condition must be unitless, got [", + issue.rhs_unit, "]." + ) + ) elseif issue.kind == :conditional_branch_unit_mismatch - @warn(string(issue.context, ": ifelse branches have mismatched units [", - issue.lhs_unit, "] and [", issue.rhs_unit, "].")) + @warn( + string( + issue.context, ": ifelse branches have mismatched units [", + issue.lhs_unit, "] and [", issue.rhs_unit, "]." + ) + ) elseif issue.kind == :exponent_unit_mismatch - @warn(string(issue.context, ": exponent must be unitless, got [", - issue.rhs_unit, "].")) + @warn( + string( + issue.context, ": exponent must be unitless, got [", + issue.rhs_unit, "]." + ) + ) elseif issue.kind == :symbolic_stoichiometry @warn(string(issue.context, ": ", issue.detail, ".")) elseif issue.kind == :symbolic_exponent @warn(string(issue.context, ": ", issue.detail, ".")) end end + return end _validation_push!(issues, issue::UnitValidationIssue) = isnothing(issues) ? nothing : push!(issues, issue) @@ -143,7 +180,7 @@ from the system's brownian and poissonian variables: - Brownians get units `time^(-1/2)` (from the Wiener process derivative `dW/dt`). - Poissonians get units from their associated rate parameter (via `getpoissonianrate`). """ -function catalyst_get_unit(x, noise_units=nothing) +function catalyst_get_unit(x, noise_units = nothing) # Unwrap Symbolics wrappers (Num, CallAndWrap, etc.) to the underlying symbolic x = Symbolics.unwrap(x) if x isa DQ.AbstractQuantity @@ -160,9 +197,12 @@ end # lose precision in compound arithmetic, leading to silent unit-check failures. function _check_symbolic_dims(u::DQ.AbstractQuantity, x) if !(DQ.dimension(u) isa DQ.SymbolicDimensions) - throw(ArgumentError( - "Unit metadata on $x uses concrete Dimensions (from u\"...\"). " * - "Catalyst requires SymbolicDimensions — use us\"...\" instead.")) + throw( + ArgumentError( + "Unit metadata on $x uses concrete Dimensions (from u\"...\"). " * + "Catalyst requires SymbolicDimensions — use us\"...\" instead." + ) + ) end return u end @@ -206,7 +246,7 @@ function _cgu_symbolic(x, noise_units) _is_unitless(exp_u) || return SYM_UNITLESS exp_val = pargs[2] if SymbolicUtils.isconst(exp_val) - return base_u ^ value(exp_val) + return base_u^value(exp_val) else # Symbolic exponent on unitful base — unit is indeterminate. # Validation code pre-checks via _has_symbolic_unitful_pow before @@ -310,8 +350,8 @@ function _has_symbolic_unitful_pow(x) # Only match unitful base with unitless symbolic exponent (e.g. A^n). # Unitful exponents (e.g. V^t) are separately caught as :exponent_unit_mismatch. if !_is_unitless(catalyst_get_unit(args[1])) && - !SymbolicUtils.isconst(args[2]) && - _is_unitless(catalyst_get_unit(args[2])) + !SymbolicUtils.isconst(args[2]) && + _is_unitless(catalyst_get_unit(args[2])) return true end end @@ -328,13 +368,13 @@ Walk a symbolic expression tree and check that all additive terms have consisten units. Returns `true` if valid, `false` if any addition has terms with mismatched units. Issues warnings for each mismatch found. """ -function _validate_unit_expr(expr, label, noise_units=nothing; issues=nothing, warn::Bool = true) +function _validate_unit_expr(expr, label, noise_units = nothing; issues = nothing, warn::Bool = true) x = expr isa Symbolics.Num ? Symbolics.unwrap(expr) : expr x isa SymbolicT || return true return _vue_walk(x, label, noise_units; issues, warn) end -function _vue_walk(x, label, noise_units; issues=nothing, warn::Bool = true) +function _vue_walk(x, label, noise_units; issues = nothing, warn::Bool = true) valid = true if SymbolicUtils.isadd(x) args = SymbolicUtils.arguments(x) @@ -343,8 +383,10 @@ function _vue_walk(x, label, noise_units; issues=nothing, warn::Bool = true) other_unit = catalyst_get_unit(args[i], noise_units) if !_units_match(first_unit, other_unit) valid = false - issue = UnitValidationIssue(:additive_term_unit_mismatch, string(label), - first_unit, other_unit, "Additive terms have mismatched units.") + issue = UnitValidationIssue( + :additive_term_unit_mismatch, string(label), + first_unit, other_unit, "Additive terms have mismatched units." + ) _validation_push!(issues, issue) warn && @warn("$label: additive terms have mismatched units [$first_unit] and [$other_unit] in $x.") end @@ -362,8 +404,10 @@ function _vue_walk(x, label, noise_units; issues=nothing, warn::Bool = true) exp_unit = catalyst_get_unit(args[2], noise_units) if !_is_unitless(exp_unit) valid = false - issue = UnitValidationIssue(:exponent_unit_mismatch, string(label), SYM_UNITLESS, - exp_unit, "Exponent must be unitless.") + issue = UnitValidationIssue( + :exponent_unit_mismatch, string(label), SYM_UNITLESS, + exp_unit, "Exponent must be unitless." + ) _validation_push!(issues, issue) warn && @warn("$label: exponent has non-unitless unit [$exp_unit] in $x.") end @@ -383,8 +427,10 @@ function _vue_walk(x, label, noise_units; issues=nothing, warn::Bool = true) rhs_unit = catalyst_get_unit(args[2], noise_units) if !_units_match(lhs_unit, rhs_unit) valid = false - issue = UnitValidationIssue(:comparison_unit_mismatch, string(label), lhs_unit, - rhs_unit, "Comparison operands must have matching units.") + issue = UnitValidationIssue( + :comparison_unit_mismatch, string(label), lhs_unit, + rhs_unit, "Comparison operands must have matching units." + ) _validation_push!(issues, issue) warn && @warn("$label: comparison operands have mismatched units [$lhs_unit] and [$rhs_unit] in $x.") end @@ -393,8 +439,10 @@ function _vue_walk(x, label, noise_units; issues=nothing, warn::Bool = true) cond_unit = catalyst_get_unit(args[1], noise_units) if !_is_unitless(cond_unit) valid = false - issue = UnitValidationIssue(:conditional_condition_unit_mismatch, string(label), - SYM_UNITLESS, cond_unit, "ifelse condition must be unitless.") + issue = UnitValidationIssue( + :conditional_condition_unit_mismatch, string(label), + SYM_UNITLESS, cond_unit, "ifelse condition must be unitless." + ) _validation_push!(issues, issue) warn && @warn("$label: ifelse condition is not unitless [$cond_unit] in $x.") end @@ -402,8 +450,10 @@ function _vue_walk(x, label, noise_units; issues=nothing, warn::Bool = true) false_unit = catalyst_get_unit(args[3], noise_units) if !_units_match(true_unit, false_unit) valid = false - issue = UnitValidationIssue(:conditional_branch_unit_mismatch, string(label), - true_unit, false_unit, "ifelse branches must have matching units.") + issue = UnitValidationIssue( + :conditional_branch_unit_mismatch, string(label), + true_unit, false_unit, "ifelse branches must have matching units." + ) _validation_push!(issues, issue) warn && @warn("$label: ifelse branches have mismatched units [$true_unit] and [$false_unit] in $x.") end @@ -429,16 +479,22 @@ Validate units in an `Equation`. Checks: The optional `noise_units` keyword is a `Dict` mapping noise variable symbols to their effective units (built by `_build_noise_units` during system validation). """ -function _validate_equation(eq::Equation; noise_units=nothing, issues=nothing, warn::Bool = true) +function _validate_equation(eq::Equation; noise_units = nothing, issues = nothing, warn::Bool = true) valid = true eq_str = string(eq) if _has_symbolic_unitful_pow(eq.lhs) || _has_symbolic_unitful_pow(eq.rhs) valid = false - issue = UnitValidationIssue(:symbolic_exponent, eq_str, nothing, nothing, - "Symbolic exponent on unitful base is not supported for unit validation") + issue = UnitValidationIssue( + :symbolic_exponent, eq_str, nothing, nothing, + "Symbolic exponent on unitful base is not supported for unit validation" + ) _validation_push!(issues, issue) - warn && @warn(string(eq_str, ": symbolic exponent on unitful base, ", - "cannot validate units.")) + warn && @warn( + string( + eq_str, ": symbolic exponent on unitful base, ", + "cannot validate units." + ) + ) return valid end valid &= _validate_unit_expr(eq.lhs, string(eq_str, ": lhs"), noise_units; issues, warn) @@ -447,11 +503,17 @@ function _validate_equation(eq::Equation; noise_units=nothing, issues=nothing, w rhs_unit = catalyst_get_unit(eq.rhs, noise_units) if !_units_match(lhs_unit, rhs_unit) valid = false - issue = UnitValidationIssue(:equation_unit_mismatch, string(eq), lhs_unit, - rhs_unit, "LHS and RHS units do not match.") + issue = UnitValidationIssue( + :equation_unit_mismatch, string(eq), lhs_unit, + rhs_unit, "LHS and RHS units do not match." + ) _validation_push!(issues, issue) - warn && @warn(string("Equation unit mismatch in ", eq, - ": lhs has units ", lhs_unit, ", rhs has units ", rhs_unit, ".")) + warn && @warn( + string( + "Equation unit mismatch in ", eq, + ": lhs has units ", lhs_unit, ", rhs has units ", rhs_unit, "." + ) + ) end return valid end @@ -470,7 +532,7 @@ function _build_noise_units(rs) timeunit = catalyst_get_unit(get_iv(rs)) noise_units = Dict{Any, Any}() for b in MT.get_brownians(rs) - noise_units[Symbolics.unwrap(b)] = timeunit^(-1//2) + noise_units[Symbolics.unwrap(b)] = timeunit^(-1 // 2) end for p in MT.get_poissonians(rs) rate = MT.getpoissonianrate(p) diff --git a/test/dsl/dsl_basic_model_construction.jl b/test/dsl/dsl_basic_model_construction.jl index 1a321d6478..00eed3051a 100644 --- a/test/dsl/dsl_basic_model_construction.jl +++ b/test/dsl/dsl_basic_model_construction.jl @@ -3,7 +3,7 @@ # Fetch packages. using DiffEqBase, Catalyst, Random, Test using ModelingToolkitBase: operation, get_unknowns, get_ps, get_eqs, get_systems, - get_iv, nameof + get_iv, nameof using Symbolics: iscall # Sets stable rng number. @@ -20,7 +20,7 @@ include("../test_functions.jl") ### Declares Testing Functions ### function unpacksys(sys) - get_eqs(sys), get_iv(sys), get_unknowns(sys), get_ps(sys), nameof(sys), get_systems(sys) + return get_eqs(sys), get_iv(sys), get_unknowns(sys), get_ps(sys), nameof(sys), get_systems(sys) end opname(x) = iscall(x) ? nameof(operation(x)) : nameof(x) @@ -50,7 +50,7 @@ function basic_test(rn, N, unknowns_syms, p_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) + return @test issetequal(map(opname, ps), p_syms) end ## Run Tests ### @@ -116,23 +116,35 @@ end # Test basic properties of networks. let - basic_test(reaction_networks_standard[1], 10, [:X1, :X2, :X3], - [:p1, :p2, :p3, :k1, :k2, :k3, :k4, :d1, :d2, :d3]) + 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]) + 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]) + 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_conserved[1], 6, [:X1, :X2, :X3], - [:k1, :k2, :k3, :k4, :k5, :k6]) + 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_conserved[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]) @@ -179,13 +191,13 @@ let different_arrow_8 = @reaction_network begin p, 2X1 < ∅ k1, X2 ← X1 - (k2, k3), X3 ⟻ (X2,X2) + (k2, k3), X3 ⟻ (X2, X2) d, ∅ ↼ X3 end push!(identical_networks_1, reaction_networks_standard[8] => different_arrow_8) for networks in identical_networks_1 - for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] + for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2, 1.0e3] u0 = rnd_u0(networks[1], rng; factor) p = rnd_ps(networks[1], rng; factor) t = rand(rng) @@ -211,12 +223,14 @@ let # 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] + for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2, 1.0e3] 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]] + 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) @@ -267,7 +281,7 @@ let push!(identical_networks_2, reaction_networks_standard[7] => differently_written_8) for networks in identical_networks_2 - for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] + for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2, 1.0e3] u0 = rnd_u0(networks[1], rng; factor) p = rnd_ps(networks[1], rng; factor) t = rand(rng) @@ -293,24 +307,32 @@ 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, [: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)]) + 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,X1) → X2 - (0.0, 2.1), X2 → (X3,X3) - (901.0, 63.5), (X3,X3) → (X4,X4) + (3.1, 3.2), (X1, X1) → X2 + (0.0, 2.1), X2 → (X3, X3) + (901.0, 63.5), (X3, X3) → (X4, X4) 7, X4 → X5 8, X4 → X5 1.0, X5 ⟶ ∅ end push!(identical_networks_3, reaction_networks_standard[10] => 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]) + 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 (networks, p_1) in zip(identical_networks_3, parameter_sets) - for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] + for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2, 1.0e3] u0 = rnd_u0(networks[1], rng; factor) t = rand(rng) @@ -327,27 +349,33 @@ let @parameters v1 K1 v2 K2 k1 k2 k3 k4 k5 p d @species X1(t) X2(t) X3(t) X4(t) X5(t) - rxs_1 = [Reaction(p, nothing, [X1], nothing, [2]), + 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)] + 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]), + 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)] + 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_conserved[3] => rs_2) - rxs_3 = [Reaction(k1, [X1], [X2], [1], [1]), + 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])] + 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) @@ -356,7 +384,7 @@ let @test alleq(get_unknowns(networks[1]), get_unknowns(networks[2])) @test alleq(get_ps(networks[1]), get_ps(networks[2])) @test ModelingToolkitBase.get_systems(networks[1]) == - ModelingToolkitBase.get_systems(networks[2]) + ModelingToolkitBase.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) @@ -380,12 +408,14 @@ let (t, k6), X3 ↔ X1 end - for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] + for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2, 1.0e3] τ = rand(rng) u = rnd_u0(reaction_networks_conserved[1], rng; factor) p_2 = rnd_ps(time_network, rng; factor) - p_1 = [p_2; reaction_networks_conserved[1].k1 => τ; - reaction_networks_conserved[1].k4 => τ; reaction_networks_conserved[1].k5 => τ] + p_1 = [ + p_2; reaction_networks_conserved[1].k1 => τ; + reaction_networks_conserved[1].k4 => τ; reaction_networks_conserved[1].k5 => τ + ] @test f_eval(reaction_networks_conserved[1], u, p_1, τ) ≈ f_eval(time_network, u, p_2, τ) @test jac_eval(reaction_networks_conserved[1], u, p_1, τ) ≈ jac_eval(time_network, u, p_2, τ) @@ -410,7 +440,9 @@ let (m, M), z ⟷ Z end - @reaction_network begin (1.0, 1.0), i ⟷ T end + @reaction_network begin + (1.0, 1.0), i ⟷ T + end @reaction_network begin (å, Å), ü ⟷ Ü @@ -503,21 +535,29 @@ end # Test that symbols with special meanings are handled properly. let - test_network = @reaction_network begin t * k, X --> ∅ end + 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_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_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_network = @reaction_network begin + ℯ, X --> ∅ + end @test length(species(test_network)) == 1 @test length(parameters(test_network)) == 0 @test reactions(test_network)[1].rate == ℯ @@ -528,10 +568,10 @@ end # Erroneous `@reaction` usage. let # Bi-directional reaction using the `@reaction` macro. - @test_throws Exception @eval @reaction (k1,k2), X1 <--> X2 + @test_throws Exception @eval @reaction (k1, k2), X1 <--> X2 # Bundles reactions. - @test_throws Exception @eval @reaction k, (X1,X2) --> 0 + @test_throws Exception @eval @reaction k, (X1, X2) --> 0 end # Tests that malformed reactions yields errors. @@ -541,12 +581,12 @@ let d, X --> 0, Y --> 0 end @test_throws Exception @eval @reaction_network begin - d, X --> 0, [misc="Ok metadata"], [description="Metadata in (erroneously) extra []."] + d, X --> 0, [misc = "Ok metadata"], [description = "Metadata in (erroneously) extra []."] end # Checks that incorrect bundling yields error. @test_throws Exception @eval @reaction_network begin - (k1,k2,k3), (X1,X2) --> 0 + (k1, k2, k3), (X1, X2) --> 0 end # Checks that incorrect stoichiometric expression yields an error. @@ -573,19 +613,41 @@ let @test_throws Exception @eval @reaction 1, 0 --> t # @reaction_network macro, symbols that cannot be in the rate. - @test_throws Exception @eval @reaction_network begin im, 0 --> X end - @test_throws Exception @eval @reaction_network begin nothing, 0 --> X end - @test_throws Exception @eval @reaction_network begin Γ, 0 --> X end - @test_throws Exception @eval @reaction_network begin ∅, 0 --> X end + @test_throws Exception @eval @reaction_network begin + im, 0 --> X + end + @test_throws Exception @eval @reaction_network begin + nothing, 0 --> X + end + @test_throws Exception @eval @reaction_network begin + Γ, 0 --> X + end + @test_throws Exception @eval @reaction_network begin + ∅, 0 --> X + end # @reaction_network macro, symbols that cannot be a reactant. - @test_throws Exception @eval @reaction_network begin 1, 0 --> im end - @test_throws Exception @eval @reaction_network begin 1, 0 --> nothing end - @test_throws Exception @eval @reaction_network begin 1, 0 --> Γ end - @test_throws Exception @eval @reaction_network begin 1, 0 --> ℯ end - @test_throws Exception @eval @reaction_network begin 1, 0 --> pi end - @test_throws Exception @eval @reaction_network begin 1, 0 --> π end - @test_throws Exception @eval @reaction_network begin 1, 0 --> t end + @test_throws Exception @eval @reaction_network begin + 1, 0 --> im + end + @test_throws Exception @eval @reaction_network begin + 1, 0 --> nothing + end + @test_throws Exception @eval @reaction_network begin + 1, 0 --> Γ + end + @test_throws Exception @eval @reaction_network begin + 1, 0 --> ℯ + end + @test_throws Exception @eval @reaction_network begin + 1, 0 --> pi + end + @test_throws Exception @eval @reaction_network begin + 1, 0 --> π + end + @test_throws Exception @eval @reaction_network begin + 1, 0 --> t + end # Checks that non-supported arrow type usage yields error. @test_throws Exception @eval @reaction_network begin @@ -597,15 +659,15 @@ end # Checks for two, more than two, and for functional rates. Uses different arrow types and directions. let @test_throws Exception @eval @reaction_network begin - (k1,k2), X --> 0 + (k1, k2), X --> 0 end @test_throws Exception @eval @reaction_network begin - (k1,k2,k3), X → 0 + (k1, k2, k3), X → 0 end @test_throws Exception @eval @reaction_network begin - (k1,k1), X => Y + (k1, k1), X => Y end @test_throws Exception @eval @reaction_network begin - (mm(X,v1,K1),mm(X,v2,K2)), 0 <-- 0 + (mm(X, v1, K1), mm(X, v2, K2)), 0 <-- 0 end end diff --git a/test/extensions/bifurcation_kit.jl b/test/extensions/bifurcation_kit.jl index 9ccdff6eee..ec30219d70 100644 --- a/test/extensions/bifurcation_kit.jl +++ b/test/extensions/bifurcation_kit.jl @@ -26,7 +26,7 @@ let 1, 2X + Y → 3X B, X → Y 1, X → ∅ - (k1*Y, k2), V <--> W + (k1 * Y, k2), V <--> W end @unpack A, B, k1 = extended_brusselator u0_guess = [:X => 1.0, :Y => 1.0, :V => 0.0, :W => 0.0] @@ -41,15 +41,15 @@ let # Checks computed V values are correct (Formula: V = k2*(V0+W0)/(k1*Y+k2), where Y=2*B.) B_vals = getfield.(bif_dia.γ.branch, :param) V_vals = getfield.(bif_dia.γ.branch, :x) - @test all(SymbolicUtils.unwrap_const.(V_vals) .≈ 0.5*(1.0+2.0) ./ (0.1 .* 2*B_vals .+ 0.5)) + @test all(SymbolicUtils.unwrap_const.(V_vals) .≈ 0.5 * (1.0 + 2.0) ./ (0.1 .* 2 * B_vals .+ 0.5)) # Checks that the bifurcation point is correct. @test length(bif_dia.γ.specialpoint) == 3 # Includes start and end point. hopf_bif_point = filter(sp -> sp.type == :hopf, bif_dia.γ.specialpoint)[1] - @test isapprox(hopf_bif_point.param, 1.5, atol=1e-5) + @test isapprox(hopf_bif_point.param, 1.5, atol = 1.0e-5) # Tests that an error is thrown if information of conserved species is not fully provided. - @test_throws Exception BifurcationProblem(extended_brusselator, u0_guess, p_start, :B; plot_var=:V, u0 = []) + @test_throws Exception BifurcationProblem(extended_brusselator, u0_guess, p_start, :B; plot_var = :V, u0 = []) end # Bistable switch. @@ -62,26 +62,26 @@ end let # Creates BifurcationProblem via Catalyst. bistable_switch = @reaction_network begin - 0.1 + hill(X,5.0,K,3), 0 --> X + 0.1 + hill(X, 5.0, K, 3), 0 --> X 1.0, X --> 0 end @unpack X, K = bistable_switch u0_guess = [X => 1.0] p_start = [K => 2.5] - bprob = BifurcationProblem(bistable_switch, u0_guess, p_start, K; jac=false, plot_var=X) + bprob = BifurcationProblem(bistable_switch, u0_guess, p_start, K; jac = false, plot_var = X) # Creates BifurcationProblem via BifurcationKit. function bistable_switch_BK(u, p) X, = u K, = p - return [0.1 + 5.0*(X^3)/(X^3 + K^3) - 1.0*X] + return [0.1 + 5.0 * (X^3) / (X^3 + K^3) - 1.0 * X] end bprob_BK = BifurcationProblem(bistable_switch_BK, [1.0], [2.5], (BifurcationKit.@optic _[1]); record_from_solution = (x, p; k...) -> x[1]) # Check the same function have been generated. bprob.u0 == bprob_BK.u0 bprob.params == bprob_BK.params - for repeat = 1:20 + for repeat in 1:20 u0 = rand(rng, 1) p = rand(rng, 1) @test bprob_BK.VF.F(u0, p) ≈ bprob.VF.F(u0, p) @@ -92,19 +92,19 @@ end # Tests with defaults within nested networks. let rn1 = @network_component rn1 begin - @parameters p=1.0 + @parameters p = 1.0 (p, d), 0 <--> X end rn2 = @network_component rn2 begin - @parameters p=2.0 + @parameters p = 2.0 (p, d), 0 <--> X end rn3 = @network_component rn3 begin - @parameters p=3.0 + @parameters p = 3.0 (p, d), 0 <--> X end rn4 = @network_component rn4 begin - @parameters p=4.0 + @parameters p = 4.0 (p, d), 0 <--> X end @named rn3 = compose(rn3, [rn4]) @@ -125,25 +125,25 @@ let bprob = BifurcationProblem(rn, u0_guess, p_start, d; plot_var = X) opts_br = ContinuationPar(dsmin = 0.0001, dsmax = 0.001, ds = 0.0001, max_steps = 10000, p_min = p_span[1], p_max = p_span[2], n_inversion = 4) bif_dia = bifurcationdiagram(bprob, PALC(), 2, opts_br; bothside = true) - @test bif_dia.γ.branch[end].x ≈ 1.0/6 + @test bif_dia.γ.branch[end].x ≈ 1.0 / 6 # Checks second layer (1). bprob = BifurcationProblem(rn, u0_guess, p_start, rn2.d; plot_var = rn2.X) opts_br = ContinuationPar(dsmin = 0.0001, dsmax = 0.001, ds = 0.0001, max_steps = 10000, p_min = p_span[1], p_max = p_span[2], n_inversion = 4) bif_dia = bifurcationdiagram(bprob, PALC(), 2, opts_br; bothside = true) - @test bif_dia.γ.branch[end].x ≈ 2.0/6 + @test bif_dia.γ.branch[end].x ≈ 2.0 / 6 # Checks second layer (2). bprob = BifurcationProblem(rn, u0_guess, p_start, rn3.d; plot_var = rn3.X) opts_br = ContinuationPar(dsmin = 0.0001, dsmax = 0.001, ds = 0.0001, max_steps = 10000, p_min = p_span[1], p_max = p_span[2], n_inversion = 4) bif_dia = bifurcationdiagram(bprob, PALC(), 2, opts_br; bothside = true) - @test bif_dia.γ.branch[end].x ≈ 3.0/6 + @test bif_dia.γ.branch[end].x ≈ 3.0 / 6 # Checks third layer. bprob = BifurcationProblem(rn, u0_guess, p_start, rn3.rn4.d; plot_var = rn3.rn4.X) opts_br = ContinuationPar(dsmin = 0.0001, dsmax = 0.001, ds = 0.0001, max_steps = 10000, p_min = p_span[1], p_max = p_span[2], n_inversion = 4) bif_dia = bifurcationdiagram(bprob, PALC(), 2, opts_br; bothside = true) - @test bif_dia.γ.branch[end].x ≈ 4.0/6 + @test bif_dia.γ.branch[end].x ≈ 4.0 / 6 end end @@ -191,7 +191,7 @@ let @species X1(t) X2(t) rxs = [ Reaction(k1, [X1], [X2]), - Reaction(k2, [X2], [X1]) + Reaction(k2, [X2], [X1]), ] @named rs = ReactionSystem(rxs, t) rs = complete(rs) @@ -222,10 +222,10 @@ let @parameters k @variables C(t) @equations begin - D(V) ~ k*X - V - 0 ~ X/V - C + D(V) ~ k * X - V + 0 ~ X / V - C end - (p/V,d/V), 0 <--> X + (p / V, d / V), 0 <--> X end u0_guess = [:X => 1.0, :V => 1.0, :C => 1.0] p_start = [:p => 2.0, :d => 1.0, :k => 5.0] @@ -255,7 +255,7 @@ end let # Create t-dependant model. rn = @reaction_network begin - (p/t,d), 0 <--> X + (p / t, d), 0 <--> X end u0_guess = [:X => 1.0] p_start = [:p => 1.0, :d => 0.2] @@ -272,15 +272,15 @@ let end rn = complete(rn) - u0_guess = [:A => 1., :B => 1.] - p_start = [:k => 2.] + u0_guess = [:A => 1.0, :B => 1.0] + p_start = [:k => 2.0] - # Computes bifurcation diagram and checks that it can be sucesfully plotted. - bprob = BifurcationProblem(rn, u0_guess, p_start, :k; plot_var = :A, u0 = [:A => 5., :B => 3.]) + # Computes bifurcation diagram and checks that it can be successfully plotted. + bprob = BifurcationProblem(rn, u0_guess, p_start, :k; plot_var = :A, u0 = [:A => 5.0, :B => 3.0]) p_span = (0.1, 6.0) opts_br = ContinuationPar(dsmin = 0.0001, dsmax = 0.001, ds = 0.0001, max_steps = 10000, p_min = p_span[1], p_max = p_span[2], n_inversion = 4) bif_dia = bifurcationdiagram(bprob, PALC(), 2, opts_br; bothside = true) - plot(bif_dia, xlabel = "k", ylabel = "A", xlims = (0, 6), ylims=(0,8)) + plot(bif_dia, xlabel = "k", ylabel = "A", xlims = (0, 6), ylims = (0, 8)) # Checks that the correct values are computed in the bifurcation diagram. As = getfield.(bif_dia.γ.branch, :x) diff --git a/test/extensions/dspace_simulation_plotting.jl b/test/extensions/dspace_simulation_plotting.jl index c577a40531..f066177714 100644 --- a/test/extensions/dspace_simulation_plotting.jl +++ b/test/extensions/dspace_simulation_plotting.jl @@ -14,7 +14,7 @@ using JumpProcesses, OrdinaryDiffEqTsit5, Test let # Creates the `DiscreteSpaceReactionSystem` model. rs = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X end diffusion_rx = @transport_reaction D X for space in [CartesianGrid(3), [true, true, false]] @@ -41,7 +41,7 @@ let # Plots the kymograph and checks that a stored value is correct. fig, ax, hm = dspace_kymograph(sol, :X, dsrs) - @test_broken hm[3].val[end,1] ≈ sol.u[end][1] # Interface for accessing internals in makie plots have changed. Need to update test. + @test_broken hm[3].val[end, 1] ≈ sol.u[end][1] # Interface for accessing internals in makie plots have changed. Need to update test. end end end @@ -53,10 +53,10 @@ end let # Creates the `DiscreteSpaceReactionSystem` model. rs = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X end diffusion_rx = @transport_reaction D X - for space in [CartesianGrid((2,2)), [true true; false true]] + for space in [CartesianGrid((2, 2)), [true true; false true]] dsrs = DiscreteSpaceReactionSystem(rs, [diffusion_rx], space) # Simulates the model (using ODE and jumps). @@ -87,7 +87,7 @@ let d, X --> 0 end diffusion_rx = @transport_reaction D X - space = CartesianGrid((2,2,2)) + space = CartesianGrid((2, 2, 2)) dsrs = DiscreteSpaceReactionSystem(rs, [diffusion_rx], space) oprob = ODEProblem(dsrs, [:X => 1.0], 1.0, [:d => 1.0, :D => 0.2]) osol = solve(oprob, Tsit5()) @@ -103,7 +103,7 @@ end let # Creates the `DiscreteSpaceReactionSystem` model. rs = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X end diffusion_rx = @transport_reaction D X space = Graphs.SimpleGraphs.cycle_graph(4) diff --git a/test/extensions/graphmakie.jl b/test/extensions/graphmakie.jl index 5c3de42b23..c2eb52d816 100644 --- a/test/extensions/graphmakie.jl +++ b/test/extensions/graphmakie.jl @@ -12,17 +12,21 @@ let srg = Catalyst.species_reaction_graph(brusselator) s = length(species(brusselator)) - edgel = Graphs.Edge.([(s+1, 1), - (1, s+2), - (2, s+2), - (s+2, 1), - (s+3, 2), - (1, s+3), - (1, s+4)]) + edgel = Graphs.Edge.( + [ + (s + 1, 1), + (1, s + 2), + (2, s + 2), + (s + 2, 1), + (s + 3, 2), + (1, s + 3), + (1, s + 4), + ] + ) @test all(∈(collect(Graphs.edges(srg))), edgel) MAPK = @reaction_network MAPK begin - (k₁, k₂),KKK + E1 <--> KKKE1 + (k₁, k₂), KKK + E1 <--> KKKE1 k₃, KKKE1 --> KKK_ + E1 (k₄, k₅), KKK_ + E2 <--> KKKE2 k₆, KKKE2 --> KKK + E2 @@ -32,7 +36,7 @@ let k₁₂, KKPKKK_ --> KKPP + KKK_ (k₁₃, k₁₄), KKP + KKPase <--> KKPKKPase k₁₅, KKPPKKPase --> KKP + KKPase - k₁₆,KKPKKPase --> KK + KKPase + k₁₆, KKPKKPase --> KK + KKPase (k₁₇, k₁₈), KKPP + KKPase <--> KKPPKKPase (k₁₉, k₂₀), KKPP + K <--> KKPPK k₂₁, KKPPK --> KKPP + KP @@ -77,9 +81,9 @@ let srg = CGME.SRGraphWrap(rn) s = length(species(rn)) @test ne(srg) == 8 - @test Graphs.Edge(2, s+3) ∈ srg.multiedges + @test Graphs.Edge(2, s + 3) ∈ srg.multiedges # Since B is both a dep and a reactant - @test count(==(Graphs.Edge(2, s+3)), edges(srg)) == 2 + @test count(==(Graphs.Edge(2, s + 3)), edges(srg)) == 2 f = plot_network(rn) save("fig.png", f) @@ -99,13 +103,13 @@ let s = length(species(rn)) @test ne(srg) == 8 # Since A, B is both a dep and a reactant - @test count(==(Graphs.Edge(1, s+2)), edges(srg)) == 2 - @test count(==(Graphs.Edge(2, s+3)), edges(srg)) == 2 + @test count(==(Graphs.Edge(1, s + 2)), edges(srg)) == 2 + @test count(==(Graphs.Edge(2, s + 3)), edges(srg)) == 2 end function test_edgeorder(rn) # The initial edgelabels in `plot_complexes` is given by the order of reactions in reactions(rn). - D = incidencemat(rn; sparse=true) + D = incidencemat(rn; sparse = true) rxs = reactions(rn) edgelist = Vector{Graphs.SimpleEdge{Int}}() rows = rowvals(D) @@ -146,7 +150,7 @@ let (k2, k3), C <--> D k4, A --> B hillr(D, α, K, n), C --> D - k5*B, A --> B + k5 * B, A --> B end rxorder = test_edgeorder(rn) edgelabels = [repr(rx.rate) for rx in reactions(rn)] diff --git a/test/extensions/homotopy_continuation.jl b/test/extensions/homotopy_continuation.jl index 0657aba6ab..d76a54ccc4 100644 --- a/test/extensions/homotopy_continuation.jl +++ b/test/extensions/homotopy_continuation.jl @@ -17,8 +17,8 @@ include("../test_functions.jl") let # Creates the model. rs = @reaction_network begin - (k1,k2), X1 <--> X2 - (k3,k4), 2X2 + X3 <--> X2_2X3 + (k1, k2), X1 <--> X2 + (k3, k4), 2X2 + X3 <--> X2_2X3 end @unpack k1, k2, k3, k4 = rs ps = [k1 => 1.0, k2 => 2.0, k3 => 2.0, k4 => 2.0] @@ -27,7 +27,7 @@ let # 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, seed = 0x000004d1) hc_ss = Pair.(unknowns(rs), hc_ss[1]) - @test maximum(abs.(f_eval(rs, hc_ss, ps, 0.0))) ≈ 0.0 atol = 1e-12 + @test maximum(abs.(f_eval(rs, hc_ss, ps, 0.0))) ≈ 0.0 atol = 1.0e-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) @@ -47,7 +47,7 @@ let ps = [:k3 => 1.0, :k2 => 2.0, :k4 => 1.5, :k1 => 8.0] hc_ss_1 = hc_steady_states(wilhelm_2009_model, ps; seed = 0x000004d1, show_progress = false) - @test sort(hc_ss_1, by = sol->sol[1]) ≈ [[0.0, 0.0], [0.5, 2.0], [4.5, 6.0]] + @test sort(hc_ss_1, by = sol -> sol[1]) ≈ [[0.0, 0.0], [0.5, 2.0], [4.5, 6.0]] hc_ss_2 = hc_steady_states(wilhelm_2009_model, ps; seed = 0x000004d2, show_progress = false) hc_ss_3 = hc_steady_states(wilhelm_2009_model, ps; seed = 0x000004d2, show_progress = false) @@ -61,28 +61,28 @@ end # Tests where input ps/u0 are tuples with mixed types. 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 - (kX1,kX2), X1 <--> X2 - (kY1,kY2), Y1 <--> Y2 - (kZ1,kZ2), Z1 <--> Z2 + @parameters kX1 = 1.0 kX2 = 2.0 kY1 = 12345.0 + @species X1(t) = 0.1 X2(t) = 0.2 Y1(t) = 12345.0 + (kX1, kX2), X1 <--> X2 + (kY1, kY2), Y1 <--> Y2 + (kZ1, kZ2), Z1 <--> Z2 end ps = (:kY1 => 1.0, :kY2 => 3, :kZ1 => 1.0, :kZ2 => 4.0) u0_1 = (:Y1 => 1.0, :Y2 => 3, :Z1 => 10, :Z2 => 40.0) - ss_1 = sort(hc_steady_states(rs_1, ps; u0 = u0_1, show_progress = false, seed = 0x000004d1), by = sol->sol[1]) + ss_1 = sort(hc_steady_states(rs_1, ps; u0 = u0_1, show_progress = false, seed = 0x000004d1), by = sol -> sol[1]) @test ss_1 ≈ [[0.2, 0.1, 3.0, 1.0, 40.0, 10.0]] rs_2 = @reaction_network begin - @parameters kX1=1.0 kX2=2.0 kY1=12345.0 - @species C2(t)=0.1 C1(t)=0.2 B2(t)=12345.0 - (kX1,kX2), C2 <--> C1 - (kY1,kY2), B2 <--> B1 - (kZ1,kZ2), A2 <--> A1 + @parameters kX1 = 1.0 kX2 = 2.0 kY1 = 12345.0 + @species C2(t) = 0.1 C1(t) = 0.2 B2(t) = 12345.0 + (kX1, kX2), C2 <--> C1 + (kY1, kY2), B2 <--> B1 + (kZ1, kZ2), A2 <--> A1 end - u0_2 = [:B2 => 1.0, :B1 => 3.0, :A2 => 10.0, :A1 =>40.0] + u0_2 = [:B2 => 1.0, :B1 => 3.0, :A2 => 10.0, :A1 => 40.0] - ss_2 = sort(hc_steady_states(rs_2, ps; u0 = u0_2, show_progress = false, seed = 0x000004d1), by = sol->sol[1]) + ss_2 = sort(hc_steady_states(rs_2, ps; u0 = u0_2, show_progress = false, seed = 0x000004d1), by = sol -> sol[1]) @test ss_1 ≈ ss_2 end @@ -93,7 +93,7 @@ end # Tests than non-integer exponents throws an error. let rs = @reaction_network begin - v*(0.1/v + hill(X,1,K,n)), 0 --> X + v * (0.1 / v + hill(X, 1, K, n)), 0 --> X d, X --> 0 end ps = Dict([:v => 5.0, :K => 2.5, :n => 3, :d => 1.0]) @@ -101,7 +101,7 @@ let @test length(sss) == 4 for ss in sss - @test ps[:v]*(0.1/ps[:v] + ss[1]^ps[:n]/(ss[1]^ps[:n] + ps[:K]^ps[:n])) - ps[:d]*ss[1]≈ 0.0 atol = 1e-12 + @test ps[:v] * (0.1 / ps[:v] + ss[1]^ps[:n] / (ss[1]^ps[:n] + ps[:K]^ps[:n])) - ps[:d] * ss[1] ≈ 0.0 atol = 1.0e-12 end ps = [:v => 5.0, :K => 2.5, :n => 2.7, :d => 1.0] @@ -114,7 +114,7 @@ end let # Finds the model steady states. rs = @reaction_network begin - 0.01 + hillar(X,Y,1.0,Kx,3), ∅ --> X + 0.01 + hillar(X, Y, 1.0, Kx, 3), ∅ --> X 0.01 + (Y^3) / (X^3 + Y^3 + Ky^3), ∅ --> Y 1.0, (X, Y) --> ∅ end @@ -125,7 +125,7 @@ let @test length(sss) == 5 @test allunique(sss) for ss in sss - @test f_eval(rs, Pair.(unknowns(rs), ss), ps, 0.0) ≈ [0.0, 0.0] atol = 1e-12 rtol = 1e-12 + @test f_eval(rs, Pair.(unknowns(rs), ss), ps, 0.0) ≈ [0.0, 0.0] atol = 1.0e-12 rtol = 1.0e-12 end end @@ -138,10 +138,10 @@ let @parameters k @variables C(t) @equations begin - D(V) ~ k*X - V - 0 ~ X/V - C + D(V) ~ k * X - V + 0 ~ X / V - C end - (p/V,d/V), 0 <--> X + (p / V, d / V), 0 <--> X end # Checks that homotopy continuation correctly find the system's single steady state. @@ -165,7 +165,7 @@ end # Tests that non-autonomous system throws an error let rs = @reaction_network begin - (k,t), 0 <--> X + (k, t), 0 <--> X end ps = [:k => 1.0] @test_throws Exception hc_steady_states(rs, ps; show_progress = false, seed = 0x000004d1) diff --git a/test/extensions/stability_computation.jl b/test/extensions/stability_computation.jl index 7d58cd5ff2..a9adcc762a 100644 --- a/test/extensions/stability_computation.jl +++ b/test/extensions/stability_computation.jl @@ -16,17 +16,19 @@ rng = StableRNG(12345) let # System which may have between 1 and 7 fixed points. rn = @reaction_network begin - v/20.0 + hillar(X,Y,v,K,n), 0 --> X - v/20.0 + hillar(Y,X,v,K,n), 0 --> Y - d, (X,Y) --> 0 + v / 20.0 + hillar(X, Y, v, K, n), 0 --> X + v / 20.0 + hillar(Y, X, v, K, n), 0 --> Y + d, (X, Y) --> 0 end ss_jac = steady_state_jac(rn) # Repeats several times, most steady state stability cases should be encountered several times. - for repeat = 1:20 + for repeat in 1:20 # Generates random parameter values (which can generate all steady states cases). - ps = (:v => 1.0 + 3*rand(rng), :K => 0.5 + 2*rand(rng), :n => rand(rng,[1,2,3,4]), - :d => 0.5 + rand(rng)) + ps = ( + :v => 1.0 + 3 * rand(rng), :K => 0.5 + 2 * rand(rng), :n => rand(rng, [1, 2, 3, 4]), + :d => 0.5 + rand(rng), + ) # Computes stability using various jacobian options. sss = hc_steady_states(rn, ps; show_progress = false) @@ -36,8 +38,8 @@ let # Confirms stability using simulations. for (idx, ss) in enumerate(sss) ssprob = SteadyStateProblem(rn, Pair.(unknowns(rn), [1.001, 0.999] .* ss), ps) - sol = solve(ssprob, DynamicSS(Vern7()); abstol = 1e-8, reltol = 1e-8) - stabs_3 = isapprox(ss, sol.u; atol = 1e-6) + sol = solve(ssprob, DynamicSS(Vern7()); abstol = 1.0e-8, reltol = 1.0e-8) + stabs_3 = isapprox(ss, sol.u; atol = 1.0e-6) @test stabs_1[idx] == stabs_2[idx] == stabs_3 # Checks stability when steady state is given on a pair form ([X => x_val, Y => y_val]). @@ -53,11 +55,11 @@ end let # Creates model. rn = @reaction_network begin - k1+Z, Y --> 2X + k1 + Z, Y --> 2X k2, 2X --> X + Y k3, X + Y --> Y k4, X --> 0 - (kD1+X, kD2), 2Z <--> Z2 + (kD1 + X, kD2), 2Z <--> Z2 end # Creates various forms of input. @@ -73,9 +75,9 @@ let # Computes stability using various input forms, and checks that the output is correct. sss = hc_steady_states(rn, ps_1; u0 = u0_1, show_progress = false) for u0 in [u0_1, u0_2, u0_3, u0_4], ps in [ps_1, ps_2, ps_3] - stab_1 = [steady_state_stability(ss, rn, ps) for ss in sss] + stab_1 = [steady_state_stability(ss, rn, ps) for ss in sss] ss_jac = steady_state_jac(rn; u0 = u0) - stab_2 = [steady_state_stability(ss, rn, ps; ss_jac = ss_jac) for ss in sss] + stab_2 = [steady_state_stability(ss, rn, ps; ss_jac = ss_jac) for ss in sss] @test length(stab_1) == length(stab_2) == 3 @test count(stab_1) == count(stab_2) == 2 end @@ -90,17 +92,17 @@ end # and error with `tol = 100`. let rn = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X end p = [:p => 1.0, :d => 1.0] u = [1.0] - @test_throws Exception steady_state_stability(u, rn, p; tol = 1e2) + @test_throws Exception steady_state_stability(u, rn, p; tol = 1.0e2) end # Test that stability computation for non-autonomous (t-dependent) systems throws error. let rn = @reaction_network begin - (p + 1/(1+t),d), 0 <--> X + (p + 1 / (1 + t), d), 0 <--> X end p = [:p => 1.0, :d => 1.0] u = [1.0] diff --git a/test/extensions/structural_identifiability.jl b/test/extensions/structural_identifiability.jl index c4becbc5e4..cbd69123f4 100644 --- a/test/extensions/structural_identifiability.jl +++ b/test/extensions/structural_identifiability.jl @@ -10,12 +10,12 @@ loglevel = Logging.Error # 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) - dict_out = Dict{Symbol,Any}() + dict_out = Dict{Symbol, Any}() for key in keys(dict_in) sym_key = Symbol(key) sym_key = Symbol(replace(String(sym_key), "(t)" => "")) dict_out[sym_key] = dict_in[key] - end + end return dict_out end @@ -24,12 +24,12 @@ end # 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) -let +let # Identifiability analysis for Catalyst model. goodwind_oscillator_catalyst = @reaction_network begin - (mmr(P,pₘ,1), dₘ), 0 <--> M - (pₑ*M,dₑ), 0 <--> E - (pₚ*E,dₚ), 0 <--> P + (mmr(P, pₘ, 1), dₘ), 0 <--> M + (pₑ * M, dₑ), 0 <--> E + (pₚ * E, dₚ), 0 <--> P end gi_1 = assess_identifiability(goodwind_oscillator_catalyst; measured_quantities = [:M], loglevel) li_1 = assess_local_identifiability(goodwind_oscillator_catalyst; measured_quantities = [:M], loglevel) @@ -43,9 +43,9 @@ let # Identifiability analysis for StructuralIdentifiability.jl model (declare this overwrites e.g. X2 variable etc.). goodwind_oscillator_si = @ODEmodel( - M'(t) = pₘ / (1 + P(t)) - dₘ*M(t), - E'(t) = -dₑ*E(t) + pₑ*M(t), - P'(t) = -dₚ*P(t) + pₚ*E(t), + M'(t) = pₘ / (1 + P(t)) - dₘ * M(t), + E'(t) = -dₑ * E(t) + pₑ * M(t), + P'(t) = -dₚ * P(t) + pₚ * E(t), y1(t) = M(t) ) gi_3 = assess_identifiability(goodwind_oscillator_si; loglevel) @@ -55,32 +55,32 @@ let # Check outputs. @test sym_dict(gi_1) == sym_dict(gi_2) == sym_dict(gi_3) @test sym_dict(li_1) == sym_dict(li_2) == sym_dict(li_3) - @test length(ifs_1) == length(ifs_2) == length(ifs_3) + @test length(ifs_1) == length(ifs_2) == length(ifs_3) # Checks output to manually checked correct answers. @test isequal(collect(keys(gi_1)), [unknowns(goodwind_oscillator_catalyst); parameters(goodwind_oscillator_catalyst)]) @test isequal(collect(values(gi_1)), [:globally, :nonidentifiable, :globally, :globally, :globally, :nonidentifiable, :locally, :nonidentifiable, :locally]) @test isequal(collect(keys(li_1)), [unknowns(goodwind_oscillator_catalyst); parameters(goodwind_oscillator_catalyst)]) - @test isequal(collect(values(li_1)), [1, 0, 1, 1, 1, 0, 1, 0, 1]) + @test isequal(collect(values(li_1)), [1, 0, 1, 1, 1, 0, 1, 0, 1]) end # Tests on a made-up reaction network with mix of identifiable and non-identifiable components. # Tests for symbolics input. # Tests using known_p argument. -let +let # Identifiability analysis for Catalyst model. rs_catalyst = @reaction_network begin (p1, d), 0 <--> X1 k1, X1 --> X2 - (k2f,k2b), X2 <--> X3 + (k2f, k2b), X2 <--> X3 k3, X3 --> X4 d, X4 --> 0 end - @unpack X2, X3 = rs_catalyst + @unpack X2, X3 = rs_catalyst gi_1 = assess_identifiability(rs_catalyst; measured_quantities = [X2, X3], known_p = [:k2f], loglevel) li_1 = assess_local_identifiability(rs_catalyst; measured_quantities = [X2, X3], known_p = [:k2f], loglevel) ifs_1 = find_identifiable_functions(rs_catalyst; measured_quantities = [X2, X3], known_p = [:k2f], loglevel) - + # Identifiability analysis for Catalyst converted to StructuralIdentifiability.jl model. rs_ode = make_si_ode(rs_catalyst; measured_quantities = [X2, X3], known_p = [:k2f]) gi_2 = assess_identifiability(rs_ode; loglevel) @@ -89,10 +89,10 @@ let # Identifiability analysis for StructuralIdentifiability.jl model (declare this overwrites e.g. X2 variable etc.). rs_si = @ODEmodel( - X1'(t) = p1 - d*X1(t) - k1*X1(t), - X2'(t) = k1*X1(t) + k2b*X3(t) - k2f*X2(t), - X3'(t) = -k2b*X3(t) + k2f*X2(t) - k3*X3(t), - X4'(t) = d*X4(t) + k3*X3(t), + X1'(t) = p1 - d * X1(t) - k1 * X1(t), + X2'(t) = k1 * X1(t) + k2b * X3(t) - k2f * X2(t), + X3'(t) = -k2b * X3(t) + k2f * X2(t) - k3 * X3(t), + X4'(t) = d * X4(t) + k3 * X3(t), y1(t) = X2, y2(t) = X3, y3(t) = k2f @@ -104,20 +104,20 @@ let # Check outputs. @test sym_dict(gi_1) == sym_dict(gi_2) == sym_dict(gi_3) @test sym_dict(li_1) == sym_dict(li_2) == sym_dict(li_3) - @test length(ifs_1) == length(ifs_2) == length(ifs_3) + @test length(ifs_1) == length(ifs_2) == length(ifs_3) - # Checks output to manually checked correct answers. - @test isequal(collect(keys(gi_1)),[unknowns(rs_catalyst); parameters(rs_catalyst)]) - @test isequal(collect(values(gi_1)),[:nonidentifiable, :globally, :globally, :nonidentifiable, :nonidentifiable, :nonidentifiable, :nonidentifiable, :globally, :globally, :globally]) - @test isequal(collect(keys(li_1)),[unknowns(rs_catalyst); parameters(rs_catalyst)]) - @test isequal(collect(values(li_1)),[0, 1, 1, 0, 0, 0, 0, 1, 1, 1]) + # Checks output to manually checked correct answers. + @test isequal(collect(keys(gi_1)), [unknowns(rs_catalyst); parameters(rs_catalyst)]) + @test isequal(collect(values(gi_1)), [:nonidentifiable, :globally, :globally, :nonidentifiable, :nonidentifiable, :nonidentifiable, :nonidentifiable, :globally, :globally, :globally]) + @test isequal(collect(keys(li_1)), [unknowns(rs_catalyst); parameters(rs_catalyst)]) + @test isequal(collect(values(li_1)), [0, 1, 1, 0, 0, 0, 0, 1, 1, 1]) end # Tests on a made-up reaction network with mix of identifiable and non-identifiable components. # Tests for system with conserved quantity. # Tests for symbolics known_p # Tests using an equation for measured quantity. -let +let # Identifiability analysis for Catalyst model. rs_catalyst = @reaction_network begin p, 0 --> X1 @@ -125,8 +125,8 @@ let k2, X2 --> X3 k3, X3 --> X4 k3, X3 --> X5 - d, (X4,X5) --> 0 - (kA*X3, kD), Yi <--> Ya + d, (X4, X5) --> 0 + (kA * X3, kD), Yi <--> Ya end @unpack X1, X2, X3, X4, k1, k2, Yi, Ya, k1, kD = rs_catalyst gi_1 = assess_identifiability(rs_catalyst; measured_quantities = [X1 + Yi, Ya], known_p = [k1, kD], loglevel) @@ -134,20 +134,20 @@ let ifs_1 = find_identifiable_functions(rs_catalyst; measured_quantities = [X1 + Yi, Ya], known_p = [k1, kD], loglevel) # Identifiability analysis for Catalyst converted to StructuralIdentifiability.jl model. - rs_ode = make_si_ode(rs_catalyst; measured_quantities=[X1 + Yi, Ya], known_p=[k1, kD], remove_conserved=false) + rs_ode = make_si_ode(rs_catalyst; measured_quantities = [X1 + Yi, Ya], known_p = [k1, kD], remove_conserved = false) gi_2 = assess_identifiability(rs_ode; loglevel) li_2 = assess_local_identifiability(rs_ode; loglevel) ifs_2 = find_identifiable_functions(rs_ode; loglevel) # Identifiability analysis for StructuralIdentifiability.jl model (declare this overwrites e.g. X2 variable etc.). rs_si = @ODEmodel( - X1'(t) = p - k1*X1(t), - X2'(t) = k1*X1(t) - k2*X2(t), - X3'(t) = k2*X2(t) - 2k3*X3(t), - X4'(t) = -d*X4(t) + k3*X3(t), - X5'(t) = -d*X5(t) + k3*X3(t), - Yi'(t) = kD*Ya(t) - kA*Yi(t)*X3(t), - Ya'(t) = -kD*Ya(t) + kA*Yi(t)*X3(t), + X1'(t) = p - k1 * X1(t), + X2'(t) = k1 * X1(t) - k2 * X2(t), + X3'(t) = k2 * X2(t) - 2k3 * X3(t), + X4'(t) = -d * X4(t) + k3 * X3(t), + X5'(t) = -d * X5(t) + k3 * X3(t), + Yi'(t) = kD * Ya(t) - kA * Yi(t) * X3(t), + Ya'(t) = -kD * Ya(t) + kA * Yi(t) * X3(t), y1(t) = X1 + Yi, y2(t) = Ya, y3(t) = k1, @@ -156,13 +156,13 @@ let gi_3 = assess_identifiability(rs_si; loglevel) li_3 = assess_local_identifiability(rs_si; loglevel) ifs_3 = find_identifiable_functions(rs_si; loglevel) - + # Check outputs. @test sym_dict(gi_1) == sym_dict(gi_2) == sym_dict(gi_3) @test sym_dict(li_1) == sym_dict(li_2) == sym_dict(li_3) @test length(ifs_1[2:end]) == length(ifs_2) == length(ifs_3) # In the first case, the conservation law parameter is also identifiable. - # Checks output to manually checked correct answers. + # Checks output to manually checked correct answers. correct_gi = Pair.([unknowns(rs_catalyst); parameters(rs_catalyst)], [:globally, :locally, :locally, :nonidentifiable, :nonidentifiable, :globally, :globally, :globally, :globally, :locally, :locally, :nonidentifiable, :locally, :globally]) correct_li = Pair.([unknowns(rs_catalyst); parameters(rs_catalyst)], [1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1]) @test issetequal(gi_1, correct_gi) @@ -170,11 +170,11 @@ let end # Tests that various inputs types work. -let +let goodwind_oscillator_catalyst = @reaction_network begin - (mmr(P,pₘ,1), dₘ), 0 <--> M - (pₑ*M,dₑ), 0 <--> E - (pₚ*E,dₚ), 0 <--> P + (mmr(P, pₘ, 1), dₘ), 0 <--> M + (pₑ * M, dₑ), 0 <--> E + (pₚ * E, dₚ), 0 <--> P end @unpack M, E, P, pₑ, pₚ, pₘ = goodwind_oscillator_catalyst si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities = [:M]) @@ -190,8 +190,8 @@ let si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities = [M], known_p = [pₑ, pₚ]) si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities = [M, E], known_p = [pₑ, pₚ]) si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities = [M + pₑ]) - si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities = [M + E, pₑ*M], known_p = [:pₑ]) - si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities = [pₑ, pₚ], known_p = [pₑ]) + si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities = [M + E, pₑ * M], known_p = [:pₑ]) + 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) @@ -201,7 +201,7 @@ let @test make_si_ode(gw_osc_complt; measured_quantities = [gw_osc_complt.M, gw_osc_complt.E], 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ₑ, gw_osc_complt.pₚ]) isa ODE @test make_si_ode(gw_osc_complt; measured_quantities = [gw_osc_complt.M], known_p = [:pₚ]) isa ODE - @test make_si_ode(gw_osc_complt; measured_quantities = [gw_osc_complt.M*gw_osc_complt.E]) isa ODE + @test make_si_ode(gw_osc_complt; measured_quantities = [gw_osc_complt.M * gw_osc_complt.E]) isa ODE end # Tests for hierarchical model with conservation laws at both top and internal levels. @@ -228,10 +228,10 @@ let # Identifiability analysis for StructuralIdentifiability.jl model (declare this overwrites e.g. X2 variable etc.). rs_si = @ODEmodel( - X1'(t) = -k1*X1(t) + k2*X2(t), - X2'(t) = k1*X1(t) - k2*X2(t), - rs2₊X3'(t) = -rs2₊k3*rs2₊X3(t) + rs2₊k4*rs2₊X4(t), - rs2₊X4'(t) = rs2₊k3*rs2₊X3(t) - rs2₊k4*rs2₊X4(t), + X1'(t) = -k1 * X1(t) + k2 * X2(t), + X2'(t) = k1 * X1(t) - k2 * X2(t), + rs2₊X3'(t) = -rs2₊k3 * rs2₊X3(t) + rs2₊k4 * rs2₊X4(t), + rs2₊X4'(t) = rs2₊k3 * rs2₊X3(t) - rs2₊k4 * rs2₊X4(t), y1(t) = X1, y2(t) = X2, y3(t) = rs2₊X3, @@ -240,7 +240,7 @@ let gi_3 = assess_identifiability(rs_si; loglevel) li_3 = assess_local_identifiability(rs_si; loglevel) ifs_3 = find_identifiable_functions(rs_si; loglevel) - + # Check outputs. @test sym_dict(gi_1) == sym_dict(gi_3) @test sym_dict(li_1) == sym_dict(li_3) @@ -249,10 +249,10 @@ let # 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. # Hence, here you display identifiability for `Γ[1]` instead of X2. - gi_1_no_cq = filter(x -> !occursin("X2",String(x[1])) && !occursin("X4",String(x[1])), sym_dict(gi_1)) - gi_2_no_cq = filter(x -> !occursin("Γ",String(x[1])), sym_dict(gi_2)) - li_1_no_cq = filter(x -> !occursin("X2",String(x[1])) && !occursin("X4",String(x[1])), sym_dict(li_1)) - li_2_no_cq = filter(x -> !occursin("Γ",String(x[1])), sym_dict(li_2)) + gi_1_no_cq = filter(x -> !occursin("X2", String(x[1])) && !occursin("X4", String(x[1])), sym_dict(gi_1)) + gi_2_no_cq = filter(x -> !occursin("Γ", String(x[1])), sym_dict(gi_2)) + li_1_no_cq = filter(x -> !occursin("X2", String(x[1])) && !occursin("X4", String(x[1])), sym_dict(li_1)) + li_2_no_cq = filter(x -> !occursin("Γ", String(x[1])), sym_dict(li_2)) @test gi_1_no_cq == gi_2_no_cq @test li_1_no_cq == li_2_no_cq end @@ -293,9 +293,9 @@ let :A0 => :globally, :A1 => :globally, :A2 => :globally, - :a => :nonidentifiable, - :b => :nonidentifiable, - :c => :nonidentifiable + :a => :nonidentifiable, + :b => :nonidentifiable, + :c => :nonidentifiable ) # Test with no parameters @@ -322,44 +322,52 @@ let @parameters k c1 c2 @variables C(t) @equations begin - D(V) ~ k*X - V - C ~ (c1 + c2) * X/V + D(V) ~ k * X - V + C ~ (c1 + c2) * X / V end - (p/V,d/V), 0 <--> X + (p / V, d / V), 0 <--> X end @unpack p, d, k, c1, c2 = rs - + # Tests identifiability assessment when all unknowns are measured. remove_conserved = false gi_1 = assess_identifiability(rs; measured_quantities = [:X, :V, :C], loglevel, remove_conserved) li_1 = assess_local_identifiability(rs; measured_quantities = [:X, :V, :C], loglevel, remove_conserved) ifs_1 = find_identifiable_functions(rs; measured_quantities = [:X, :V, :C], loglevel, remove_conserved) - @test sym_dict(gi_1) == Dict([:X => :globally, :C => :globally, :V => :globally, :k => :globally, - :c1 => :nonidentifiable, :c2 => :nonidentifiable, :p => :globally, :d => :globally]) + @test sym_dict(gi_1) == Dict( + [ + :X => :globally, :C => :globally, :V => :globally, :k => :globally, + :c1 => :nonidentifiable, :c2 => :nonidentifiable, :p => :globally, :d => :globally, + ] + ) @test sym_dict(li_1) == Dict([:X => 1, :C => 1, :V => 1, :k => 1, :c1 => 0, :c2 => 0, :p => 1, :d => 1]) @test issetequal(ifs_1, [d, p, k, c1 + c2]) - - # Tests identifiability assessment when only variables are measured. + + # Tests identifiability assessment when only variables are measured. # Checks that a parameter in an equation can be set as known. gi_2 = assess_identifiability(rs; measured_quantities = [:V, :C], known_p = [:c1], loglevel, remove_conserved) li_2 = assess_local_identifiability(rs; measured_quantities = [:V, :C], known_p = [:c1], loglevel, remove_conserved) ifs_2 = find_identifiable_functions(rs; measured_quantities = [:V, :C], known_p = [:c1], loglevel, remove_conserved) - @test sym_dict(gi_2) == Dict([:X => :nonidentifiable, :C => :globally, :V => :globally, :k => :nonidentifiable, - :c1 => :globally, :c2 => :nonidentifiable, :p => :nonidentifiable, :d => :globally]) + @test sym_dict(gi_2) == Dict( + [ + :X => :nonidentifiable, :C => :globally, :V => :globally, :k => :nonidentifiable, + :c1 => :globally, :c2 => :nonidentifiable, :p => :nonidentifiable, :d => :globally, + ] + ) @test sym_dict(li_2) == Dict([:X => 0, :C => 1, :V => 1, :k => 0, :c1 => 1, :c2 => 0, :p => 0, :d => 1]) - @test issetequal(ifs_2, [d, c1, k*p, c1*p + c2*p]) + @test issetequal(ifs_2, [d, c1, k * p, c1 * p + c2 * p]) end # Checks that identifiability functions cannot be applied to non-complete `ReactionSystems`s. -let +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, loglevel) @test_throws Exception assess_local_identifiability(incomplete_network; measured_quantities, loglevel) @test_throws Exception find_identifiable_functions(incomplete_network; measured_quantities, loglevel) -end \ No newline at end of file +end diff --git a/test/miscellaneous_tests/compound_macro.jl b/test/miscellaneous_tests/compound_macro.jl index 9a2f5f14bd..fdd9df69e3 100644 --- a/test/miscellaneous_tests/compound_macro.jl +++ b/test/miscellaneous_tests/compound_macro.jl @@ -15,10 +15,10 @@ let # Basic cases that should pass: @compound H2O_1 ~ 2H + O - @compound (H2O_2, [output=true]) ~ 2H + O + @compound (H2O_2, [output = true]) ~ 2H + O @compound (H2O_3 = 1.5) ~ 2H + O - @compound (H2O_4 = 4, [output=true]) ~ 2H + O - @compound (H2O_5 = p1, [output=true]) ~ 2H + p2*O + @compound (H2O_4 = 4, [output = true]) ~ 2H + O + @compound (H2O_5 = p1, [output = true]) ~ 2H + p2 * O @test iscompound(H2O_1) @test iscompound(H2O_2) @test iscompound(H2O_3) @@ -27,20 +27,20 @@ let # Other errors. @test_throws Exception @eval @compound H2O = 2H + O - @test_throws Exception @eval @compound (H2O, [output=true]) = 2H + O + @test_throws Exception @eval @compound (H2O, [output = true]) = 2H + O @test_throws Exception @eval @compound (H2O = 1.5) ~ 2H + O - @test_throws Exception @eval @compound (H2O = 4, [output=true]) ~ 2H + O - @test_throws Exception @eval @compound (H2O = p1, [output=true]) ~ 2H + p2*O + @test_throws Exception @eval @compound (H2O = 4, [output = true]) ~ 2H + O + @test_throws Exception @eval @compound (H2O = p1, [output = true]) ~ 2H + p2 * O # Compounds created in block notation. @compounds begin CO2_1 ~ 2H + O end @compounds begin - (CO2_2, [output=true]) ~ 2H + O + (CO2_2, [output = true]) ~ 2H + O (CO2_3 = 1.5) ~ 2H + O - (CO2_4 = 4, [output=true]) ~ 2H + O - (CO2_5 = p1, [output=true]) ~ 2H + p2*O + (CO2_4 = 4, [output = true]) ~ 2H + O + (CO2_5 = p1, [output = true]) ~ 2H + p2 * O end @test iscompound(CO2_1) @test iscompound(CO2_2) @@ -54,10 +54,10 @@ let @parameters p1 p2 @compounds begin NH3_1 ~ N + 3H - (NH3_2, [output=true]) ~ N + 3H + (NH3_2, [output = true]) ~ N + 3H (NH3_3 = 1.5) ~ N + 3H - (NH3_4 = 4, [output=true]) ~ N + 3H - (NH3_5 = p1, [output=true]) ~ N + p2*H + (NH3_4 = 4, [output = true]) ~ N + 3H + (NH3_5 = p1, [output = true]) ~ N + p2 * H end end @test iscompound(rn.NH3_1) @@ -123,21 +123,21 @@ end # Test using different independent variable combinations. let @parameters x y z - @species C(t) H(x) N(x) O(t) P(t,x) S(x,y) + @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 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 + @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 + @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(Symbolics.sorted_arguments(ModelingToolkitBase.unwrap(CO2)), [t]) @@ -185,22 +185,22 @@ let alpha = 2 h = H - @compound H2_1 ~ 2*H - @compound H2_2 ~ alpha*H - @compound H2_3 ~ 2*h - @compound H2_4 ~ alpha*H + @compound H2_1 ~ 2 * H + @compound H2_2 ~ alpha * H + @compound H2_3 ~ 2 * h + @compound H2_4 ~ alpha * H @test iscompound(H2_1) @test iscompound(H2_2) @test iscompound(H2_2) @test iscompound(H2_4) - @test isequal(components(H2_1),components(H2_2)) - @test isequal(components(H2_2),components(H2_3)) - @test isequal(components(H2_3),components(H2_4)) - @test isequal(coefficients(H2_1),coefficients(H2_2)) - @test isequal(coefficients(H2_2),coefficients(H2_3)) - @test isequal(coefficients(H2_3),coefficients(H2_4)) + @test isequal(components(H2_1), components(H2_2)) + @test isequal(components(H2_2), components(H2_3)) + @test isequal(components(H2_3), components(H2_4)) + @test isequal(coefficients(H2_1), coefficients(H2_2)) + @test isequal(coefficients(H2_2), coefficients(H2_3)) + @test isequal(coefficients(H2_3), coefficients(H2_4)) end # Case 4. @@ -208,13 +208,13 @@ let @parameters alpha = 2 @species H(t) - @compound H2_1 ~ alpha*H + @compound H2_1 ~ alpha * H @compound H2_2 ~ 2H @test iscompound(H2_1) @test iscompound(H2_2) - @test isequal(components(H2_1),components(H2_2)) + @test isequal(components(H2_1), components(H2_2)) @test isequal(coefficients(H2_1), @parameters alpha = 2) end @@ -228,7 +228,7 @@ let @test iscompound(A2) @test iscompound(B2) - @test isequal(components(A2),components(B2)) + @test isequal(components(A2), components(B2)) @test isequal(coefficients(A2), coefficients(B2)) @test isequal(component_coefficients(A2), component_coefficients(B2)) end @@ -249,10 +249,10 @@ let @test iscompound(OH_alt) @test iscompound(C3H5OH3_alt) - @test isequal(components(OH),components(OH_alt)) + @test isequal(components(OH), components(OH_alt)) @test isequal(coefficients(OH), coefficients(OH_alt)) @test isequal(component_coefficients(OH), component_coefficients(OH_alt)) - @test isequal(components(C3H5OH3),components(C3H5OH3_alt)) + @test isequal(components(C3H5OH3), components(C3H5OH3_alt)) @test isequal(coefficients(C3H5OH3), coefficients(C3H5OH3_alt)) @test isequal(component_coefficients(C3H5OH3), component_coefficients(C3H5OH3_alt)) end @@ -271,7 +271,7 @@ let @test iscompound(comp) @test iscompound(comp_alt) - @test isequal(components(comp),components(comp_alt)) + @test isequal(components(comp), components(comp_alt)) @test isequal(coefficients(comp), coefficients(comp_alt)) @test isequal(component_coefficients(comp), component_coefficients(comp_alt)) end diff --git a/test/miscellaneous_tests/explicit_imports.jl b/test/miscellaneous_tests/explicit_imports.jl index b630a9dbba..abeb1f367f 100644 --- a/test/miscellaneous_tests/explicit_imports.jl +++ b/test/miscellaneous_tests/explicit_imports.jl @@ -5,12 +5,16 @@ using Catalyst, ExplicitImports, Test @testset "Explicit Imports" begin # Test that there are no implicit imports # allow_unanalyzable is needed because PhysicalScale enum causes parsing issues - @test check_no_implicit_imports(Catalyst; - allow_unanalyzable=(Catalyst, Catalyst.PhysicalScale)) === nothing + @test check_no_implicit_imports( + Catalyst; + allow_unanalyzable = (Catalyst, Catalyst.PhysicalScale) + ) === nothing # Test that there are no stale explicit imports - @test check_no_stale_explicit_imports(Catalyst; - allow_unanalyzable=(Catalyst, Catalyst.PhysicalScale)) === nothing + @test check_no_stale_explicit_imports( + Catalyst; + allow_unanalyzable = (Catalyst, Catalyst.PhysicalScale) + ) === nothing # Test that all explicit imports are from owner modules @test check_all_explicit_imports_via_owners(Catalyst) === nothing diff --git a/test/miscellaneous_tests/reaction_balancing.jl b/test/miscellaneous_tests/reaction_balancing.jl index a5908a6614..446842da15 100644 --- a/test/miscellaneous_tests/reaction_balancing.jl +++ b/test/miscellaneous_tests/reaction_balancing.jl @@ -18,12 +18,12 @@ let H2O ~ 2H + 1O end - rx = Reaction(k,[H2,O2],[H2O]) + rx = Reaction(k, [H2, O2], [H2O]) - @test isequal(Catalyst.create_matrix(rx),[2 0 -2; 0 2 -1;]) - @test isequal(Catalyst.get_balanced_stoich(rx),[[2, 1, 2]]) + @test isequal(Catalyst.create_matrix(rx), [2 0 -2; 0 2 -1;]) + @test isequal(Catalyst.get_balanced_stoich(rx), [[2, 1, 2]]) - balanced_rx = Reaction(k,[H2,O2],[H2O],[2,1],[2]) + balanced_rx = Reaction(k, [H2, O2], [H2O], [2, 1], [2]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -42,12 +42,12 @@ let @compound H2O ~ 2H + 1O @compound C6H12O6 ~ 6C + 12H + 6O - rx = Reaction(k,[CO2,H2O],[C6H12O6,O2]) + rx = Reaction(k, [CO2, H2O], [C6H12O6, O2]) - @test isequal(Catalyst.create_matrix(rx),[ 1 0 -6 0; 2 1 -6 -2; 0 2 -12 0;]) - @test isequal(Catalyst.get_balanced_stoich(rx),[[6, 6, 1, 6]]) + @test isequal(Catalyst.create_matrix(rx), [ 1 0 -6 0; 2 1 -6 -2; 0 2 -12 0;]) + @test isequal(Catalyst.get_balanced_stoich(rx), [[6, 6, 1, 6]]) - balanced_rx = Reaction(k,[CO2,H2O],[C6H12O6,O2],[6, 6], [1, 6]) + balanced_rx = Reaction(k, [CO2, H2O], [C6H12O6, O2], [6, 6], [1, 6]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -80,7 +80,7 @@ let rx = Reaction(1.0, [H, O], [H2O], [23, 1], [7]) - balanced_rx = Reaction(1.0, [H,O], [H2O], [2, 1], [1]) + balanced_rx = Reaction(1.0, [H, O], [H2O], [2, 1], [1]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -113,7 +113,7 @@ let rx = Reaction(1.0, [N2, H2], [NH3]) - balanced_rx = Reaction(1.0, [N2, H2], [NH3], [1 ,3], [2]) + balanced_rx = Reaction(1.0, [N2, H2], [NH3], [1, 3], [2]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -174,9 +174,9 @@ let @compound Na2SO4 ~ 2Na + 1S + 4O @compound H2O ~ 2H + O - rx = Reaction(1.0, [NaOH,H2SO4], [Na2SO4,H2O]) + rx = Reaction(1.0, [NaOH, H2SO4], [Na2SO4, H2O]) - balanced_rx = Reaction(1.0, [NaOH,H2SO4], [Na2SO4,H2O], [2, 1], [1, 2]) + balanced_rx = Reaction(1.0, [NaOH, H2SO4], [Na2SO4, H2O], [2, 1], [1, 2]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -205,8 +205,8 @@ let @compound HCl ~ 1H + 1Cl @compound CaCl2 ~ 1Ca + 2Cl - rx = Reaction(1.0,[CaCO3,HCl],[CaCl2,CO2,H2O]) - balanced_rx = Reaction(1.0,[CaCO3,HCl],[CaCl2,CO2,H2O], [1, 2], [1, 1, 1]) + rx = Reaction(1.0, [CaCO3, HCl], [CaCl2, CO2, H2O]) + balanced_rx = Reaction(1.0, [CaCO3, HCl], [CaCl2, CO2, H2O], [1, 2], [1, 1, 1]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -220,8 +220,8 @@ let @compound H4SiO4 ~ 4H + Si + 4O @compound HCl ~ H + Cl - rx = Reaction(1.0,[SiCl4,H2O],[H4SiO4,HCl]) - balanced_rx = Reaction(1.0,[SiCl4,H2O],[H4SiO4,HCl], [1,4], [1,4]) + rx = Reaction(1.0, [SiCl4, H2O], [H4SiO4, HCl]) + balanced_rx = Reaction(1.0, [SiCl4, H2O], [H4SiO4, HCl], [1, 4], [1, 4]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -234,8 +234,8 @@ let @compound AlCl3 ~ Al + 3Cl @compound H2 ~ 2H - rx = Reaction(1.0,[Al,HCl],[AlCl3,H2]) - balanced_rx = Reaction(1.0,[Al,HCl],[AlCl3,H2],[2,6], [2,3]) + rx = Reaction(1.0, [Al, HCl], [AlCl3, H2]) + balanced_rx = Reaction(1.0, [Al, HCl], [AlCl3, H2], [2, 6], [2, 3]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -250,8 +250,8 @@ let @compound H2O ~ 2H + O @compound CO2 ~ C + 2O - rx = Reaction(1.0,[Na2CO3,HCl],[NaCl,H2O,CO2]) - balanced_rx = Reaction(1.0,[Na2CO3,HCl],[NaCl,H2O,CO2], [1,2], [2,1,1]) + rx = Reaction(1.0, [Na2CO3, HCl], [NaCl, H2O, CO2]) + balanced_rx = Reaction(1.0, [Na2CO3, HCl], [NaCl, H2O, CO2], [1, 2], [2, 1, 1]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -265,8 +265,8 @@ let @compound CO2 ~ C + 2O @compound H2O ~ 2H + O - rx = Reaction(1.0,[C7H6O2,O2],[CO2,H2O]) - balanced_rx = Reaction(1.0,[C7H6O2,O2],[CO2,H2O], [2,15], [14,6]) + rx = Reaction(1.0, [C7H6O2, O2], [CO2, H2O]) + balanced_rx = Reaction(1.0, [C7H6O2, O2], [CO2, H2O], [2, 15], [14, 6]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -280,8 +280,8 @@ let @compound K2SO4 ~ 2K + S + 4O @compound FeO3H3 ~ Fe + 3O + 3H - rx = Reaction(1.0,[Fe2S3O12,KOH],[K2SO4,FeO3H3]) #5x4 matrix - balanced_rx = Reaction(1.0,[Fe2S3O12,KOH],[K2SO4,FeO3H3], [1,6], [3,2]) + rx = Reaction(1.0, [Fe2S3O12, KOH], [K2SO4, FeO3H3]) #5x4 matrix + balanced_rx = Reaction(1.0, [Fe2S3O12, KOH], [K2SO4, FeO3H3], [1, 6], [3, 2]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -295,8 +295,8 @@ let @compound P4O10 ~ 4P + 10O @compound CaSiO3 ~ Ca + Si + 3O - rx = Reaction(1.0,[Ca3P2O8,SiO2],[P4O10,CaSiO3]) #5x4 matrix - balanced_rx = Reaction(1.0,[Ca3P2O8,SiO2],[P4O10,CaSiO3], [2,6] , [1,6]) + rx = Reaction(1.0, [Ca3P2O8, SiO2], [P4O10, CaSiO3]) #5x4 matrix + balanced_rx = Reaction(1.0, [Ca3P2O8, SiO2], [P4O10, CaSiO3], [2, 6], [1, 6]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -309,8 +309,8 @@ let @compound KClO4 ~ K + Cl + 4O @compound KCl ~ K + Cl - rx = Reaction(1.0,[KClO3],[KClO4,KCl]) - balanced_rx = Reaction(1.0,[KClO3],[KClO4,KCl], [4], [3,1]) + rx = Reaction(1.0, [KClO3], [KClO4, KCl]) + balanced_rx = Reaction(1.0, [KClO3], [KClO4, KCl], [4], [3, 1]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -324,8 +324,8 @@ let @compound AlO3H3 ~ Al + 3O + 3H @compound CaSO4 ~ Ca + S + 4O - rx = Reaction(1.0,[Al2S3O12,CaO2H2],[AlO3H3,CaSO4]) - balanced_rx = Reaction(1.0,[Al2S3O12,CaO2H2],[AlO3H3,CaSO4], [1,3], [2,3]) + rx = Reaction(1.0, [Al2S3O12, CaO2H2], [AlO3H3, CaSO4]) + balanced_rx = Reaction(1.0, [Al2S3O12, CaO2H2], [AlO3H3, CaSO4], [1, 3], [2, 3]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -340,8 +340,8 @@ let @compound I2 ~ 2I @compound H2O ~ 2H + O - rx = Reaction(1.0,[H2SO4,HI],[H2S,I2,H2O]) - balanced_rx = Reaction(1.0,[H2SO4,HI],[H2S,I2,H2O], [1,8], [1,4,4]) + rx = Reaction(1.0, [H2SO4, HI], [H2S, I2, H2O]) + balanced_rx = Reaction(1.0, [H2SO4, HI], [H2S, I2, H2O], [1, 8], [1, 4, 4]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -355,8 +355,8 @@ let @compound CO2 ~ C + 2O @compound H2O ~ 2H + O - rx = Reaction(1.0,[C2H4,O2],[CO2,H2O]) - balanced_rx = Reaction(1.0,[C2H4,O2],[CO2,H2O],[1,3],[2,2]) + rx = Reaction(1.0, [C2H4, O2], [CO2, H2O]) + balanced_rx = Reaction(1.0, [C2H4, O2], [CO2, H2O], [1, 3], [2, 2]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -373,9 +373,9 @@ let @compound CH4 ~ C + 4H @compound H2O ~ 2H + O - rx = Reaction(1.0,[CO,CO2,H2],[CH4,H2O]) + rx = Reaction(1.0, [CO, CO2, H2], [CH4, H2O]) brxs = balance_reaction(rx) - @test_logs (:warn, r"The space of possible balanced *") match_mode=:any balance_reaction(rx) + @test_logs (:warn, r"The space of possible balanced *") match_mode = :any balance_reaction(rx) @test length(brxs) == 2 end @@ -389,9 +389,9 @@ let @compound NO ~ N + O @compound H2SO4 ~ 2H + S + 4O - rx = Reaction(1.0,[FeS2,HNO3],[Fe2S3O12,NO,H2SO4]) + rx = Reaction(1.0, [FeS2, HNO3], [Fe2S3O12, NO, H2SO4]) brxs = balance_reaction(rx) - @test_logs (:warn, "Unable to balance reaction.") match_mode=:any balance_reaction(rx) + @test_logs (:warn, "Unable to balance reaction.") match_mode = :any balance_reaction(rx) @test isempty(brxs) end @@ -418,14 +418,14 @@ let end k, CO2 + H2O --> C6H12O6 + O2 end - + brxs = balance_reaction(reactions(rn)[1])[1] - + @test isequal(rn.k, brxs.rate) - @test isequal([rn.CO2, rn.H2O], brxs.substrates) - @test isequal([rn.C6H12O6, rn.O2], brxs.products) + @test isequal([rn.CO2, rn.H2O], brxs.substrates) + @test isequal([rn.C6H12O6, rn.O2], brxs.products) @test isequal([6, 6], brxs.substoich) - @test isequal([1, 6], brxs.prodstoich) + @test isequal([1, 6], brxs.prodstoich) end @@ -496,7 +496,7 @@ let k, CO + CO2 + H2 --> CH4 + H20 end @test_throws Exception balance_system(rs1) - + # Check system with reaction without any balanced versions. rs2 = @reaction_network begin @species Fe(t) S(t) O(t) H(t) N(t) @@ -510,4 +510,4 @@ let k, FeS2 + HNO3 --> Fe2S3O12 + NO + H2SO4 end @test_throws Exception balance_system(rs2) -end \ No newline at end of file +end diff --git a/test/miscellaneous_tests/reactionsystem_serialisation.jl b/test/miscellaneous_tests/reactionsystem_serialisation.jl index 329867b678..5cbd687b05 100644 --- a/test/miscellaneous_tests/reactionsystem_serialisation.jl +++ b/test/miscellaneous_tests/reactionsystem_serialisation.jl @@ -100,7 +100,7 @@ let bool_md = false int_md = 3 float_md = 1.2 - rat_md = 4//5 + rat_md = 4 // 5 sym_md = :sym c_md = 'c' str_md = "A string" @@ -116,30 +116,30 @@ let # Creates parameters, variables, and species (with various metadata and default values). @parameters begin - a, [input=bool_md] - b, [misc=int_md] - c = float_md, [misc=rat_md] - d1, [misc=c_md] - d2, [description=str_md] - e1, [misc=nothing_md] - e2, [misc=symb_md] + a, [input = bool_md] + b, [misc = int_md] + c = float_md, [misc = rat_md] + d1, [misc = c_md] + d2, [description = str_md] + e1, [misc = nothing_md] + e2, [misc = symb_md] end @variables begin A(t) = float_md - B(t), [misc=expr_md] - C(t), [misc=pair_md] - D1(t), [misc=tup_md] - D2(t), [misc=vec_md] - E1(t), [misc=dict_md] - E2(t), [misc=mat_md] + B(t), [misc = expr_md] + C(t), [misc = pair_md] + D1(t), [misc = tup_md] + D2(t), [misc = vec_md] + E1(t), [misc = dict_md] + E2(t), [misc = mat_md] end @species begin - X(t), [input=bool_md] - Y(t), [misc=int_md] - Z(t), [misc=float_md] - V1(t), [description=str_md] - V2(t), [misc=dict_md] - W1(t), [misc=mat_md] + X(t), [input = bool_md] + Y(t), [misc = int_md] + Z(t), [misc = float_md] + V1(t), [description = str_md] + V2(t), [misc = dict_md] + W1(t), [misc = mat_md] W2(t) = float_md end @@ -265,18 +265,18 @@ end # Tests for complete and incomplete system. let # Prepares spatial independent variables (technically not used and only supplied to systems). - sivs = @variables x y z [description="A spatial independent variable."] + sivs = @variables x y z [description = "A spatial independent variable."] # Prepares parameters, species, and variables. @parameters p d k1_1 k2_1 k1_2 k2_2 k1_3 k2_3 k1_4 k2_4 a b_1 b_2 b_3 b_4 η @parameters begin t_1 = 2.0 t_2::Float64 - t_3, [description="A parameter."] - t_4::Float32 = p, [description="A parameter."] + t_3, [description = "A parameter."] + t_4::Float32 = p, [description = "A parameter."] end - @species X(t) X2_1(t) X2_2(t) X2_3(t) X2_4(t)=p [description="A species."] - @variables A(t)=p [description="A variable."] B_1(t) B_2(t) B_3(t) B_4(t) + @species X(t) X2_1(t) X2_2(t) X2_3(t) X2_4(t) = p [description = "A species."] + @variables A(t) = p [description = "A variable."] B_1(t) B_2(t) B_3(t) B_4(t) # Prepares all equations. eqs_1 = [ @@ -285,7 +285,7 @@ let Reaction(k1_1, [X], [X2_1], [2], [1]), Reaction(k2_1, [X2_1], [X], [1], [2]), D(A) ~ a - A, - A + 2B_1^3 ~ b_1 * X + A + 2B_1^3 ~ b_1 * X, ] eqs_2 = [ Reaction(p, [], [X]; metadata = [:description => "A reaction"]), @@ -293,7 +293,7 @@ let Reaction(k1_2, [X], [X2_2], [2], [1]), Reaction(k2_2, [X2_2], [X], [1], [2]), D(A) ~ a - A, - A + 2B_2^3 ~ b_2 * X + A + 2B_2^3 ~ b_2 * X, ] eqs_3 = [ Reaction(p, [], [X]; metadata = [:description => "A reaction"]), @@ -301,7 +301,7 @@ let Reaction(k1_3, [X], [X2_3], [2], [1]), Reaction(k2_3, [X2_3], [X], [1], [2]), D(A) ~ a - A, - A + 2B_3^3 ~ b_3 * X + A + 2B_3^3 ~ b_3 * X, ] eqs_4 = [ Reaction(p, [], [X]; metadata = [:description => "A reaction"]), @@ -309,14 +309,14 @@ let Reaction(k1_4, [X], [X2_4], [2], [1]), Reaction(k2_4, [X2_4], [X], [1], [2]), D(A) ~ a - A, - A + 2B_4^3 ~ b_4 * X + A + 2B_4^3 ~ b_4 * X, ] # Prepares all events. - continuous_events_1 = [(A ~ t_1) => [A ~ A + 2.0, X ~ X/2]] - continuous_events_2 = [(A ~ t_2) => [A ~ A + 2.0, X ~ X/2]] - continuous_events_3 = [(A ~ t_3) => [A ~ A + 2.0, X ~ X/2]] - continuous_events_4 = [(A ~ t_4) => [A ~ A + 2.0, X ~ X/2]] + continuous_events_1 = [(A ~ t_1) => [A ~ A + 2.0, X ~ X / 2]] + continuous_events_2 = [(A ~ t_2) => [A ~ A + 2.0, X ~ X / 2]] + continuous_events_3 = [(A ~ t_3) => [A ~ A + 2.0, X ~ X / 2]] + continuous_events_4 = [(A ~ t_4) => [A ~ A + 2.0, X ~ X / 2]] discrete_events_1 = [ 10.0 => [X2_1 ~ X2_1 + 1.0] [5.0, 10.0] => [b_1 ~ 2 * b_1] @@ -339,18 +339,26 @@ let ] # Creates the systems. - @named rs_4 = ReactionSystem(eqs_4, t; continuous_events = continuous_events_4, - discrete_events = discrete_events_4, spatial_ivs = sivs, - metadata = [MiscSystemData => "System 4"], systems = []) - @named rs_2 = ReactionSystem(eqs_2, t; continuous_events = continuous_events_2, - discrete_events = discrete_events_2, spatial_ivs = sivs, - metadata = [MiscSystemData => "System 2"], systems = []) - @named rs_3 = ReactionSystem(eqs_3, t; continuous_events = continuous_events_3, - discrete_events = discrete_events_3, spatial_ivs = sivs, - metadata = [MiscSystemData => "System 3"], systems = [rs_4]) - @named rs_1 = ReactionSystem(eqs_1, t; continuous_events = continuous_events_1, - discrete_events = discrete_events_1, spatial_ivs = sivs, - metadata = [MiscSystemData => "System 1"], systems = [rs_2, rs_3]) + @named rs_4 = ReactionSystem( + eqs_4, t; continuous_events = continuous_events_4, + discrete_events = discrete_events_4, spatial_ivs = sivs, + metadata = [MiscSystemData => "System 4"], systems = [] + ) + @named rs_2 = ReactionSystem( + eqs_2, t; continuous_events = continuous_events_2, + discrete_events = discrete_events_2, spatial_ivs = sivs, + metadata = [MiscSystemData => "System 2"], systems = [] + ) + @named rs_3 = ReactionSystem( + eqs_3, t; continuous_events = continuous_events_3, + discrete_events = discrete_events_3, spatial_ivs = sivs, + metadata = [MiscSystemData => "System 3"], systems = [rs_4] + ) + @named rs_1 = ReactionSystem( + eqs_1, t; continuous_events = continuous_events_1, + discrete_events = discrete_events_1, spatial_ivs = sivs, + metadata = [MiscSystemData => "System 1"], systems = [rs_2, rs_3] + ) rs = complete(rs_1) # Checks that the correct system is saved (both complete and incomplete ones). @@ -374,7 +382,7 @@ let [X ~ 5.0] => [X => X + 1.0] [X ~ 20.0] => [X => X - 1.0] end - @discrete_events 5.0 => [d => d/2] + @discrete_events 5.0 => [d => d / 2] d, X --> 0 end @@ -393,9 +401,9 @@ let rs = @reaction_network begin @ivs t x y z @parameters p - @species X(t,x,y) Y(t,x,y) XY(t,x,y) Z(t,x,y) - @variables V(t,x,z) - (kB,kD), X + Y <--> XY + @species X(t, x, y) Y(t, x, y) XY(t, x, y) Z(t, x, y) + @variables V(t, x, z) + (kB, kD), X + Y <--> XY end save_reactionsystem(testpath("serialised_rs.jl"), rs) @test Catalyst.isequivalent(rs, include(testpath("serialised_rs.jl"))) @@ -413,7 +421,7 @@ let @parameters k1 k2::Int64 rxs = [ Reaction(k1, [X1], [X2]), - Reaction(k2, [X2], [X1]) + Reaction(k2, [X2], [X1]), ] initial_conditions = Dict((X1 => 1.0, k2 => 2)) @@ -422,7 +430,7 @@ let conservationlaws(rs) # Serialises model and then loads and checks it. - @test_logs (:warn, ) match_mode=:any save_reactionsystem(testpath("serialised_rs.jl"), rs) + @test_logs (:warn,) match_mode = :any save_reactionsystem(testpath("serialised_rs.jl"), rs) rs_loaded = include(testpath("serialised_rs.jl")) @test Catalyst.isequivalent(rs, rs_loaded) @test isequal(ModelingToolkitBase.get_initial_conditions(rs), ModelingToolkitBase.get_initial_conditions(rs_loaded)) @@ -437,7 +445,7 @@ let rxs = [ Reaction(p, [], [X]), - Reaction(d, [X], []) + Reaction(d, [X], []), ] eq = D(V) ~ V_max - V @@ -450,7 +458,7 @@ end let # Checks for complete system. rs_complete = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X end save_reactionsystem(testpath("serialised_rs_complete.jl"), rs_complete) rs_complete_loaded = include(testpath("serialised_rs_complete.jl")) @@ -459,7 +467,7 @@ let # Checks for non-complete system. rs_incomplete = @network_component begin - (p,d), 0 <--> X + (p, d), 0 <--> X end save_reactionsystem(testpath("serialised_rs_incomplete.jl"), rs_incomplete) rs_incomplete_loaded = include(testpath("serialised_rs_incomplete.jl")) @@ -492,8 +500,8 @@ let Xcount ~ X + X2 end p, 0 --> X - d*X2, X => 0 - (k1,k2), 2X <--> X2 + d * X2, X => 0 + (k1, k2), 2X <--> X2 end # Checks its serialisation. diff --git a/test/network_analysis/conservation_laws.jl b/test/network_analysis/conservation_laws.jl index 0929fc61cc..ed27f51f6b 100644 --- a/test/network_analysis/conservation_laws.jl +++ b/test/network_analysis/conservation_laws.jl @@ -33,9 +33,11 @@ let # For the A + B <--> C subsystem one of these must occur # as a conservation law. - D = [1 -1 0 0 0 0 0 0 0 0; - -1 1 0 0 0 0 0 0 0 0 - 0 1 1 0 0 0 0 0 0 0] + D = [ + 1 -1 0 0 0 0 0 0 0 0; + -1 1 0 0 0 0 0 0 0 0 + 0 1 1 0 0 0 0 0 0 0 + ] @test any(D[j, :] == C[i, :] for i in 1:size(C, 1), j in 1:size(D, 1)) C = conservationlaws(rn) @@ -55,7 +57,7 @@ let # Networks for which there are known conservation laws (stored in `reaction_network_conslaws`). function consequiv(A, B) - rank([A; B]) == rank(A) == rank(B) + return rank([A; B]) == rank(A) == rank(B) end Cs_constraint = map(conservationlaws, reaction_networks_conserved) @test all(consequiv.(Matrix{Int}.(Cs_constraint), reaction_network_conslaws)) @@ -84,7 +86,7 @@ end let # Creates network with/without cached conservation laws. rn = @reaction_network rn begin - (k1,k2), X1 <--> X2 + (k1, k2), X1 <--> X2 end rn_cached = deepcopy(rn) conservationlaws(rn_cached) @@ -108,8 +110,10 @@ let end @unpack A, B, C, D, E, F1, F2, F3, k1, k2, m1, m2, b12, b23, b31 = rn sps = species(rn) - 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] + 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, + ] p = [k1 => 1.0, k2 => 0.1, m1 => 1.0, m2 => 2.0, b12 => 1.0, b23 => 2.0, b31 => 0.1] tspan = (0.0, 20.0) @@ -118,9 +122,9 @@ let oprob1 = ODEProblem(osys, [u0; p], tspan) oprob2 = ODEProblem(rn, u0, tspan, p) oprob3 = ODEProblem(rn, u0, tspan, p; remove_conserved = true) - osol1 = solve(oprob1, Tsit5(); abstol = 1e-8, reltol = 1e-8, saveat = 0.2) - osol2 = solve(oprob2, Tsit5(); abstol = 1e-8, reltol = 1e-8, saveat = 0.2) - osol3 = solve(oprob3, Tsit5(); abstol = 1e-8, reltol = 1e-8, saveat = 0.2) + osol1 = solve(oprob1, Tsit5(); abstol = 1.0e-8, reltol = 1.0e-8, saveat = 0.2) + osol2 = solve(oprob2, Tsit5(); abstol = 1.0e-8, reltol = 1.0e-8, saveat = 0.2) + osol3 = solve(oprob3, Tsit5(); abstol = 1.0e-8, reltol = 1.0e-8, saveat = 0.2) @test osol1[sps] ≈ osol2[sps] ≈ osol3[sps] let # SteadyStateProblem issue from MTK #4177 is now fixed. @@ -132,18 +136,18 @@ let nprob1b = NonlinearProblem{true}(nsys_ss, merge(Dict(u0), Dict(p))) nprob2 = NonlinearProblem(rn, u0, p; remove_conserved = true) nprob2b = NonlinearProblem(rn, u0, p; remove_conserved = true, strucmtkcompiletural_simplify = true) - nsol1 = solve(nprob1, NewtonRaphson(); abstol = 1e-8) - nsol1b = solve(nprob1b, NewtonRaphson(); abstol = 1e-8) - nsol2 = solve(nprob2, NewtonRaphson(); abstol = 1e-8) - nsol2b = solve(nprob2b, NewtonRaphson(); abstol = 1e-8) + nsol1 = solve(nprob1, NewtonRaphson(); abstol = 1.0e-8) + nsol1b = solve(nprob1b, NewtonRaphson(); abstol = 1.0e-8) + nsol2 = solve(nprob2, NewtonRaphson(); abstol = 1.0e-8) + nsol2b = solve(nprob2b, NewtonRaphson(); abstol = 1.0e-8) # Nonlinear problems cannot find steady states properly without removing conserved species. ssprob1 = SteadyStateProblem{true}(osys, merge(Dict(u0), Dict(p))) ssprob2 = SteadyStateProblem(rn, u0, p) ssprob3 = SteadyStateProblem(rn, u0, p; remove_conserved = true) - sssol1 = solve(ssprob1, DynamicSS(Tsit5()); abstol = 1e-8, reltol = 1e-8) - sssol2 = solve(ssprob2, DynamicSS(Tsit5()); abstol = 1e-8, reltol = 1e-8) - sssol3 = solve(ssprob3, DynamicSS(Tsit5()); abstol = 1e-8, reltol = 1e-8) + sssol1 = solve(ssprob1, DynamicSS(Tsit5()); abstol = 1.0e-8, reltol = 1.0e-8) + sssol2 = solve(ssprob2, DynamicSS(Tsit5()); abstol = 1.0e-8, reltol = 1.0e-8) + sssol3 = solve(ssprob3, DynamicSS(Tsit5()); abstol = 1.0e-8, reltol = 1.0e-8) @test nsol1[sps] ≈ nsol1b[sps] @test nsol1[sps] ≈ nsol2[sps] @test nsol1[sps] ≈ nsol2b[sps] @@ -153,8 +157,10 @@ let end # Creates SDEProblems using various approaches. - u0_sde = [A => 100.0, B => 20.0, C => 5.0, D => 10.0, E => 3.0, F1 => 8.0, F2 => 2.0, - F3 => 20.0] + u0_sde = [ + 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 = complete(sde_model(rn; remove_conserved = true)) sprob1 = SDEProblem(ssys, [u0_sde; p], tspan) sprob2 = SDEProblem(rn, u0_sde, tspan, p) @@ -189,7 +195,7 @@ let # Prepares the model. rn = @reaction_network rn begin @parameters kB::Int64 - (kB,kD), X + Y <--> XY + (kB, kD), X + Y <--> XY end sps = species(rn) @unpack kB, kD, X, Y, XY = rn @@ -209,7 +215,7 @@ end let # Creates `SDEProblem`s. rn = @reaction_network begin - (k1,k2), X1 <--> X2 + (k1, k2), X1 <--> X2 end u0 = Dict([:X1 => 100.0, :X2 => 120.0]) ps = [:k1 => 0.2, :k2 => 0.15] @@ -224,7 +230,7 @@ end let # Prepares `ODEProblem`s. rn = @reaction_network begin - (k1,k2), X1 <--> X2 + (k1, k2), X1 <--> X2 end osys = complete(ode_model(rn; remove_conserved = true)) u0_1 = [osys.X1 => 1.0, osys.X2 => 1.0] @@ -244,18 +250,18 @@ end # Checks `num_cons_laws` function. let rn1 = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X end @test Catalyst.num_cons_laws(rn1) == 0 rn2 = @reaction_network begin - (k1,k2), X1 <--> X2 + (k1, k2), X1 <--> X2 end @test Catalyst.num_cons_laws(rn2) == 1 rn3 = @reaction_network begin - (k1,k2), X1 <--> X2 - (k1,k2), Y1 <--> Y2 + (k1, k2), X1 <--> X2 + (k1, k2), Y1 <--> Y2 end @test Catalyst.num_cons_laws(rn3) == 2 end @@ -271,7 +277,7 @@ let @species X1(t) X2(t) rxs = [ Reaction(k1, [X1], [X2]), - Reaction(k2, [X2], [X1]) + Reaction(k2, [X2], [X1]), ] @named rs = ReactionSystem(rxs, t) osys = complete(ode_model(complete(rs); remove_conserved = true)) @@ -308,9 +314,9 @@ let end # Additional test checking that correct values are stored in systems of various types after conservation law removal. - let - rn = @reaction_network begin - (kB,kD), 2X <--> X2 +let + rn = @reaction_network begin + (kB, kD), 2X <--> X2 end u0 = [:X => 2.0, :X2 => 3.0] ps = [:kB => 0.1, :kD => 0.2] @@ -337,10 +343,10 @@ end let # Creates the model (contains both conserved and non-conserved species). rn = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X d, XY --> Y - (k1,k2), X + Y <--> XY - (k3,k4), 2Y <--> Y2 + (k1, k2), X + Y <--> XY + (k3, k4), 2Y <--> Y2 end # Finds a steady state at which we will compute the Jacobian. @@ -352,7 +358,7 @@ let # Creates a function which evaluates whether the Jacobian is singular. # Singularity means infinite condition number (here it is about 1e17). - function is_singular(prob; infthres = 1e12) + function is_singular(prob; infthres = 1.0e12) J = zeros(length(prob.u0), length(prob.u0)) ModelingToolkitBase.is_time_dependent(prob) ? prob.f.jac(J, prob.u0, prob.p, 0.0) : prob.f.jac(J, prob.u0, prob.p) return cond(J) > infthres @@ -384,8 +390,8 @@ end let # Prepares the problem inputs and computes the conservation equation. rn = @reaction_network begin - (k1,k2), 2X1 <--> X2 - (k3,k4), X1 + X2 <--> X3 + (k1, k2), 2X1 <--> X2 + (k3, k4), X1 + X2 <--> X3 end @unpack X1, X2, X3 = rn u0 = [X1 => 1.0, X2 => 1.0, X3 => 1.0] @@ -404,8 +410,8 @@ let # computed after initialization. Hence, comparisons are made for the integrators. # StochasticDiffEq defaults to abstol=reltol=0.01, which gets inherited by the # initialization NonlinearSolve. We pass tight tolerances to get precise initialization. - init_kwargs = solver isa ImplicitEM ? (; abstol = 1e-8, reltol = 1e-8) : (;) - for _ = 1:3 + init_kwargs = solver isa ImplicitEM ? (; abstol = 1.0e-8, reltol = 1.0e-8) : (;) + for _ in 1:3 integ_old = init(prob_old, solver; init_kwargs...) # Updates X2, checks the values of all species and Γ, then resets `prob_old`. X2_new = rand(rng, 1.0:10.0) @@ -479,7 +485,7 @@ end # Checks that jump `System`s with conservation laws cannot be generated. let rn = @reaction_network begin - (k1,k2), X1 <--> X2 + (k1, k2), X1 <--> X2 end @test_throws ArgumentError jump_model(rn; remove_conserved = true) end @@ -489,8 +495,8 @@ end let # Creates ODE System with conserved quantities. rs = @reaction_network begin - (k1,k2), X1 <--> X2 - (k1,k2), Y1 <--> Y2 + (k1, k2), X1 <--> X2 + (k1, k2), Y1 <--> Y2 end osys = ode_model(rs; remove_conserved = true) @@ -509,7 +515,7 @@ let @parameters k[1:2] rxs = [ Reaction(k[1], [X[1]], [X[2]]), - Reaction(k[2], [X[2]], [X[1]]) + Reaction(k[2], [X[2]], [X[1]]), ] @named rs = ReactionSystem(rxs, t) rs = complete(rs) @@ -518,7 +524,7 @@ let u0 = [X => [3.0, 9.0]] ps = [k => [1.0, 2.0]] oprob = ODEProblem(rs, u0, (0.0, 1000.0), ps; remove_conserved = true) - sol = solve(oprob, Vern7(); abstol = 1e-8, reltol = 1e-8) + sol = solve(oprob, Vern7(); abstol = 1.0e-8, reltol = 1.0e-8) @test sol[X[1]][end] ≈ 8.0 @test sol[X[2]][end] ≈ 4.0 end @@ -527,8 +533,8 @@ end let # Create models. rn = @reaction_network begin - (k1,k2), X1 <--> X2 - (k3,k4), X1 + X2 <--> 2X3 + (k1, k2), X1 <--> X2 + (k3, k4), X1 + X2 <--> 2X3 end u0 = [:X1 => 1.0, :X2 => 2.0, :X3 => 3.0] ps = [:k1 => 0.1, :k2 => 0.2, :k3 => 0.3, :k4 => 0.4] diff --git a/test/network_analysis/crn_theory.jl b/test/network_analysis/crn_theory.jl index 90a5d31d4d..312317d86a 100644 --- a/test/network_analysis/crn_theory.jl +++ b/test/network_analysis/crn_theory.jl @@ -131,7 +131,7 @@ end function testreversibility(rn, B, rev, weak_rev) @test isreversible(rn) == rev subrn = subnetworks(rn) - @test isweaklyreversible(rn, subrn) == weak_rev + return @test isweaklyreversible(rn, subrn) == weak_rev end # Tests reversibility for networks with known reversibility. @@ -256,7 +256,9 @@ let end let - rn = @reaction_network begin (k2, k1), A + B <--> 2A end + rn = @reaction_network begin + (k2, k1), A + B <--> 2A + end rev = true weak_rev = true testreversibility(rn, reactioncomplexes(rn)[2], rev, weak_rev) @@ -441,9 +443,9 @@ let end rcs, D = reactioncomplexes(rn) - rates1 = [:k1=>1.0, :k2=>1.0, :k3=>1.0, :k4=>1.0, :k5=>1.0, :k6=>1.0] + rates1 = [:k1 => 1.0, :k2 => 1.0, :k3 => 1.0, :k4 => 1.0, :k5 => 1.0, :k6 => 1.0] @test Catalyst.isdetailedbalanced(rn, rates1) == true - rates2 = [:k1=>2.0, :k2=>1.0, :k3=>1.0, :k4=>1.0, :k5=>1.0, :k6=>1.0] + rates2 = [:k1 => 2.0, :k2 => 1.0, :k3 => 1.0, :k4 => 1.0, :k5 => 1.0, :k6 => 1.0] @test Catalyst.isdetailedbalanced(rn, rates2) == false end @@ -468,9 +470,9 @@ let @test Catalyst.isdetailedbalanced(rn, rates) == false # Adjust rate constants to obey the independent cycle conditions. - rates[p[6]] = rates[p[1]]*rates[p[4]]*rates[p[5]] / (rates[p[2]]*rates[p[3]]) - rates[p[14]] = rates[p[13]]*rates[p[11]]*rates[p[8]] / (rates[p[12]]*rates[p[7]]) - rates[p[16]] = rates[p[8]]*rates[p[15]]*rates[p[9]]*rates[p[11]] / (rates[p[7]]*rates[p[12]]*rates[p[10]]) + rates[p[6]] = rates[p[1]] * rates[p[4]] * rates[p[5]] / (rates[p[2]] * rates[p[3]]) + rates[p[14]] = rates[p[13]] * rates[p[11]] * rates[p[8]] / (rates[p[12]] * rates[p[7]]) + rates[p[16]] = rates[p[8]] * rates[p[15]] * rates[p[9]] * rates[p[11]] / (rates[p[7]] * rates[p[12]] * rates[p[10]]) @test Catalyst.isdetailedbalanced(rn, rates) == true end @@ -493,16 +495,16 @@ let @test Catalyst.isdetailedbalanced(rn, rates) == false # Adjust rate constants to fulfill independent cycle conditions. - rates[p[8]] = rates[p[7]]*rates[p[5]]*rates[p[9]] / (rates[p[6]]*rates[p[10]]) - rates[p[3]] = rates[p[2]]*rates[p[4]]*rates[p[9]] / (rates[p[1]]*rates[p[10]]) + rates[p[8]] = rates[p[7]] * rates[p[5]] * rates[p[9]] / (rates[p[6]] * rates[p[10]]) + rates[p[3]] = rates[p[2]] * rates[p[4]] * rates[p[9]] / (rates[p[1]] * rates[p[10]]) @test Catalyst.isdetailedbalanced(rn, rates) == false # Should still fail - doesn't satisfy spanning forest conditions. # Adjust rate constants to fulfill spanning forest conditions. cons = rates[p[6]] / rates[p[5]] rates[p[1]] = rates[p[2]] * cons - rates[p[9]] = rates[p[10]] * cons^(3/2) - rates[p[8]] = rates[p[7]]*rates[p[5]]*rates[p[9]] / (rates[p[6]]*rates[p[10]]) - rates[p[3]] = rates[p[2]]*rates[p[4]]*rates[p[9]] / (rates[p[1]]*rates[p[10]]) + rates[p[9]] = rates[p[10]] * cons^(3 / 2) + rates[p[8]] = rates[p[7]] * rates[p[5]] * rates[p[9]] / (rates[p[6]] * rates[p[10]]) + rates[p[3]] = rates[p[2]] * rates[p[4]] * rates[p[9]] / (rates[p[1]] * rates[p[10]]) @test Catalyst.isdetailedbalanced(rn, rates) == true end diff --git a/test/network_analysis/network_properties.jl b/test/network_analysis/network_properties.jl index 77c5f8fe5c..662e51b7b6 100644 --- a/test/network_analysis/network_properties.jl +++ b/test/network_analysis/network_properties.jl @@ -25,7 +25,7 @@ end # Tests network analysis functions on MAPK network (by comparing to manually computed outputs). let MAPK = @reaction_network MAPK begin - (k₁, k₂),KKK + E1 <--> KKKE1 + (k₁, k₂), KKK + E1 <--> KKKE1 k₃, KKKE1 --> KKK_ + E1 (k₄, k₅), KKK_ + E2 <--> KKKE2 k₆, KKKE2 --> KKK + E2 @@ -35,7 +35,7 @@ let k₁₂, KKPKKK_ --> KKPP + KKK_ (k₁₃, k₁₄), KKP + KKPase <--> KKPKKPase k₁₅, KKPPKKPase --> KKP + KKPase - k₁₆,KKPKKPase --> KK + KKPase + k₁₆, KKPKKPase --> KK + KKPase (k₁₇, k₁₈), KKPP + KKPase <--> KKPPKKPase (k₁₉, k₂₀), KKPP + K <--> KKPPK k₂₁, KKPPK --> KKPP + KP @@ -63,10 +63,10 @@ let @test Catalyst.iscomplexbalanced(MAPK, rates) == false cyclemat = Catalyst.cycles(MAPK) S = netstoichmat(MAPK) - for i in 1:size(S, 2)-1 - if S[:,i] == -S[:,i+1] - cycle = [(j == i) || (j == i+1) ? 1 : 0 for j in 1:size(S,2)] - @test rank(cyclemat) == rank(hcat(cyclemat, cycle)) + for i in 1:(size(S, 2) - 1) + if S[:, i] == -S[:, i + 1] + cycle = [(j == i) || (j == i + 1) ? 1 : 0 for j in 1:size(S, 2)] + @test rank(cyclemat) == rank(hcat(cyclemat, cycle)) end end end @@ -76,9 +76,9 @@ let rn2 = @reaction_network begin (k₁, k₂), E + S1 <--> ES1 (k₃, k₄), E + S2 <--> ES2 - (k₅, k₆), S2 + ES1 <--> ES1S2 + (k₅, k₆), S2 + ES1 <--> ES1S2 (k₆, k₇), ES1S2 <--> S1 + ES2 - k₈, ES1S2 --> E+P + k₈, ES1S2 --> E + P (k₉, k₁₀), S1 <--> 0 (k₁₀, k₁₁), 0 <--> S2 k₁₂, P --> 0 @@ -104,16 +104,16 @@ let rn3 = @reaction_network begin (k₁, k₂), A11 <--> 0 (k₃, k₄), A11 <--> A13 - (k₅, k₆), 0 <--> A12 + (k₅, k₆), 0 <--> A12 (k₆, k₇), 0 <--> A2 k₈, A10 --> 0 (k₉, k₁₀), A12 <--> A6 - (k₁₁, k₁₂), A6<--> A4 + (k₁₁, k₁₂), A6 <--> A4 (k₁₃, k₁₄), A4 <--> A3 k₁₅, A8 --> A9 - (k₁₆,k₁₇), A8 <--> A3 + A11 + (k₁₆, k₁₇), A8 <--> A3 + A11 k₁₈, A9 --> A3 + A10 - k₁₉, A2+A4 --> A2 + A6 + k₁₉, A2 + A4 --> A2 + A6 end rcs, B = reactioncomplexes(rn3) @test length(rcs) == 15 @@ -148,8 +148,8 @@ let tslcs = terminallinkageclasses(rn) @test length(slcs) == 3 @test length(tslcs) == 2 - @test issubset([[1,2], [3,4,5], [6,7]], slcs) - @test issubset([[3,4,5], [6,7]], tslcs) + @test issubset([[1, 2], [3, 4, 5], [6, 7]], slcs) + @test issubset([[3, 4, 5], [6, 7]], tslcs) end # b) Makes the D + E --> G reaction irreversible. Thus, (D+E) becomes a non-terminal linkage class. Checks whether correctly identifies both (A, B+C) and (D+E) as non-terminal @@ -168,8 +168,8 @@ let tslcs = terminallinkageclasses(rn) @test length(slcs) == 4 @test length(tslcs) == 2 - @test issubset([[1,2], [3,4,5], [6], [7]], slcs) - @test issubset([[3,4,5], [7]], tslcs) + @test issubset([[1, 2], [3, 4, 5], [6], [7]], slcs) + @test issubset([[3, 4, 5], [7]], tslcs) end # From a), makes the B + C <--> D reaction reversible. Thus, the non-terminal (A, B+C) linkage class gets absorbed into the terminal (A, B+C, D, E, 2F) linkage class, and the terminal linkage classes and strong linkage classes coincide. @@ -188,8 +188,8 @@ let tslcs = terminallinkageclasses(rn) @test length(slcs) == 2 @test length(tslcs) == 2 - @test issubset([[1,2,3,4,5], [6,7]], slcs) - @test issubset([[1,2,3,4,5], [6,7]], tslcs) + @test issubset([[1, 2, 3, 4, 5], [6, 7]], slcs) + @test issubset([[1, 2, 3, 4, 5], [6, 7]], tslcs) end # Simple test for strong and terminal linkage classes @@ -209,8 +209,8 @@ let tslcs = terminallinkageclasses(rn) @test length(slcs) == 3 @test length(tslcs) == 2 - @test issubset([[1,2], [3,4], [5,6,7]], slcs) - @test issubset([[3,4], [5,6,7]], tslcs) + @test issubset([[1, 2], [3, 4], [5, 6, 7]], slcs) + @test issubset([[3, 4], [5, 6, 7]], tslcs) end # Cycle Test: Open Reaction Network @@ -231,27 +231,31 @@ end # From stoichiometric matrix. Reference: Trinh, 2008, https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2909134/ let - S = [1 -1 0 0 -1 0 0 0 0; + S = [ + 1 -1 0 0 -1 0 0 0 0; 0 0 0 0 1 -1 -1 -1 0; 0 1 -1 0 0 1 0 0 0; 0 0 1 0 0 0 0 0 -1; - 0 0 1 -1 0 0 2 0 0] - - EFMs = [1 0 1 1 0 1 1 1; - 1 0 0 1 0 0 1 0; - 0 1 0 1 0 0 0 1; - 0 1 0 1 2 2 2 1; - 0 0 1 0 0 1 0 1; - -1 1 0 0 0 0 -1 1; - 0 0 0 0 1 1 1 0; - 1 -1 1 0 -1 0 0 0; - 0 1 0 1 0 0 0 1] - - cyclemat = Catalyst.cycles(S) - for i in 1:size(EFMs, 2) - EFM = EFMs[:, i] - @test rank(cyclemat) == rank(hcat(cyclemat, EFM)) - end + 0 0 1 -1 0 0 2 0 0 + ] + + EFMs = [ + 1 0 1 1 0 1 1 1; + 1 0 0 1 0 0 1 0; + 0 1 0 1 0 0 0 1; + 0 1 0 1 2 2 2 1; + 0 0 1 0 0 1 0 1; + -1 1 0 0 0 0 -1 1; + 0 0 0 0 1 1 1 0; + 1 -1 1 0 -1 0 0 0; + 0 1 0 1 0 0 0 1 + ] + + cyclemat = Catalyst.cycles(S) + for i in 1:size(EFMs, 2) + EFM = EFMs[:, i] + @test rank(cyclemat) == rank(hcat(cyclemat, EFM)) + end end # No cycles should exist in the following network (the graph is treelike and irreversible) @@ -280,7 +284,7 @@ let # Declares network. rs = @reaction_network begin k1, X1 + X2 --> X3 + X4 - (k2,k2), X3 + X4 <--> X1 + (k2, k2), X3 + X4 <--> X1 k3, X1 --> X2 k4, X1 + X2 --> X2 end @@ -327,7 +331,7 @@ end let MAPK = @reaction_network MAPK begin - (k₁, k₂),KKK + E1 <--> KKKE1 + (k₁, k₂), KKK + E1 <--> KKKE1 k₃, KKKE1 --> KKK_ + E1 (k₄, k₅), KKK_ + E2 <--> KKKE2 k₆, KKKE2 --> KKK + E2 @@ -337,7 +341,7 @@ let k₁₂, KKPKKK_ --> KKPP + KKK_ (k₁₃, k₁₄), KKP + KKPase <--> KKPKKPase k₁₅, KKPPKKPase --> KKP + KKPase - k₁₆,KKPKKPase --> KK + KKPase + k₁₆, KKPKKPase --> KK + KKPase (k₁₇, k₁₈), KKPP + KKPase <--> KKPPKKPase (k₁₉, k₂₀), KKPP + K <--> KKPPK k₂₁, KKPPK --> KKPP + KP @@ -351,33 +355,34 @@ let Φ = Catalyst.massactionvector(MAPK) specs = species(MAPK) - truevec = [MAPK.KKK * MAPK.E1, - MAPK.KKKE1, - MAPK.KKK_ * MAPK.E1, - MAPK.KKK_ * MAPK.E2, - MAPK.KKKE2, - MAPK.KKK * MAPK.E2, - MAPK.KK * MAPK.KKK_, - MAPK.KK_KKK_, - MAPK.KKP * MAPK.KKK_, - MAPK.KKPKKK_, - MAPK.KKPP * MAPK.KKK_, - MAPK.KKP * MAPK.KKPase, - MAPK.KKPKKPase, - MAPK.KKPPKKPase, - MAPK.KK * MAPK.KKPase, - MAPK.KKPP * MAPK.KKPase, - MAPK.KKPP * MAPK.K, - MAPK.KKPPK, - MAPK.KKPP * MAPK.KP, - MAPK.KPKKPP, - MAPK.KPP * MAPK.KKPP, - MAPK.KP * MAPK.KPase, - MAPK.KPKPase, - MAPK.KKPPKPase, - MAPK.K * MAPK.KPase, - MAPK.KPP * MAPK.KPase, - ] + truevec = [ + MAPK.KKK * MAPK.E1, + MAPK.KKKE1, + MAPK.KKK_ * MAPK.E1, + MAPK.KKK_ * MAPK.E2, + MAPK.KKKE2, + MAPK.KKK * MAPK.E2, + MAPK.KK * MAPK.KKK_, + MAPK.KK_KKK_, + MAPK.KKP * MAPK.KKK_, + MAPK.KKPKKK_, + MAPK.KKPP * MAPK.KKK_, + MAPK.KKP * MAPK.KKPase, + MAPK.KKPKKPase, + MAPK.KKPPKKPase, + MAPK.KK * MAPK.KKPase, + MAPK.KKPP * MAPK.KKPase, + MAPK.KKPP * MAPK.K, + MAPK.KKPPK, + MAPK.KKPP * MAPK.KP, + MAPK.KPKKPP, + MAPK.KPP * MAPK.KKPP, + MAPK.KP * MAPK.KPase, + MAPK.KPKPase, + MAPK.KKPPKPase, + MAPK.K * MAPK.KPase, + MAPK.KPP * MAPK.KPase, + ] @test isequal(Φ, truevec) K = Catalyst.fluxmat(MAPK) @@ -393,9 +398,9 @@ let @test isequal(K[1, 1], MAPK.k₁) @test all(==(0), K[1, 2:end]) @test isequal(K[2, 2], MAPK.k₂) - @test all(==(0), vcat(K[2,1], K[2,3:end])) + @test all(==(0), vcat(K[2, 1], K[2, 3:end])) @test isequal(K[3, 2], MAPK.k₃) - @test all(==(0), vcat(K[3,1], K[3,3:end])) + @test all(==(0), vcat(K[3, 1], K[3, 3:end])) @test count(k -> !isequal(k, 0), K) == length(reactions(MAPK)) A_k = Catalyst.laplacianmat(MAPK) @@ -403,11 +408,11 @@ let S = netstoichmat(MAPK) Y = complexstoichmat(MAPK) - @test isequal(S*K, Y*A_k) + @test isequal(S * K, Y * A_k) eqs = Catalyst.assemble_oderhs(MAPK, specs) - @test all(_iszero, simplify(eqs - S*K*Φ)) - @test_broken all(_iszero, simplify(eqs - Y*A_k*Φ)) # Does not simplify to 0 (it should) due to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. + @test all(_iszero, simplify(eqs - S * K * Φ)) + @test_broken all(_iszero, simplify(eqs - Y * A_k * Φ)) # Does not simplify to 0 (it should) due to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. # Test using numbers k = rand(rng, numparams(MAPK)) @@ -425,8 +430,8 @@ let for i in 1:length(eqs) numeqs[i] = substitute(eqs[i], ratemap) end - @test all(_iszero, simplify(numeqs - S*K*Φ)) - @test all(_iszero, simplify(numeqs - Y*A_k*Φ)) + @test all(_iszero, simplify(numeqs - S * K * Φ)) + @test all(_iszero, simplify(numeqs - Y * A_k * Φ)) end # Test handling for weird complexes and combinatoric rate laws. @@ -438,15 +443,19 @@ let Φ = Catalyst.massactionvector(rn) specs = species(rn) - crvec = [rn.X^2/2 * rn.Y * rn.Z^3/6, - 1., - rn.Y^2/2 * rn.Z^2/2, - rn.X^3/6] + crvec = [ + rn.X^2 / 2 * rn.Y * rn.Z^3 / 6, + 1.0, + rn.Y^2 / 2 * rn.Z^2 / 2, + rn.X^3 / 6, + ] @test isequal(Φ, crvec) - ncrvec = [rn.X^2 * rn.Y * rn.Z^3, - 1., - rn.Y^2 * rn.Z^2, - rn.X^3] + ncrvec = [ + rn.X^2 * rn.Y * rn.Z^3, + 1.0, + rn.Y^2 * rn.Z^2, + rn.X^3, + ] Φ_2 = Catalyst.massactionvector(rn; combinatoric_ratelaws = false) @test isequal(Φ_2, ncrvec) @@ -456,12 +465,12 @@ let Y = complexstoichmat(rn) K = fluxmat(rn) A_k = laplacianmat(rn) - @test all(_iszero, simplify(eqs - S*K*Φ)) - @test_broken all(_iszero, simplify(eqs - Y*A_k*Φ)) # Does not simplify to 0 (it should) due to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. + @test all(_iszero, simplify(eqs - S * K * Φ)) + @test_broken all(_iszero, simplify(eqs - Y * A_k * Φ)) # Does not simplify to 0 (it should) due to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. eq_ncr = Catalyst.assemble_oderhs(rn, specs; combinatoric_ratelaws = false) - @test all(_iszero, simplify(eq_ncr - S*K*Φ_2)) - @test_broken all(_iszero, simplify(eq_ncr - Y*A_k*Φ_2)) # Does not simplify to 0 (it should) due to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. + @test all(_iszero, simplify(eq_ncr - S * K * Φ_2)) + @test_broken all(_iszero, simplify(eq_ncr - Y * A_k * Φ_2)) # Does not simplify to 0 (it should) due to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. # Test that the ODEs with rate constants are the same. k = rand(rng, numparams(rn)) @@ -475,37 +484,37 @@ let numeqs[i] = substitute(eqs[i], ratemap) end @test_broken false # all(_iszero, simplify(numeqs - S*K*Φ)) # Sometimes does not simplify to 0 (it should), but something very close. Potentially related to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. - @test_broken all(_iszero, simplify(numeqs - Y*A_k*Φ)) # Does not simplify to 0 (it should). Potentially related to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. + @test_broken all(_iszero, simplify(numeqs - Y * A_k * Φ)) # Does not simplify to 0 (it should). Potentially related to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. numeqs_ncr = similar(eq_ncr) for i in 1:length(eq_ncr) numeqs_ncr[i] = substitute(eq_ncr[i], ratemap) end - @test all(_iszero, simplify(numeqs_ncr - S*K*Φ_2)) - @test_broken all(_iszero, simplify(numeqs_ncr - Y*A_k*Φ_2)) # Does not simplify to 0 (it should). Potentially related to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. + @test all(_iszero, simplify(numeqs_ncr - S * K * Φ_2)) + @test_broken all(_iszero, simplify(numeqs_ncr - Y * A_k * Φ_2)) # Does not simplify to 0 (it should). Potentially related to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. # Test that handling of species concentrations is correct. - u0vec = [:X => 3., :Y => .5, :Z => 2.] + u0vec = [:X => 3.0, :Y => 0.5, :Z => 2.0] u0map = Dict(u0vec) u0tup = Tuple(u0vec) Φ = Catalyst.massactionvector(rn, u0vec) - @test isequal(Φ[1], 3.) + @test isequal(Φ[1], 3.0) Φ_2 = Catalyst.massactionvector(rn, u0tup; combinatoric_ratelaws = false) - @test isequal(Φ_2[1], 36.) + @test isequal(Φ_2[1], 36.0) Φ = Catalyst.massactionvector(rn, u0map) - @test isequal(Φ[1], 3.) + @test isequal(Φ[1], 3.0) # Test full simplification. u0map = Catalyst.symmap_to_varmap(rn, u0map) numeqs = [substitute(eq, u0map) for eq in numeqs] - for (v1,v2,v3) in zip(numeqs, S*K*Φ, Y*A_k*Φ) + for (v1, v2, v3) in zip(numeqs, S * K * Φ, Y * A_k * Φ) @test SymbolicUtils.unwrap_const(v1) ≈ SymbolicUtils.unwrap_const(ModelingToolkitBase.unwrap(v2)) @test SymbolicUtils.unwrap_const(v1) ≈ SymbolicUtils.unwrap_const(ModelingToolkitBase.unwrap(v3)) end numeqs_ncr = [substitute(eq, u0map) for eq in numeqs_ncr] - for (v1,v2,v3) in zip(numeqs_ncr, S*K*Φ_2, Y*A_k*Φ_2) + for (v1, v2, v3) in zip(numeqs_ncr, S * K * Φ_2, Y * A_k * Φ_2) @test SymbolicUtils.unwrap_const(v1) ≈ SymbolicUtils.unwrap_const(ModelingToolkitBase.unwrap(v2)) @test SymbolicUtils.unwrap_const(v1) ≈ SymbolicUtils.unwrap_const(ModelingToolkitBase.unwrap(v3)) end @@ -514,13 +523,13 @@ end ### Error Handling Tests ### # Tests that non-integer stoichiometry throws a helpful error in network analysis functions. -let +let # Create a network with non-integer stoichiometry. rn = @reaction_network begin - k1, 2.5*A --> B + k1, 2.5 * A --> B k2, B --> C end - + # Test that network analysis functions that require integer stoichiometry error appropriately. @test_throws ErrorException reactioncomplexes(rn) @test_throws ErrorException reactioncomplexmap(rn) @@ -539,10 +548,10 @@ let @test occursin("non-integer stoichiometry", e.msg) @test occursin("Network analysis functions", e.msg) end - + # Confirm that integer stoichiometry works fine. rn_int = @reaction_network begin - k1, 2*A --> B + k1, 2 * A --> B k2, B --> C end @test length(reactioncomplexes(rn_int)[1]) == 3 diff --git a/test/reactionsystem_core/coupled_equation_crn_systems.jl b/test/reactionsystem_core/coupled_equation_crn_systems.jl index a7daef61d2..d16ecfb3b4 100644 --- a/test/reactionsystem_core/coupled_equation_crn_systems.jl +++ b/test/reactionsystem_core/coupled_equation_crn_systems.jl @@ -25,7 +25,7 @@ let eqs = [ Reaction(p, nothing, [X]), Reaction(d, [X], nothing), - D(A) ~ p*X - k*A + D(A) ~ p * X - k * A, ] @named coupled_rs = ReactionSystem(eqs, t) coupled_rs = complete(coupled_rs) @@ -46,18 +46,18 @@ let # Checks that the correct steady state is found through ODEProblem. oprob = ODEProblem(coupled_rs, u0, tspan, ps) - osol = solve(oprob, Vern7(); abstol = 1e-8, reltol = 1e-8) - @test osol[[X,A]][end] ≈ [2.0, 1.0] + osol = solve(oprob, Vern7(); abstol = 1.0e-8, reltol = 1.0e-8) + @test osol[[X, A]][end] ≈ [2.0, 1.0] # Checks that the correct steady state is found through NonlinearProblem. nlprob = NonlinearProblem(coupled_rs, u0, ps) - nlsol = solve(nlprob; abstol = 1e-8, reltol = 1e-8) - @test nlsol[[X,A]] ≈ [2.0, 1.0] + nlsol = solve(nlprob; abstol = 1.0e-8, reltol = 1.0e-8) + @test nlsol[[X, A]] ≈ [2.0, 1.0] # Checks that the correct steady state is found through SteadyStateProblem. ssprob = SteadyStateProblem(coupled_rs, u0, ps) - sssol = solve(ssprob, DynamicSS(Rosenbrock23()); abstol = 1e-8, reltol = 1e-8) - @test sssol[[X,A]] ≈ [2.0, 1.0] + sssol = solve(ssprob, DynamicSS(Rosenbrock23()); abstol = 1.0e-8, reltol = 1.0e-8) + @test sssol[[X, A]] ≈ [2.0, 1.0] end # Checks that coupled systems created via the DSL, extension, and programmatically are identical. @@ -74,15 +74,15 @@ let eqs_prog = [ D(A) ~ X1 + a - A, D(B) ~ X2 + b - B, - Reaction(k1*A, [X1], [X2]), - Reaction(k2*B, [X2], [X1]) + Reaction(k1 * A, [X1], [X2]), + Reaction(k2 * B, [X2], [X1]), ] coupled_rs_prog = ReactionSystem(eqs_prog, t, [A, B, X1, X2], [k1, k2, a, b]; name = :coupled_rs) coupled_rs_prog = complete(coupled_rs_prog) # Creates model by extending a `ReactionSystem` with another ReactionSystem containing ODEs. rn_extended = @network_component begin - ($k1*$A, $k2*$B), X1 <--> X2 + ($k1 * $A, $k2 * $B), X1 <--> X2 end eqs_extended = [ D(A) ~ X1 + a - A @@ -98,7 +98,7 @@ let D(A) ~ X1 + a - A D(B) ~ X2 + b - B end - (k1*A, k2*B), X1 <--> X2 + (k1 * A, k2 * B), X1 <--> X2 end # Checks that models are equivalent and contain the correct stuff. @@ -112,12 +112,12 @@ let # Simulates the three models, checking that they all yield the correct end point. u0 = [A => 1.0, B => 1.0, X1 => 10.0, X2 => 10.0] - tspan = (0.0, 100.) + tspan = (0.0, 100.0) ps = [a => 1.0, b => 1.0, k1 => 1.0, k2 => 1.0] for coupled_rs in [coupled_rs_prog, coupled_rs_extended, coupled_rs_dsl] oprob = ODEProblem(coupled_rs, u0, tspan, ps) - osol = solve(oprob, Vern7(); abstol = 1e-8, reltol = 1e-8) - osol[[A,B,X1,X2]][end] ≈ [10.0, 10.0, 11.0, 11.0] + osol = solve(oprob, Vern7(); abstol = 1.0e-8, reltol = 1.0e-8) + osol[[A, B, X1, X2]][end] ≈ [10.0, 10.0, 11.0, 11.0] end end @@ -135,7 +135,7 @@ let eqs = [ Reaction(p, nothing, [X]), Reaction(d, [X], nothing), - a*A^2 ~ X + b + a * A^2 ~ X + b, ] @named coupled_rs = ReactionSystem(eqs, t) coupled_rs = complete(coupled_rs) @@ -159,22 +159,24 @@ let @test_throws Exception SteadyStateProblem(coupled_rs, u0, ps) # Checks that the correct steady state is found through ODEProblem. - oprob = ODEProblem(coupled_rs, u0, tspan, ps; mtkcompile = true, - guesses = [A => 1.0]) - osol = solve(oprob, Rosenbrock23(); abstol = 1e-8, reltol = 1e-8) - @test osol[[X,A]][end] ≈ [2.0, 3.0] + oprob = ODEProblem( + coupled_rs, u0, tspan, ps; mtkcompile = true, + guesses = [A => 1.0] + ) + osol = solve(oprob, Rosenbrock23(); abstol = 1.0e-8, reltol = 1.0e-8) + @test osol[[X, A]][end] ≈ [2.0, 3.0] # Checks that the correct steady state is found through NonlinearProblem. - u0 = [X => 0.1, A => 16.1/2] + u0 = [X => 0.1, A => 16.1 / 2] nlprob = NonlinearProblem(coupled_rs, u0, ps, mtkcompile = true) nlsol = solve(nlprob) - @test nlsol[[X,A]] ≈ [2.0, 3.0] + @test nlsol[[X, A]] ≈ [2.0, 3.0] # Checks that the correct steady state is found through SteadyStateProblem. u0 = [X => 0.1, A => 1.0] ssprob = SteadyStateProblem(coupled_rs, u0, ps; mtkcompile = true) - sssol = solve(ssprob, DynamicSS(Rosenbrock23()); abstol = 1e-8, reltol = 1e-8) - @test_broken sssol[[X,A]] ≈ [2.0, 3.0] # The previous lines fails to solve. Issue at: https://github.com/SciML/ModelingToolkit.jl/issues/4174. This currently also yields a warning. + sssol = solve(ssprob, DynamicSS(Rosenbrock23()); abstol = 1.0e-8, reltol = 1.0e-8) + @test_broken sssol[[X, A]] ≈ [2.0, 3.0] # The previous lines fails to solve. Issue at: https://github.com/SciML/ModelingToolkit.jl/issues/4174. This currently also yields a warning. end @@ -195,7 +197,7 @@ let Δ(B) ~ sqrt(A + X + b) - B, Reaction(p, nothing, [X], nothing, [2]), Reaction(d, [X], nothing), - (X + C)*B ~ A + (X + C) * B ~ A, ] @named coupled_rs = ReactionSystem(eqs, τ) coupled_rs = complete(coupled_rs) @@ -206,14 +208,18 @@ let # Creates and solves a ODE, SteadyState, and Nonlinear problems. # Success is tested by checking that the same steady state solution is found. - oprob = ODEProblem(coupled_rs, u0, (0.0, 1000.0), ps; mtkcompile = true, - warn_initialize_determined = false) - ssprob = SteadyStateProblem(coupled_rs, u0, ps; mtkcompile = true, - warn_initialize_determined = false) + oprob = ODEProblem( + coupled_rs, u0, (0.0, 1000.0), ps; mtkcompile = true, + warn_initialize_determined = false + ) + ssprob = SteadyStateProblem( + coupled_rs, u0, ps; mtkcompile = true, + warn_initialize_determined = false + ) nlprob = NonlinearProblem(coupled_rs, u0, ps; mtkcompile = true) - osol = solve(oprob, Rosenbrock23(); abstol = 1e-8, reltol = 1e-8) - sssol = solve(ssprob, DynamicSS(Rosenbrock23()); abstol = 1e-8, reltol = 1e-8) - nlsol = solve(nlprob; abstol = 1e-8, reltol = 1e-8) + osol = solve(oprob, Rosenbrock23(); abstol = 1.0e-8, reltol = 1.0e-8) + sssol = solve(ssprob, DynamicSS(Rosenbrock23()); abstol = 1.0e-8, reltol = 1.0e-8) + nlsol = solve(nlprob; abstol = 1.0e-8, reltol = 1.0e-8) @test osol[[A, B, C, X]][end] ≈ sssol[[A, B, C, X]] ≈ nlsol[[A, B, C, X]] end @@ -233,8 +239,8 @@ let Reaction(p, [], [X]), Reaction(d, [X], []), Reaction(d, [X], nothing, [2], nothing), - D(V) ~ X - v*V, - W^2 ~ log(V) + X + D(V) ~ X - v * V, + W^2 ~ log(V) + X, ] @named coupled_rs = ReactionSystem(eqs, t) @@ -280,8 +286,8 @@ let eqs = [ Reaction(k1, [X], [X2], [2], [1]), Reaction(k2, [X2], [X], [1], [2]), - D(A) ~ k*X2 - A, - B + A ~ b1*X + b2*X2 + D(A) ~ k * X2 - A, + B + A ~ b1 * X + b2 * X2, ] @named coupled_rs = ReactionSystem(eqs, τ) coupled_rs = complete(coupled_rs) @@ -304,25 +310,25 @@ end # Checks that metadata, types, and default values are carried through correctly. let # Creates the model - @parameters a1 [description="Parameter a1"] a2::Rational{Int64} a3=0.3 a4::Rational{Int64}=4//10 [description="Parameter a4"] - @parameters b1 [description="Parameter b1"] b2::Int64 b3 = 3 b4::Int64=4 [description="Parameter b4"] - @parameters c1 [description="Parameter c1"] c2::Float32 c3=30.0 c4::Float32=40.0 [description="Parameter c4"] - @species A1(t) [description="Species A1"] A2(t)=0.2 A3(t)=0.3 [description="Species A3"] A4(t) - @variables B1(t) [description="Variable B1"] B2(t)=2.0 B3(t)=3.0 [description="Variable B3"] B4(t) - @variables C1(t) [description="Variable C1"] C2(t) C3(t) [description="Variable C3"] C4(t) + @parameters a1 [description = "Parameter a1"] a2::Rational{Int64} a3 = 0.3 a4::Rational{Int64} = 4 // 10 [description = "Parameter a4"] + @parameters b1 [description = "Parameter b1"] b2::Int64 b3 = 3 b4::Int64 = 4 [description = "Parameter b4"] + @parameters c1 [description = "Parameter c1"] c2::Float32 c3 = 30.0 c4::Float32 = 40.0 [description = "Parameter c4"] + @species A1(t) [description = "Species A1"] A2(t) = 0.2 A3(t) = 0.3 [description = "Species A3"] A4(t) + @variables B1(t) [description = "Variable B1"] B2(t) = 2.0 B3(t) = 3.0 [description = "Variable B3"] B4(t) + @variables C1(t) [description = "Variable C1"] C2(t) C3(t) [description = "Variable C3"] C4(t) eqs = [ Reaction(a1, [A1], nothing), Reaction(a2, [A2], nothing), Reaction(a3, [A3], nothing), Reaction(a4, [A4], nothing), - D(B1) ~ b1*B1, - D(B2) ~ b2*B2, - D(B3) ~ b3*B3, - D(B4) ~ b4*B4, + D(B1) ~ b1 * B1, + D(B2) ~ b2 * B2, + D(B3) ~ b3 * B3, + D(B4) ~ b4 * B4, C1 ~ sqrt(c1 + B1^5), C2 ~ sqrt(c2 + B2^5), C3 ~ sqrt(c3 + B3^5), - C4 ~ sqrt(c4 + B4^5) + C4 ~ sqrt(c4 + B4^5), ] @named coupled_rs = ReactionSystem(eqs, t) coupled_rs = complete(coupled_rs) @@ -356,7 +362,7 @@ let @test getdescription(coupled_rs.c1) == "Parameter c1" @test getdescription(coupled_rs.c4) == "Parameter c4" @test getdefault(coupled_rs.a3) == 0.3 - @test getdefault(coupled_rs.a4) == 4//10 + @test getdefault(coupled_rs.a4) == 4 // 10 @test getdefault(coupled_rs.b3) == 3 @test getdefault(coupled_rs.b4) == 4 @test getdefault(coupled_rs.c3) == 30 @@ -376,7 +382,7 @@ let u0 = [A1 => 0.1, A4 => 0.4, B1 => 1.0, B4 => 4.0] u0_nlp = [A1 => 0.1, A4 => 0.4, B1 => 1.0, B4 => 4.0, C1 => sqrt(10.0 + 1.0^5), C2 => sqrt(20.0 + 2.0^5), C3 => sqrt(30.0 + 3.0^5), C4 => sqrt(40.0 + 4.0^5)] tspan = (0.0, 1.0) - ps = [a1 => 0.1, a2 => 2//10, b1 => 1.0, b2 => 2, c1 => 10.0, c2 => 20.0] + ps = [a1 => 0.1, a2 => 2 // 10, b1 => 1.0, b2 => 2, c1 => 10.0, c2 => 20.0] # Create ODE structures. oprob = ODEProblem(coupled_rs, u0, tspan, ps; mtkcompile = true, warn_initialize_determined = false) @@ -397,9 +403,9 @@ let for mtk_struct in [oprob, oint, osol, sprob, sint, ssol, nlprob, nlint, nlsol] # Parameters. @test mtk_struct.ps[a1] == 0.1 - @test_broken mtk_struct.ps[a2] == 2//10 # An equivalent value (but different nominator and denominator) is generated. https://github.com/SciML/ModelingToolkit.jl/issues/4163. + @test_broken mtk_struct.ps[a2] == 2 // 10 # An equivalent value (but different nominator and denominator) is generated. https://github.com/SciML/ModelingToolkit.jl/issues/4163. @test mtk_struct.ps[a3] == 0.3 - @test mtk_struct.ps[a4] == 4//10 + @test mtk_struct.ps[a4] == 4 // 10 @test mtk_struct.ps[b1] == 1.0 @test mtk_struct.ps[b2] == 2 @test mtk_struct.ps[b3] == 3.0 @@ -461,8 +467,8 @@ let eqs = [ Reaction(p, nothing, [X]; metadata = [:noise_scaling => 0.1]), Reaction(d, [X], nothing; metadata = [:noise_scaling => 0.1]), - D(A) ~ X - k1*A, - D(B) ~ k2 - B + D(A) ~ X - k1 * A, + D(B) ~ k2 - B, ] @named coupled_rs = ReactionSystem(eqs, t) coupled_rs = complete(coupled_rs) @@ -474,9 +480,9 @@ let # Checks that the simulations have the expected means (or endpoint, for B). sprob = SDEProblem(coupled_rs, u0, tspan, ps) - ssol = solve(sprob, ImplicitEM(); maxiters = 1e9, seed) - @test mean(ssol[:X]) ≈ 100.0 atol = 1e-2 rtol = 1e-2 - @test mean(ssol[:A]) ≈ 50.0 atol = 1e-2 rtol = 1e-2 + ssol = solve(sprob, ImplicitEM(); maxiters = 1.0e9, seed) + @test mean(ssol[:X]) ≈ 100.0 atol = 1.0e-2 rtol = 1.0e-2 + @test mean(ssol[:A]) ≈ 50.0 atol = 1.0e-2 rtol = 1.0e-2 @test ssol[:B][end] ≈ 20.0 end @@ -490,7 +496,7 @@ let eqs = [ Reaction(p, nothing, [X]), Reaction(d, [X], nothing), - 2 + k1 * A ~ 3 + k2 * X + 2 + k1 * A ~ 3 + k2 * X, ] @named coupled_rs = ReactionSystem(eqs, t) coupled_rs = complete(coupled_rs) @@ -505,8 +511,8 @@ let # Checks the algebraic equation holds. sprob = SDEProblem(coupled_rs, u0, tspan, ps; guesses = [A => 1.0], mtkcompile = true, warn_initialize_determined = false) - ssol = solve(sprob, ImplicitEM(); abstol = 1e-5, reltol = 1e-5) - @test (2 .+ ps[k1] * ssol[:A]) ≈ (3 .+ ps[k2] * ssol[:X]) atol = 1e-1 rtol = 1e-1 + ssol = solve(sprob, ImplicitEM(); abstol = 1.0e-5, reltol = 1.0e-5) + @test (2 .+ ps[k1] * ssol[:A]) ≈ (3 .+ ps[k2] * ssol[:X]) atol = 1.0e-1 rtol = 1.0e-1 end @@ -528,7 +534,7 @@ let @differentials D = Differential(t) @variables V(t) @equations D(D(V)) ~ 1.0 - V - (p,d), 0 <--> X + (p, d), 0 <--> X end @test_throws Exception ss_ode_model(rs) end @@ -538,7 +544,7 @@ let rs = @reaction_network begin @variables U(t) @equations D(V) ~ 1.0 - V + D(U) - (p,d), 0 <--> X + (p, d), 0 <--> X end @test_throws Exception ss_ode_model(rs) end @@ -549,7 +555,7 @@ let @differentials D = Differential(t) @variables V(t) @equations D(V) + V ~ 1.0 - V - (p,d), 0 <--> X + (p, d), 0 <--> X end @test_throws Exception ss_ode_model(rs) end @@ -568,8 +574,8 @@ end eqs = [ Reaction(p, nothing, [X]), Reaction(d, [X], nothing), - D(D(A)) + 2ω*D(A) +(ω^2)*A ~ 0, - A + k*(B + D(A)) ~ X + D(D(A)) + 2ω * D(A) + (ω^2) * A ~ 0, + A + k * (B + D(A)) ~ X, ] @named coupled_rs = ReactionSystem(eqs, t) coupled_rs = complete(coupled_rs) @@ -578,18 +584,18 @@ end # Checks that ODE an simulation of the system achieves the correct steady state. oprob = ODEProblem(coupled_rs, u0, (0.0, 1000.0), ps; mtkcompile = true) - osol = solve(oprob, Vern7(); abstol = 1e-8, reltol = 1e-8) + osol = solve(oprob, Vern7(); abstol = 1.0e-8, reltol = 1.0e-8) @test osol[X][end] ≈ 2.0 - @test osol[A][end] ≈ 0.0 atol = 1e-8 - @test osol[D(A)][end] ≈ 0.0 atol = 1e-8 + @test osol[A][end] ≈ 0.0 atol = 1.0e-8 + @test osol[D(A)][end] ≈ 0.0 atol = 1.0e-8 @test osol[B][end] ≈ 1.0 # Checks that SteadyState simulation of the system achieves the correct steady state. ssprob = SteadyStateProblem(coupled_rs, u0, ps; mtkcompile = true) - sssol = solve(ssprob, DynamicSS(Vern7()); abstol = 1e-8, reltol = 1e-8) + sssol = solve(ssprob, DynamicSS(Vern7()); abstol = 1.0e-8, reltol = 1.0e-8) @test sssol[X][end] ≈ 2.0 - @test sssol[A][end] ≈ 0.0 atol = 1e-8 - @test sssol[D(A)][end] ≈ 0.0 atol = 1e-8 + @test sssol[A][end] ≈ 0.0 atol = 1.0e-8 + @test sssol[D(A)][end] ≈ 0.0 atol = 1.0e-8 @test sssol[B][end] ≈ 1.0 # Checks that the steady state can be found by solving a nonlinear problem. @@ -613,28 +619,28 @@ end let # Creates the model programmatically. @species X1(t) X2(t) X3(t) - @variables V(t)=5.0 [description="Volume"] N(t) X_conc(t) X_tot(t) + @variables V(t) = 5.0 [description = "Volume"] N(t) X_conc(t) X_tot(t) @parameters p k1 k2 d v n x_scale::Float32 eqs = [ Reaction(p, nothing, [X1]) Reaction(k1, [X1], [X2]) Reaction(k2, [X2], [X3]) Reaction(d, [X3], nothing) - D(V) ~ X3/(1+X3) - v*V - D(N) ~ - n*N*X3 - V*X_conc ~ x_scale*(X1 + X2 + X3) + D(V) ~ X3 / (1 + X3) - v * V + D(N) ~ - n * N * X3 + V * X_conc ~ x_scale * (X1 + X2 + X3) X_tot + X1 + X2 ~ -X3 ] rs_prog = complete(ReactionSystem(eqs, t; name = :coupled_rs)) # Creates the model via the DSL. rs_dsl = @reaction_network coupled_rs begin - @variables X_conc(t) V(t)=5.0 [description="Volume"] X_tot(t) + @variables X_conc(t) V(t) = 5.0 [description = "Volume"] X_tot(t) @parameters v n x_scale::Float32 @equations begin - D(V) ~ X3/(1+X3) - v*V - D(N) ~ - n*N*X3 - V*X_conc ~ x_scale*(X1 + X2 + X3) + D(V) ~ X3 / (1 + X3) - v * V + D(N) ~ - n * N * X3 + V * X_conc ~ x_scale * (X1 + X2 + X3) X_tot + X1 + X2 ~ -X3 end p, 0 --> X1 @@ -671,13 +677,13 @@ end let # Checks for system with a single differential equation. rs_1_line = @reaction_network rs_1 begin - @equations D(M) ~ -M*I + @equations D(M) ~ -M * I i, S + I --> 2I r, I --> R end rs_1_block = @reaction_network rs_1 begin @equations begin - D(M) ~ -M*I + D(M) ~ -M * I end i, S + I --> 2I r, I --> R @@ -709,7 +715,7 @@ let rs_1 = @reaction_network begin @variables H(t) @equations begin - D(M) ~ -M*I + D(M) ~ -M * I H ~ 100 - I end i, S + I --> 2I @@ -721,10 +727,10 @@ let # Checks for system with two differential equations, and which do not use `@variables`, rs_2 = @reaction_network coupled_rs begin @equations begin - D(V) ~ X/(1+X) - V + D(V) ~ X / (1 + X) - V D(N) ~ - V end - (p,d), 0 <--> X + (p, d), 0 <--> X end issetequal(species(rs_2), [rs_2.X]) issetequal(unknowns(rs_2)[2:3], [rs_2.V, rs_2.N]) @@ -733,10 +739,10 @@ let rs_2 = @reaction_network coupled_rs begin @variables N(t) @equations begin - D(V) ~ X/(1+X) - V + D(V) ~ X / (1 + X) - V D(N) ~ - V end - (p,d), 0 <--> X + (p, d), 0 <--> X end issetequal(species(rs_2), [rs_2.X]) issetequal(unknowns(rs_2)[2:3], [rs_2.V, rs_2.N]) @@ -746,7 +752,7 @@ end # manually, have their additional inputs properly registered. let rs = @reaction_network begin - @variables V(t)=2.0 [description = "A variable"] + @variables V(t) = 2.0 [description = "A variable"] @equations D(V) ~ -1 end @test getdefault(rs.V) == 2.0 @@ -763,37 +769,37 @@ let @parameters p q @species X(t) @variables A(t) B(t) - @equations X^2 + log(A+X) ~ 1 - sqrt(B) + sin(p + X + π)/exp(A/(1+t)) + q + @equations X^2 + log(A + X) ~ 1 - sqrt(B) + sin(p + X + π) / exp(A / (1 + t)) + q end rs_2 = @reaction_network rs begin @parameters p q @species X(t) @variables A(t) B(t) - @equations X^2 + log(A+X) + sqrt(B) - sin(p + X + π)/exp(A/(1+t)) - q ~ 1 + @equations X^2 + log(A + X) + sqrt(B) - sin(p + X + π) / exp(A / (1 + t)) - q ~ 1 end rs_3 = @reaction_network rs begin @parameters p q @species X(t) @variables A(t) B(t) - @equations X^2 + log(A+X) + sqrt(B) - sin(p + X + π)/exp(A/(1+t)) - 1 - q ~ 0 + @equations X^2 + log(A + X) + sqrt(B) - sin(p + X + π) / exp(A / (1 + t)) - 1 - q ~ 0 end rs_4 = @reaction_network rs begin @parameters p q @species X(t) @variables A(t) B(t) - @equations 0 ~ X^2 + log(A+X) + sqrt(B) - sin(p + X + π)/exp(A/(1+t)) - 1 - q + @equations 0 ~ X^2 + log(A + X) + sqrt(B) - sin(p + X + π) / exp(A / (1 + t)) - 1 - q end rs_5 = @reaction_network rs begin @parameters p q @species X(t) @variables A(t) B(t) - @equations q ~ X^2 + log(A+X) + sqrt(B) - sin(p + X + π)/exp(A/(1+t)) - 1 + @equations q ~ X^2 + log(A + X) + sqrt(B) - sin(p + X + π) / exp(A / (1 + t)) - 1 end rs_6 = @reaction_network rs begin @parameters p q @species X(t) @variables A(t) B(t) - @equations X^2 + log(A+X) + (A + B)^p ~ 1 - sqrt(B) + sin(p + X + π)/exp(A/(1+t)) + q + (A + B)^p + @equations X^2 + log(A + X) + (A + B)^p ~ 1 - sqrt(B) + sin(p + X + π) / exp(A / (1 + t)) + q + (A + B)^p end # Uses a special function to check that all equations indeed are identical. @@ -835,7 +841,7 @@ let # Declares the reaction system using the default differential and iv. rs_1 = @reaction_network begin @equations D(N) ~ -N - (p,d), 0 <--> X + (p, d), 0 <--> X end # Declares the reaction system using a new iv, and overwriting the default differential. @@ -845,7 +851,7 @@ let @variables N(τ) @differentials D = Differential(τ) @equations D(N) ~ -N - (p,d), 0 <--> X + (p, d), 0 <--> X end # Declares the reaction system using a new differential and iv. @@ -855,12 +861,12 @@ let @variables N(τ) @differentials Δ = Differential(τ) @equations Δ(N) ~ -N - (p,d), 0 <--> X + (p, d), 0 <--> X end # Simulates all three models, checking that the results are identical. u0 = [:X => 5.0, :N => 10.0] - tspan = (0.0, 10.) + tspan = (0.0, 10.0) ps = [:p => 1.0, :d => 0.2] oprob_1 = ODEProblem(rs_1, u0, tspan, ps) oprob_2 = ODEProblem(rs_2, u0, tspan, ps) @@ -914,21 +920,27 @@ let E = Differential(τ) # Variables as reaction reactants. - @test_throws Exception ReactionSystem([ - Reaction(p1, [S1], [V1]) - ], t; name = :rs) + @test_throws Exception ReactionSystem( + [ + Reaction(p1, [S1], [V1]), + ], t; name = :rs + ) # Equation with variable using non-declared independent variable. - @test_throws Exception ReactionSystem([ - Reaction(p1, [S1], [S2]), - E(U1) ~ S1 + p2 - ], t; name = :rs) + @test_throws Exception ReactionSystem( + [ + Reaction(p1, [S1], [S2]), + E(U1) ~ S1 + p2, + ], t; name = :rs + ) # Differential with respect to non-declared independent variable. - @test_throws Exception ReactionSystem([ - Reaction(p1, [S1], [S2]), - E(V1) ~ S1 + p2 - ], [t, τ]; name = :rs) + @test_throws Exception ReactionSystem( + [ + Reaction(p1, [S1], [S2]), + E(V1) ~ S1 + p2, + ], [t, τ]; name = :rs + ) end # Checks that various attempts to create `ODEProblem`s from faulty systems generate errors. @@ -941,7 +953,7 @@ let eqs = [ Reaction(p1, [S1], [S2]), V1 ~ p2 - S1, - S2 ~ V1^2 + sqrt(S2) + S2 ~ V1^2 + sqrt(S2), ] @named rs = ReactionSystem(eqs, t) rs = complete(rs) diff --git a/test/reactionsystem_core/custom_crn_functions.jl b/test/reactionsystem_core/custom_crn_functions.jl index c2714a05b4..9a9e9041c0 100644 --- a/test/reactionsystem_core/custom_crn_functions.jl +++ b/test/reactionsystem_core/custom_crn_functions.jl @@ -46,7 +46,7 @@ let v5 * (X7^2) / (K5^2 + X7^2 + Y7^2), X7 + Y7 --> Z7 end - for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2] + for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2] u0 = rnd_u0(custom_function_network_1, rng; factor) ps = rnd_ps(custom_function_network_1, rng; factor) t = rand(rng) @@ -69,33 +69,55 @@ let @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(derivative(Catalyst.hill(X, v, K, n), X), - n * v * (K^n) * (X^(n - 1)) / (K^n + X^n)^2) + @test isequal( + derivative(Catalyst.hill(X, v, K, n), X), + n * v * (K^n) * (X^(n - 1)) / (K^n + X^n)^2 + ) @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(derivative(Catalyst.hill(X, v, K, n), n), - v * (X^n) * (K^n) * (log(X) - log(K)) / (K^n + X^n)^2) + @test isequal( + derivative(Catalyst.hill(X, v, K, n), K), + -n * v * (K^(n - 1)) * (X^n) / (K^n + X^n)^2 + ) + @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(derivative(Catalyst.hillr(X, v, K, n), X), - -n * v * (K^n) * (X^(n - 1)) / (K^n + X^n)^2) + @test isequal( + derivative(Catalyst.hillr(X, v, K, n), X), + -n * v * (K^n) * (X^(n - 1)) / (K^n + X^n)^2 + ) @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(derivative(Catalyst.hillr(X, v, K, n), n), - v * (X^n) * (K^n) * (log(K) - log(X)) / (K^n + X^n)^2) - - @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(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(derivative(Catalyst.hillar(X, Y, v, K, n), v), - X^n / (K^n + X^n + Y^n)) - @test isequal(derivative(Catalyst.hillar(X, Y, v, K, n), K), - -n * v * (K^(n - 1)) * (X^n) / (K^n + X^n + Y^n)^2) - @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) + @test isequal( + derivative(Catalyst.hillr(X, v, K, n), K), + n * v * (K^(n - 1)) * (X^n) / (K^n + X^n)^2 + ) + @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( + 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( + 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( + derivative(Catalyst.hillar(X, Y, v, K, n), v), + X^n / (K^n + X^n + Y^n) + ) + @test isequal( + derivative(Catalyst.hillar(X, Y, v, K, n), K), + -n * v * (K^(n - 1)) * (X^n) / (K^n + X^n + Y^n)^2 + ) + @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 Function Expansion ### @@ -167,17 +189,17 @@ let @variables V(t) @parameters v K eqs = [ - Reaction(mm(X,v,K), [], [X]), - mm(V,v,K) ~ V + 1 + Reaction(mm(X, v, K), [], [X]), + mm(V, v, K) ~ V + 1, ] @named rs = ReactionSystem(eqs, t) # Check that `expand_registered_functions` does not mutate original model. rs_expanded_funcs = Catalyst.expand_registered_functions(rs) - @test isequal(only(Catalyst.get_rxs(rs)).rate, Catalyst.mm(X,v,K)) - @test isequal(only(Catalyst.get_rxs(rs_expanded_funcs)).rate, v*X/(X + K)) - @test isequal(last(Catalyst.get_eqs(rs)).lhs, Catalyst.mm(V,v,K)) - @test isequal(last(Catalyst.get_eqs(rs_expanded_funcs)).lhs, v*V/(V + K)) + @test isequal(only(Catalyst.get_rxs(rs)).rate, Catalyst.mm(X, v, K)) + @test isequal(only(Catalyst.get_rxs(rs_expanded_funcs)).rate, v * X / (X + K)) + @test isequal(last(Catalyst.get_eqs(rs)).lhs, Catalyst.mm(V, v, K)) + @test isequal(last(Catalyst.get_eqs(rs_expanded_funcs)).lhs, v * V / (V + K)) end # Tests on model with events. @@ -185,14 +207,14 @@ let # Creates a model, saves it, and creates an expanded version. rs = @reaction_network begin @continuous_events begin - [mm(X,v,K) ~ 1.0] => [X => X] + [mm(X, v, K) ~ 1.0] => [X => X] end @discrete_events begin - [1.0] => [X => mmr(X,v,K) + Y*(v + K)] + [1.0] => [X => mmr(X, v, K) + Y * (v + K)] 1.0 => [X => X] - (hill(X,v,K,n) > 1000.0) => [X => hillr(X,v,K,n) + 2] + (hill(X, v, K, n) > 1000.0) => [X => hillr(X, v, K, n) + 2] end - v0 + hillar(X,Y,v,K,n), X --> Y + v0 + hillar(X, Y, v, K, n), X --> Y end rs_saved = deepcopy(rs) rs_expanded = Catalyst.expand_registered_functions(rs) @@ -205,10 +227,10 @@ let # Checks that the new system is expanded. @unpack v0, X, Y, v, K, n = rs continuous_events = [ - [v*X/(X + K) ~ 1.0] => [X ~ Pre(X)] + [v * X / (X + K) ~ 1.0] => [X ~ Pre(X)], ] discrete_events = [ - [1.0] => [X ~ v*K/(Pre(X) + K) + Pre(Y)*(v + K)] + [1.0] => [X ~ v * K / (Pre(X) + K) + Pre(Y) * (v + K)] 1.0 => [X ~ Pre(X)] (v * (X^n) / (X^n + K^n) > 1000.0) => [X ~ v * (K^n) / (Pre(X)^n + K^n) + 2] ] @@ -230,16 +252,18 @@ let [X ~ mm(X, v, K)]; # affect uses registered fn affect_neg = [X ~ hill(X, v, K, n)] # affect_neg uses DIFFERENT registered fn ) - rs = ReactionSystem([Reaction(mm(X, v, K), [X], [Y])], t; - continuous_events = [ce], name = :test_affect_neg) + rs = ReactionSystem( + [Reaction(mm(X, v, K), [X], [Y])], t; + continuous_events = [ce], name = :test_affect_neg + ) rs_exp = Catalyst.expand_registered_functions(rs) ce_exp = get_continuous_events(rs_exp)[1] # Verify conditions expanded - @test isequal(ce_exp.conditions, [v*X/(X + K) ~ 1.0]) + @test isequal(ce_exp.conditions, [v * X / (X + K) ~ 1.0]) # Verify affect expanded (mm → v*X/(X+K)) - @test isequal(ce_exp.affect.affect, [X ~ v*X/(X + K)]) + @test isequal(ce_exp.affect.affect, [X ~ v * X / (X + K)]) # Verify affect_neg expanded (hill → v*X^n/(X^n+K^n)) - @test isequal(ce_exp.affect_neg.affect, [X ~ v*(X^n)/(X^n + K^n)]) + @test isequal(ce_exp.affect_neg.affect, [X ~ v * (X^n) / (X^n + K^n)]) # 2. Continuous event with discrete_parameters preserved after expansion. @discretes α(t) = 1.0 @@ -247,11 +271,13 @@ let [mm(X, v, K) ~ 1.0] => [α ~ 0]; discrete_parameters = [α] ) - rs_dp = ReactionSystem([Reaction(v, [X], [Y])], t; - continuous_events = [ce_dp], name = :test_disc_ps_cont) + rs_dp = ReactionSystem( + [Reaction(v, [X], [Y])], t; + continuous_events = [ce_dp], name = :test_disc_ps_cont + ) rs_dp_exp = Catalyst.expand_registered_functions(rs_dp) ce_dp_exp = get_continuous_events(rs_dp_exp)[1] - @test isequal(ce_dp_exp.conditions, [v*X/(X + K) ~ 1.0]) + @test isequal(ce_dp_exp.conditions, [v * X / (X + K) ~ 1.0]) @test issetequal(ce_dp_exp.affect.discrete_parameters, [α]) # 3. Discrete event with discrete_parameters preserved and affect expanded. @@ -259,11 +285,13 @@ let 2.0 => [α ~ mm(X, v, K)]; discrete_parameters = [α] ) - rs_de = ReactionSystem([Reaction(v, [X], [Y])], t; - discrete_events = [de_dp], name = :test_disc_ps_disc) + rs_de = ReactionSystem( + [Reaction(v, [X], [Y])], t; + discrete_events = [de_dp], name = :test_disc_ps_disc + ) rs_de_exp = Catalyst.expand_registered_functions(rs_de) de_dp_exp = get_discrete_events(rs_de_exp)[1] - @test isequal(de_dp_exp.affect.affect, [α ~ v*X/(X + K)]) + @test isequal(de_dp_exp.affect.affect, [α ~ v * X / (X + K)]) @test issetequal(de_dp_exp.affect.discrete_parameters, [α]) end @@ -280,72 +308,84 @@ let D = default_time_deriv() @unpack X, v, K, n, Y, Z = rn osyseqs = equations(osys) - eqs = [D(X) ~ -hill(X, v, K, n)*X*Y - mm(X,v,K)*X*Y - hillr(X,v,K,n)*X*Y - mmr(X,v,K)*X*Y, - D(Y) ~ -hill(X, v, K, n)*X*Y - mm(X,v,K)*X*Y - hillr(X,v,K,n)*X*Y - mmr(X,v,K)*X*Y, - D(Z) ~ hill(X, v, K, n)*X*Y + mm(X,v,K)*X*Y + hillr(X,v,K,n)*X*Y + mmr(X,v,K)*X*Y] + eqs = [ + D(X) ~ -hill(X, v, K, n) * X * Y - mm(X, v, K) * X * Y - hillr(X, v, K, n) * X * Y - mmr(X, v, K) * X * Y, + D(Y) ~ -hill(X, v, K, n) * X * Y - mm(X, v, K) * X * Y - hillr(X, v, K, n) * X * Y - mmr(X, v, K) * X * Y, + D(Z) ~ hill(X, v, K, n) * X * Y + mm(X, v, K) * X * Y + hillr(X, v, K, n) * X * Y + mmr(X, v, K) * X * Y, + ] reorder = [findfirst(eq -> isequal(eq.lhs, osyseq.lhs), eqs) for osyseq in osyseqs] - for (osysidx,eqidx) in enumerate(reorder) + for (osysidx, eqidx) in enumerate(reorder) @test _iszero(simplify(eqs[eqidx].rhs - osyseqs[osysidx].rhs)) end osys2 = complete(ode_model(rn)) hill2(x, v, k, n) = v * x^n / (k^n + x^n) - mm2(X,v,K) = v*X / (X + K) - mmr2(X,v,K) = v*K / (X + K) - hillr2(X,v,K,n) = v * (K^n) / (X^n + K^n) - eqs2 = [D(X) ~ -hill2(X, v, K, n)*X*Y - mm2(X,v,K)*X*Y - hillr2(X,v,K,n)*X*Y - mmr2(X,v,K)*X*Y, - D(Y) ~ -hill2(X, v, K, n)*X*Y - mm2(X,v,K)*X*Y - hillr2(X,v,K,n)*X*Y - mmr2(X,v,K)*X*Y, - D(Z) ~ hill2(X, v, K, n)*X*Y + mm2(X,v,K)*X*Y + hillr2(X,v,K,n)*X*Y + mmr2(X,v,K)*X*Y] + mm2(X, v, K) = v * X / (X + K) + mmr2(X, v, K) = v * K / (X + K) + hillr2(X, v, K, n) = v * (K^n) / (X^n + K^n) + eqs2 = [ + D(X) ~ -hill2(X, v, K, n) * X * Y - mm2(X, v, K) * X * Y - hillr2(X, v, K, n) * X * Y - mmr2(X, v, K) * X * Y, + D(Y) ~ -hill2(X, v, K, n) * X * Y - mm2(X, v, K) * X * Y - hillr2(X, v, K, n) * X * Y - mmr2(X, v, K) * X * Y, + D(Z) ~ hill2(X, v, K, n) * X * Y + mm2(X, v, K) * X * Y + hillr2(X, v, K, n) * X * Y + mmr2(X, v, K) * X * Y, + ] osyseqs2 = equations(osys2) reorder = [findfirst(eq -> isequal(eq.lhs, osyseq.lhs), eqs2) for osyseq in osyseqs2] - for (osysidx,eqidx) in enumerate(reorder) + for (osysidx, eqidx) in enumerate(reorder) @test _iszero(simplify(eqs2[eqidx].rhs - osyseqs2[osysidx].rhs)) end nlsys = complete(ss_ode_model(rn; expand_catalyst_funs = false)) nlsyseqs = equations(nlsys) - eqs = [0 ~ -hill(X, v, K, n)*X*Y - mm(X,v,K)*X*Y - hillr(X,v,K,n)*X*Y - mmr(X,v,K)*X*Y, - 0 ~ -hill(X, v, K, n)*X*Y - mm(X,v,K)*X*Y - hillr(X,v,K,n)*X*Y - mmr(X,v,K)*X*Y, - 0 ~ hill(X, v, K, n)*X*Y + mm(X,v,K)*X*Y + hillr(X,v,K,n)*X*Y + mmr(X,v,K)*X*Y] + eqs = [ + 0 ~ -hill(X, v, K, n) * X * Y - mm(X, v, K) * X * Y - hillr(X, v, K, n) * X * Y - mmr(X, v, K) * X * Y, + 0 ~ -hill(X, v, K, n) * X * Y - mm(X, v, K) * X * Y - hillr(X, v, K, n) * X * Y - mmr(X, v, K) * X * Y, + 0 ~ hill(X, v, K, n) * X * Y + mm(X, v, K) * X * Y + hillr(X, v, K, n) * X * Y + mmr(X, v, K) * X * Y, + ] for (i, eq) in enumerate(eqs) @test _iszero(simplify(eq.rhs - nlsyseqs[i].rhs)) end nlsys2 = complete(ss_ode_model(rn)) nlsyseqs2 = equations(nlsys2) - eqs2 = [0 ~ -hill2(X, v, K, n)*X*Y - mm2(X,v,K)*X*Y - hillr2(X,v,K,n)*X*Y - mmr2(X,v,K)*X*Y, - 0 ~ -hill2(X, v, K, n)*X*Y - mm2(X,v,K)*X*Y - hillr2(X,v,K,n)*X*Y - mmr2(X,v,K)*X*Y, - 0 ~ hill2(X, v, K, n)*X*Y + mm2(X,v,K)*X*Y + hillr2(X,v,K,n)*X*Y + mmr2(X,v,K)*X*Y] + eqs2 = [ + 0 ~ -hill2(X, v, K, n) * X * Y - mm2(X, v, K) * X * Y - hillr2(X, v, K, n) * X * Y - mmr2(X, v, K) * X * Y, + 0 ~ -hill2(X, v, K, n) * X * Y - mm2(X, v, K) * X * Y - hillr2(X, v, K, n) * X * Y - mmr2(X, v, K) * X * Y, + 0 ~ hill2(X, v, K, n) * X * Y + mm2(X, v, K) * X * Y + hillr2(X, v, K, n) * X * Y + mmr2(X, v, K) * X * Y, + ] for (i, eq) in enumerate(eqs2) @test _iszero(simplify(eq.rhs - nlsyseqs2[i].rhs)) end sdesys = complete(sde_model(rn; expand_catalyst_funs = false)) sdesyseqs = equations(sdesys) - eqs = [D(X) ~ -hill(X, v, K, n)*X*Y - mm(X,v,K)*X*Y - hillr(X,v,K,n)*X*Y - mmr(X,v,K)*X*Y, - D(Y) ~ -hill(X, v, K, n)*X*Y - mm(X,v,K)*X*Y - hillr(X,v,K,n)*X*Y - mmr(X,v,K)*X*Y, - D(Z) ~ hill(X, v, K, n)*X*Y + mm(X,v,K)*X*Y + hillr(X,v,K,n)*X*Y + mmr(X,v,K)*X*Y] + eqs = [ + D(X) ~ -hill(X, v, K, n) * X * Y - mm(X, v, K) * X * Y - hillr(X, v, K, n) * X * Y - mmr(X, v, K) * X * Y, + D(Y) ~ -hill(X, v, K, n) * X * Y - mm(X, v, K) * X * Y - hillr(X, v, K, n) * X * Y - mmr(X, v, K) * X * Y, + D(Z) ~ hill(X, v, K, n) * X * Y + mm(X, v, K) * X * Y + hillr(X, v, K, n) * X * Y + mmr(X, v, K) * X * Y, + ] reorder = [findfirst(eq -> isequal(eq.lhs, sdesyseq.lhs), eqs) for sdesyseq in sdesyseqs] - for (sdesysidx,eqidx) in enumerate(reorder) + for (sdesysidx, eqidx) in enumerate(reorder) @test _iszero(simplify(eqs[eqidx].rhs - sdesyseqs[sdesysidx].rhs)) end sdesysnoiseeqs = ModelingToolkitBase.get_noise_eqs(sdesys) - neqvec = diagm(sqrt.(abs.([hill(X, v, K, n)*X*Y, mm(X,v,K)*X*Y, hillr(X,v,K,n)*X*Y, mmr(X,v,K)*X*Y]))) + neqvec = diagm(sqrt.(abs.([hill(X, v, K, n) * X * Y, mm(X, v, K) * X * Y, hillr(X, v, K, n) * X * Y, mmr(X, v, K) * X * Y]))) neqmat = [-1 -1 -1 -1; -1 -1 -1 -1; 1 1 1 1] neqmat *= neqvec @test all(_iszero, simplify.(sdesysnoiseeqs .- neqmat)) sdesys = complete(sde_model(rn)) sdesyseqs = equations(sdesys) - eqs = [D(X) ~ -hill2(X, v, K, n)*X*Y - mm2(X,v,K)*X*Y - hillr2(X,v,K,n)*X*Y - mmr2(X,v,K)*X*Y, - D(Y) ~ -hill2(X, v, K, n)*X*Y - mm2(X,v,K)*X*Y - hillr2(X,v,K,n)*X*Y - mmr2(X,v,K)*X*Y, - D(Z) ~ hill2(X, v, K, n)*X*Y + mm2(X,v,K)*X*Y + hillr2(X,v,K,n)*X*Y + mmr2(X,v,K)*X*Y] + eqs = [ + D(X) ~ -hill2(X, v, K, n) * X * Y - mm2(X, v, K) * X * Y - hillr2(X, v, K, n) * X * Y - mmr2(X, v, K) * X * Y, + D(Y) ~ -hill2(X, v, K, n) * X * Y - mm2(X, v, K) * X * Y - hillr2(X, v, K, n) * X * Y - mmr2(X, v, K) * X * Y, + D(Z) ~ hill2(X, v, K, n) * X * Y + mm2(X, v, K) * X * Y + hillr2(X, v, K, n) * X * Y + mmr2(X, v, K) * X * Y, + ] reorder = [findfirst(eq -> isequal(eq.lhs, sdesyseq.lhs), eqs) for sdesyseq in sdesyseqs] - for (sdesysidx,eqidx) in enumerate(reorder) + for (sdesysidx, eqidx) in enumerate(reorder) @test _iszero(simplify(eqs[eqidx].rhs - sdesyseqs[sdesysidx].rhs)) end sdesysnoiseeqs = ModelingToolkitBase.get_noise_eqs(sdesys) - neqvec = diagm(sqrt.(abs.([hill2(X, v, K, n)*X*Y, mm2(X,v,K)*X*Y, hillr2(X,v,K,n)*X*Y, mmr2(X,v,K)*X*Y]))) + neqvec = diagm(sqrt.(abs.([hill2(X, v, K, n) * X * Y, mm2(X, v, K) * X * Y, hillr2(X, v, K, n) * X * Y, mmr2(X, v, K) * X * Y]))) neqmat = [-1 -1 -1 -1; -1 -1 -1 -1; 1 1 1 1] neqmat *= neqvec @test all(_iszero, simplify.(sdesysnoiseeqs .- neqmat)) @@ -355,7 +395,7 @@ let jsysjumps = MT.jumps(jsys) rates = getfield.(jsysjumps, :rate) affects = getfield.(jsysjumps, :affect!) - reqs = [Y*X*hill(X, v, K, n), Y*X*mm(X, v, K), hillr(X, v, K, n)*Y*X, Y*X*mmr(X, v, K)] + reqs = [Y * X * hill(X, v, K, n), Y * X * mm(X, v, K), hillr(X, v, K, n) * Y * X, Y * X * mmr(X, v, K)] affeqs = [Z ~ 1 + Pre(Z), Y ~ -1 + Pre(Y), X ~ -1 + Pre(X)] @test all(_iszero, simplify.(rates .- reqs)) @test all(aff -> issetequal(aff, affeqs), affects) @@ -365,7 +405,7 @@ let jsysjumps = MT.jumps(jsys) rates = getfield.(jsysjumps, :rate) affects = getfield.(jsysjumps, :affect!) - reqs = [Y*X*hill2(X, v, K, n), Y*X*mm2(X, v, K), hillr2(X, v, K, n)*Y*X, Y*X*mmr2(X, v, K)] + reqs = [Y * X * hill2(X, v, K, n), Y * X * mm2(X, v, K), hillr2(X, v, K, n) * Y * X, Y * X * mmr2(X, v, K)] affeqs = [Z ~ 1 + Pre(Z), Y ~ -1 + Pre(Y), X ~ -1 + Pre(X)] @test all(_iszero, simplify.(rates .- reqs)) @test all(aff -> issetequal(aff, affeqs), affects) diff --git a/test/reactionsystem_core/events.jl b/test/reactionsystem_core/events.jl index a0b05d644c..29f35d6bf0 100644 --- a/test/reactionsystem_core/events.jl +++ b/test/reactionsystem_core/events.jl @@ -19,7 +19,7 @@ D = default_time_deriv() # Test discrete event is propagated to ODE solver correctly. let # Creates model (essentially a jagged oscillation, where `V` is reset to 1.0 every 1.0 time units). - @variables V(t)=1.0 + @variables V(t) = 1.0 eqs = [D(V) ~ V] discrete_events = [1.0 => [V ~ 1.0]] rxs = [ @@ -36,17 +36,17 @@ let @test length(ModelingToolkitBase.discrete_events(osys)) == 1 oprob = ODEProblem(osys, [osys.A => 0.0], (0.0, 20.0)) sol = solve(oprob, Tsit5()) - @test 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 (a production/degradation system, but both reactions stop at `t=2.5`). - @discretes α(t)=5.0 β(t)=1.0 - @species V(t)=0.0 + @discretes α(t) = 5.0 β(t) = 1.0 + @species V(t) = 0.0 rxs = [ Reaction(α, nothing, [V]), - Reaction(β, [V], nothing) + Reaction(β, [V], nothing), ] continuous_events = SymbolicContinuousCallback([V ~ 2.5] => [α ~ 0, β ~ 0]; discrete_parameters = [α, β]) @named rs = ReactionSystem(rxs, t; continuous_events) @@ -68,10 +68,10 @@ end let # Creates model. @parameters p d α::Int64 = 1 - @species X(t) A(t) = 2 [description="A species"] a(t) = 3 + @species X(t) A(t) = 2 [description = "A species"] a(t) = 3 rxs = [ Reaction(p, nothing, [X]), - Reaction(d, [X], nothing) + Reaction(d, [X], nothing), ] continuous_events = [α ~ t] => [A ~ Pre(A + a)] discrete_events = [2.0 => [A ~ Pre(α + a)]] @@ -134,7 +134,7 @@ let @species X(t) rxs = [ Reaction(p, nothing, [X]), - Reaction(d, [X], nothing) + Reaction(d, [X], nothing), ] ce = [X ~ 1.0] => [X ~ 0.5] de = [2.0] => [p ~ 1.0] @@ -153,11 +153,11 @@ let @species X(t) rxs = [ Reaction(p, nothing, [X]), - Reaction(d, [X], nothing) + Reaction(d, [X], nothing), ] # Declares various misformatted events . - @test_broken false # Some miss-formatted events don't yield errors, but should (https://github.com/SciML/ModelingToolkit.jl/issues/4167). These are commented out. + @test_broken false # Some miss-formatted events don't yield errors, but should (https://github.com/SciML/ModelingToolkit.jl/issues/4167). These are commented out. continuous_events_bad = [ X ~ 1.0 => [X ~ 0.5], # Scalar condition. [X ~ 1.0] => X ~ 0.5, # Scalar affect. @@ -170,7 +170,7 @@ let [2.0] => p ~ 1.0, # Scalar affect. #[2.0] => (p ~ 1.0, ), # Tuple affect. # Should not work, potentially bad for performance as compared to vectors (https://github.com/SciML/ModelingToolkit.jl/issues/4167). [X > 2.0] => [p ~ 1.0], # Vector conditions. - (1.0, 2.0) => [p ~ 1.0] # Tuple condition. + (1.0, 2.0) => [p ~ 1.0], # Tuple condition. ] # Checks that errors are produced. @@ -192,7 +192,7 @@ end # Tests event affecting non-species components. let rn_dsl = @reaction_network rn begin - @parameters thres=7.0 dY_up + @parameters thres = 7.0 dY_up @continuous_events begin [t ~ 2.5] => [p => p + 0.2] [X ~ thres, Y ~ X] => [X => X - 0.5, Z => Z + 0.1] @@ -204,21 +204,21 @@ let end (p, dX), 0 <--> X - (1.1*p, dY), 0 <--> Y + (1.1 * p, dY), 0 <--> Y d, Z --> 0 end # Creates model programmatically. t = default_t() @species X(t) Y(t) Z(t) - @parameters thres=7.0 dY_up d + @parameters thres = 7.0 dY_up d @discretes p(t) dX(t) dY(t) rxs = [ Reaction(p, nothing, [X], nothing, [1]), Reaction(dX, [X], nothing, [1], nothing), - Reaction(1.1*p, nothing, [Y], nothing, [1]), + Reaction(1.1 * p, nothing, [Y], nothing, [1]), Reaction(dY, [Y], nothing, [1], nothing), - Reaction(d, [Z], nothing, [1], nothing) + Reaction(d, [Z], nothing, [1], nothing), ] continuous_events = [ SymbolicContinuousCallback([t ~ 2.5] => [p ~ Pre(p) + 0.2]; discrete_parameters = [p]) @@ -305,7 +305,7 @@ let # Tuple affect (discrete events). @test_throws Exception @eval @reaction_network begin @species X(t) - @discrete_events 1.0 => (X => X + 1, ) + @discrete_events 1.0 => (X => X + 1,) end # Equation condition (discrete events). @@ -323,17 +323,17 @@ end let # Creates model with all types of events. The `e` parameters track whether events are triggered. rn = @reaction_network begin - @discretes e1(t)=0 e2(t)=0 e3(t)=0 e4(t)=0 + @discretes e1(t) = 0 e2(t) = 0 e3(t) = 0 e4(t) = 0 @continuous_events begin [X ~ 1000.0] => [e1 => 1] end @discrete_events begin [1.0] => [e2 => 1] 1.0 => [e3 => 1] - (Y > 1000.0) & (e4==0) => [e4 => 1] + (Y > 1000.0) & (e4 == 0) => [e4 => 1] end - (p,d), 0 <--> X - (p,d), 0 <--> Y + (p, d), 0 <--> X + (p, d), 0 <--> Y end # Simulates the model for conditions where it *definitely* will cross `X = 1000.0` @@ -354,13 +354,13 @@ end let # Creates model with all types of events. The `e` parameters track whether events are triggered. rn = @reaction_network begin - @discretes e1(t)=0 e2(t)=0 e3(t)=0 + @discretes e1(t) = 0 e2(t) = 0 e3(t) = 0 @discrete_events begin [1.0] => [e1 => 1] 1.0 => [e2 => 1] - (X > 1000.0) & (e3==0) => [e3 => 1] + (X > 1000.0) & (e3 == 0) => [e3 => 1] end - (p,d), 0 <--> X + (p, d), 0 <--> X end # Simulates the model for conditions where it *definitely* will cross `X = 1000.0` @@ -384,8 +384,8 @@ let rn = @reaction_network begin @default_noise_scaling 0.0 @parameters add::Int64 - (p,d), 0 <--> X - (p,d), 0 <--> Y + (p, d), 0 <--> X + (p, d), 0 <--> Y end rn_events = @reaction_network begin @default_noise_scaling 0.0 @@ -398,8 +398,8 @@ let 20.0 => [X => X + add] (Y < X) => [Y => Y + add] end - (p,d), 0 <--> X - (p,d), 0 <--> Y + (p, d), 0 <--> X + (p, d), 0 <--> Y end rn_dics_events = @reaction_network begin @parameters add::Int64 @@ -408,8 +408,8 @@ let 20.0 => [X => X + add] (Y < X) => [Y => Y + add] end - (p,d), 0 <--> X - (p,d), 0 <--> Y + (p, d), 0 <--> X + (p, d), 0 <--> Y end # Sets simulation inputs. @@ -419,9 +419,9 @@ let # Create callbacks cb_cont = ContinuousCallback((u, t, int) -> (int[:X] - 90.0), int -> (int[:X] += 10.0)) - cb_disc_1 = PresetTimeCallback([5.0, 10.0], int -> (int[:X] += int.ps[:add]; int[:Y] += int.ps[:add];)) + cb_disc_1 = PresetTimeCallback([5.0, 10.0], int -> (int[:X] += int.ps[:add]; int[:Y] += int.ps[:add])) cb_disc_2 = PresetTimeCallback(20.0:20.0:tspan[end], int -> (int[:X] += int.ps[:add])) - cb_disc_3 = DiscreteCallback((u,t,i) -> i[:Y] < i[:X], int -> (int[:Y] += int.ps[:add])) + cb_disc_3 = DiscreteCallback((u, t, i) -> i[:Y] < i[:X], int -> (int[:Y] += int.ps[:add])) callback = CallbackSet(cb_cont, cb_disc_1, cb_disc_2, cb_disc_3) # Checks for ODE simulations. @@ -456,7 +456,7 @@ let @species X(t) A(t) a(t) rxs = [ Reaction(p, nothing, [X]), - Reaction(d, [X], nothing) + Reaction(d, [X], nothing), ] continuous_events = [α ~ t] => [A ~ A + a] @named rs = ReactionSystem(rxs, t; continuous_events) @@ -606,15 +606,19 @@ end # tstops field is needed to ensure the solver steps to exactly t_switch. # Note: SDE/Jump/Hybrid solvers also support SymbolicTstops (tested separately below). let - @variables V(t)=10.0 - @parameters t_switch=3.0 + @variables V(t) = 10.0 + @parameters t_switch = 3.0 D = default_time_deriv() eqs = [D(V) ~ -V] # Generic symbolic condition — NOT a numeric PresetTimeCallback. # The tstop forces the solver to step to exactly t_switch so the equality holds. discrete_events = [(t == t_switch) => [V ~ 100.0]] - rs = complete(ReactionSystem(eqs, t; discrete_events, - tstops = [t_switch], name = :rs)) + rs = complete( + ReactionSystem( + eqs, t; discrete_events, + tstops = [t_switch], name = :rs + ) + ) osys = complete(ode_model(rs)) oprob = ODEProblem(osys, [], (0.0, 8.0)) @@ -622,7 +626,7 @@ let # Without the tstop the solver would likely step over t_switch=3.0; # with it, V is reset to 100.0 at exactly that time. - @test sol(3.0 + 0.01, idxs = V) ≈ 100.0 atol=1.0 + @test sol(3.0 + 0.01, idxs = V) ≈ 100.0 atol = 1.0 # Verify the symbolic tstop was forwarded to the System. @test issetequal(ModelingToolkitBase.get_tstops(osys), [t_switch]) @@ -631,7 +635,7 @@ end # Tests that symbolic tstops are forwarded through sde_model to the converted System. let rn = @reaction_network begin - @parameters t_event=3.0 + @parameters t_event = 3.0 @tstops begin t_event 2 * t_event @@ -639,14 +643,16 @@ let (10.0, 0.01), 0 <--> X end ssys = sde_model(rn) - @test issetequal(ModelingToolkitBase.get_tstops(ssys), - ModelingToolkitBase.get_tstops(rn)) + @test issetequal( + ModelingToolkitBase.get_tstops(ssys), + ModelingToolkitBase.get_tstops(rn) + ) end # Tests that symbolic tstops are forwarded through jump_model to the converted System. let rn = @reaction_network begin - @parameters t_event=2.0 + @parameters t_event = 2.0 @tstops begin t_event 2 * t_event @@ -654,28 +660,32 @@ let (10.0, 0.01), 0 <--> X end jsys = jump_model(rn) - @test issetequal(ModelingToolkitBase.get_tstops(jsys), - ModelingToolkitBase.get_tstops(rn)) + @test issetequal( + ModelingToolkitBase.get_tstops(jsys), + ModelingToolkitBase.get_tstops(rn) + ) end # Tests that symbolic tstops are forwarded through hybrid_model to the converted System. let rn = @reaction_network begin - @parameters t_event=4.0 + @parameters t_event = 4.0 @tstops t_event (10.0, 0.01), 0 <--> X 1.0, X --> 0, [physical_scale = Catalyst.PhysicalScale.Jump] end hsys = hybrid_model(rn; default_scale = Catalyst.PhysicalScale.ODE) - @test issetequal(ModelingToolkitBase.get_tstops(hsys), - ModelingToolkitBase.get_tstops(rn)) + @test issetequal( + ModelingToolkitBase.get_tstops(hsys), + ModelingToolkitBase.get_tstops(rn) + ) end # Integration test: solve JumpProblem with symbolic tstops and a discrete event. # The discrete event at t_event adds a large number of molecules, verifiable in the solution. let rn = @reaction_network begin - @parameters t_event=3.0 + @parameters t_event = 3.0 @tstops t_event @discrete_events (t == t_event) => [X => X + 10000] (10.0, 0.01), 0 <--> X @@ -693,7 +703,7 @@ end # Integration test: solve SDEProblem with symbolic tstops and a discrete event. let rn = @reaction_network begin - @parameters t_event=3.0 + @parameters t_event = 3.0 @tstops t_event @discrete_events (t == t_event) => [X => X + 10000.0] (10.0, 0.01), 0 <--> X @@ -711,7 +721,7 @@ end # Uses ODE-scale production/degradation + a Jump-scale degradation to ensure a true hybrid. let rn = @reaction_network begin - @parameters t_event=3.0 + @parameters t_event = 3.0 @tstops t_event @discrete_events (t == t_event) => [X => X + 10000.0] 10.0, 0 --> X, [physical_scale = Catalyst.PhysicalScale.ODE] @@ -738,13 +748,17 @@ let # Species in tstops (5-arg constructor). @test_throws ArgumentError ReactionSystem(rxs, t, [X], [k]; tstops = [X], name = :rs) # Variable in tstops (5-arg constructor). - @test_throws ArgumentError ReactionSystem([rxs; D(V) ~ -V], t, [X, V], [k]; - tstops = [V], name = :rs) + @test_throws ArgumentError ReactionSystem( + [rxs; D(V) ~ -V], t, [X, V], [k]; + tstops = [V], name = :rs + ) # Species in tstops (short-form constructor). @test_throws ArgumentError ReactionSystem(rxs, t; tstops = [X], name = :rs) # Variable in tstops (short-form constructor). - @test_throws ArgumentError ReactionSystem([rxs; D(V) ~ -V], t; - tstops = [V], name = :rs) + @test_throws ArgumentError ReactionSystem( + [rxs; D(V) ~ -V], t; + tstops = [V], name = :rs + ) # Species/variable in tstops via the DSL. @test_throws ArgumentError @eval @reaction_network begin @tstops X diff --git a/test/reactionsystem_core/functional_parameters.jl b/test/reactionsystem_core/functional_parameters.jl index 8099df248e..b99f5a0dec 100644 --- a/test/reactionsystem_core/functional_parameters.jl +++ b/test/reactionsystem_core/functional_parameters.jl @@ -1,4 +1,3 @@ - # Fetch packages. using Catalyst, DataInterpolations, JumpProcesses, OrdinaryDiffEqDefault, OrdinaryDiffEqTsit5, StochasticDiffEq, Test @@ -14,17 +13,17 @@ let # Defines an input process (modified sinus wave). tend = 5.0 ts = collect(0.0:0.0001:tend) - A = 2.0; f = 1.6; ϕ = 0.5; - spline = LinearInterpolation(A .* (sin.(2π * f .* ts .- ϕ) .+ 1) /2, ts) + A = 2.0; f = 1.6; ϕ = 0.5 + spline = LinearInterpolation(A .* (sin.(2π * f .* ts .- ϕ) .+ 1) / 2, ts) @parameters (pIn::typeof(spline))(..) # Defines a `ReactionSystem` without the input parameter. @species X(t) Y1(t) Y2(t) @parameters p d k1 k2 rxs_base = [ - Reaction(A * (sin(2π * f * t .- ϕ) + 1) /2, [], [X]) + Reaction(A * (sin(2π * f * t .- ϕ) + 1) / 2, [], [X]) Reaction(d, [X], []) - Reaction(k1*X, [Y1], [Y2]) + Reaction(k1 * X, [Y1], [Y2]) Reaction(k2, [Y2], [Y1]) ] @named rs_base = ReactionSystem(rxs_base, t) @@ -34,7 +33,7 @@ let rxs_pIn = [ Reaction(pIn(t), [], [X]) Reaction(d, [X], []) - Reaction(k1*X, [Y1], [Y2]) + Reaction(k1 * X, [Y1], [Y2]) Reaction(k2, [Y2], [Y1]) ] @named rs_pIn = ReactionSystem(rxs_pIn, t) @@ -42,8 +41,8 @@ let # Defines a `ReactionSystem` with the input parameter (DSL). rs_pIn_dsl = @reaction_network rs_pIn begin - ($pIn(t),d), 0 <--> X - (k1*X,k2), Y1 <--> Y2 + ($pIn(t), d), 0 <--> X + (k1 * X, k2), Y1 <--> Y2 end # Checks that the two model declarations are equivalent. @@ -56,13 +55,13 @@ let oprob_base = ODEProblem(rs_base, u0, tend, ps_base) oprob_prog = ODEProblem(rs_pIn, u0, tend, ps) oprob_dsl = ODEProblem(rs_pIn_dsl, u0, tend, ps) - sol_base = solve(oprob_base; saveat = 0.1, abstol = 1e-8, reltol = 1e-8) - sol_prog = solve(oprob_prog; saveat = 0.1, abstol = 1e-8, reltol = 1e-8) - sol_dsl = solve(oprob_dsl; saveat = 0.1, abstol = 1e-8, reltol = 1e-8) + sol_base = solve(oprob_base; saveat = 0.1, abstol = 1.0e-8, reltol = 1.0e-8) + sol_prog = solve(oprob_prog; saveat = 0.1, abstol = 1.0e-8, reltol = 1.0e-8) + sol_dsl = solve(oprob_dsl; saveat = 0.1, abstol = 1.0e-8, reltol = 1.0e-8) # Checks that simulations are identical. - @test sol_base[X] ≈ sol_prog[X] atol = 1e-8 rtol = 1e-8 - @test sol_base[X] ≈ sol_dsl[X] atol = 1e-8 rtol = 1e-8 + @test sol_base[X] ≈ sol_prog[X] atol = 1.0e-8 rtol = 1.0e-8 + @test sol_base[X] ≈ sol_dsl[X] atol = 1.0e-8 rtol = 1.0e-8 end # Checks a simple model for different problem types. @@ -77,14 +76,18 @@ let # Defines the `ReactionSystem` with/without the temporal parameter. @species X(t) @parameters d - @named rs_base = ReactionSystem([ - Reaction((10 + t) / (2 + t), [], [X]), - Reaction(d, [X], []) - ]) - @named rs_pIn = ReactionSystem([ - Reaction(pIn(t), [], [X]), - Reaction(d, [X], []) - ]) + @named rs_base = ReactionSystem( + [ + Reaction((10 + t) / (2 + t), [], [X]), + Reaction(d, [X], []), + ] + ) + @named rs_pIn = ReactionSystem( + [ + Reaction(pIn(t), [], [X]), + Reaction(d, [X], []), + ] + ) rs_base = complete(rs_base) rs_pIn = complete(rs_pIn) @@ -94,8 +97,8 @@ let ps = [ps_base; pIn => spline] # Checks ODE simulations. - ode_sol_base = solve(ODEProblem(rs_base, u0, tend, ps_base); reltol = 1e-8, abstol = 1e-8, saveat = 0.1) - ode_sol_pIn = solve(ODEProblem(rs_pIn, u0, tend, ps); reltol = 1e-8, abstol = 1e-8, saveat = 0.1) + ode_sol_base = solve(ODEProblem(rs_base, u0, tend, ps_base); reltol = 1.0e-8, abstol = 1.0e-8, saveat = 0.1) + ode_sol_pIn = solve(ODEProblem(rs_pIn, u0, tend, ps); reltol = 1.0e-8, abstol = 1.0e-8, saveat = 0.1) @test ode_sol_base ≈ ode_sol_pIn # Checks SDE simulations. @@ -103,14 +106,14 @@ let esde_prob_pIn = EnsembleProblem(SDEProblem(rs_pIn, u0, tend, ps)) sde_sol_mean_base = EnsembleAnalysis.timeseries_point_mean(solve(esde_prob_base, ImplicitEM(); trajectories = 1000), 1.0:10.0).u sde_sol_mean_pIn = EnsembleAnalysis.timeseries_point_mean(solve(esde_prob_pIn, ImplicitEM(); trajectories = 1000), 1.0:10.0).u - @test sde_sol_mean_base ≈ sde_sol_mean_pIn rtol = 1e-1 atol = 1e-1 + @test sde_sol_mean_base ≈ sde_sol_mean_pIn rtol = 1.0e-1 atol = 1.0e-1 # Checks Jump simulations (uses Tsit5 because model has time-dependent rates -> VariableRateJumps). ejmp_prob_base = EnsembleProblem(JumpProblem(rs_base, u0, (0.0, tend), ps_base)) ejmp_prob_pIn = EnsembleProblem(JumpProblem(rs_pIn, u0, (0.0, tend), ps)) jmp_sol_mean_base = EnsembleAnalysis.timeseries_point_mean(solve(ejmp_prob_base, Tsit5(); trajectories = 1000), 1.0:10.0).u jmp_sol_mean_pIn = EnsembleAnalysis.timeseries_point_mean(solve(ejmp_prob_pIn, Tsit5(); trajectories = 1000), 1.0:10.0).u - @test jmp_sol_mean_base ≈ jmp_sol_mean_pIn rtol = 1e-1 atol = 1e-1 + @test jmp_sol_mean_base ≈ jmp_sol_mean_pIn rtol = 1.0e-1 atol = 1.0e-1 end # Checks correctness for non-time functions. @@ -119,16 +122,16 @@ end let # Declares the functional parameter. Is = collect(0.0:0.0001:200.0) - spline1d = LinearInterpolation(100.0*Is ./ (100.0 .+ Is), Is) + spline1d = LinearInterpolation(100.0 * Is ./ (100.0 .+ Is), Is) @parameters (i_rate::typeof(spline1d))(..) # Declares the models. sir = @reaction_network begin - k1*100*I/(100 + I), S --> I + k1 * 100 * I / (100 + I), S --> I k2, I --> R end sir_funcp = @reaction_network rs begin - k1*$i_rate(I), S --> I + k1 * $i_rate(I), S --> I k2, I --> R end @@ -139,14 +142,14 @@ let oprob_funcp = ODEProblem(sir_funcp, u0, 200.0, [ps; i_rate => spline1d]) sol = solve(oprob) sol_funcp = solve(oprob_funcp) - @test sol.u ≈ sol_funcp.u atol = 1e-6 rtol = 1e-6 + @test sol.u ≈ sol_funcp.u atol = 1.0e-6 rtol = 1.0e-6 end ### Other tests ### -# Tests that a functional parameter can be interpolated as the function only, as a function of a +# Tests that a functional parameter can be interpolated as the function only, as a function of a # symbolic variable, or interpolated with the functional parameter and argument separately. -let +let # Prepares the functional parameter. ts = collect(0.0:0.1:1.0) spline = LinearInterpolation(log.(ts), ts) diff --git a/test/reactionsystem_core/higher_order_reactions.jl b/test/reactionsystem_core/higher_order_reactions.jl index 070ce7c961..e113bf08f0 100644 --- a/test/reactionsystem_core/higher_order_reactions.jl +++ b/test/reactionsystem_core/higher_order_reactions.jl @@ -34,12 +34,12 @@ let r3 * X3 * X4^2 / factorial(2), X3 + 2X4 ⟾ 3X5 + 3X6 r4 * X2 * X5^3 * X6^3 / (factorial(3) * factorial(3)), 3X5 + 3X6 ⟾ 3X5 + 2X7 + 4X8 r5 * X5^3 * X7^2 * X8^4 / (factorial(3) * factorial(2) * factorial(4)), - 3X5 + 2X7 + 4X8 ⟾ 10X9 + 3X5 + 2X7 + 4X8 ⟾ 10X9 r6 * X9^10 / factorial(10), 10X9 ⟾ X10 d * X10^2 / factorial(2), 2X10 ⟾ ∅ end - for factor in [1e-1, 1e0, 1e1, 1e2] + for factor in [1.0e-1, 1.0e0, 1.0e1, 1.0e2] u0 = rnd_u0(base_higher_order_network, rng; factor) ps = rnd_ps(base_higher_order_network, rng; factor) t = rand(rng) @@ -75,16 +75,18 @@ let 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;) + 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!]) + higher_order_network_alt2 = ConstantRateJump.( + [rate1, rate2, rate3, rate4, rate5, rate6, rate7, rate8], + [affect1!, affect2!, affect3!, affect4!, affect5!, affect6!, affect7!, affect8!] + ) # Prepares JumpProblem via Catalyst. u0_base = rnd_u0_Int64(base_higher_order_network, rng) @@ -106,6 +108,6 @@ let sol_alt2 = solve(jprob_alt2, SSAStepper(); seed, saveat = 1.0) # Checks that species means in the simulations are similar - @test mean(sol_base[:X10]) ≈ mean(sol_alt1[:X10]) atol = 1e-1 rtol = 1e-1 - @test mean(sol_alt1[:X10]) ≈ mean(sol_alt2[10,:]) atol = 1e-1 rtol = 1e-1 + @test mean(sol_base[:X10]) ≈ mean(sol_alt1[:X10]) atol = 1.0e-1 rtol = 1.0e-1 + @test mean(sol_alt1[:X10]) ≈ mean(sol_alt2[10, :]) atol = 1.0e-1 rtol = 1.0e-1 end diff --git a/test/reactionsystem_core/parameter_type_designation.jl b/test/reactionsystem_core/parameter_type_designation.jl index b82d0d4a8f..6782a91edc 100644 --- a/test/reactionsystem_core/parameter_type_designation.jl +++ b/test/reactionsystem_core/parameter_type_designation.jl @@ -31,7 +31,7 @@ begin Reaction(d2, [X2], nothing), Reaction(d3, [X3], nothing), Reaction(d4, [X4], nothing), - Reaction(d5, [X5], nothing) + Reaction(d5, [X5], nothing), ] @named rs = ReactionSystem(rxs) rs = complete(rs) @@ -39,9 +39,9 @@ begin # Declares initial condition and potential parameter sets. u0 = [X1 => 0.1, X2 => 0.2, X3 => 0.3, X4 => 0.4, X5 => 0.5] p_alts = [ - [p1 => 1.0, d1 => 1.0, p2 => 1.2, p3 => 2, p4 => 0.5, d4 => 1//2, p5 => 3//2, d5 => 1.5], - (p1 => 1.0, d1 => 1.0, p2 => 1.2, p3 => 2, p4 => 0.5, d4 => 1//2, p5 => 3//2, d5 => 1.5), - Dict([p1 => 1.0, d1 => 1.0, p2 => 1.2, p3 => 2, p4 => 0.5, d4 => 1//2, p5 => 3//2, d5 => 1.5]) + [p1 => 1.0, d1 => 1.0, p2 => 1.2, p3 => 2, p4 => 0.5, d4 => 1 // 2, p5 => 3 // 2, d5 => 1.5], + (p1 => 1.0, d1 => 1.0, p2 => 1.2, p3 => 2, p4 => 0.5, d4 => 1 // 2, p5 => 3 // 2, d5 => 1.5), + Dict([p1 => 1.0, d1 => 1.0, p2 => 1.2, p3 => 2, p4 => 0.5, d4 => 1 // 2, p5 => 3 // 2, d5 => 1.5]), ] end @@ -62,7 +62,7 @@ end # Tests that simulations with differentially typed variables yields correct results. let for p in p_alts - oprob = ODEProblem(rs, u0, (0.0, 1000.0), p; abstol = 1e-10, reltol = 1e-10) + oprob = ODEProblem(rs, u0, (0.0, 1000.0), p; abstol = 1.0e-10, reltol = 1.0e-10) sol = solve(oprob, Tsit5()) @test all(sol.u[end] .≈ 1.0) end @@ -108,8 +108,8 @@ let @test unwrap(mtk_struct.ps[p3]) == 2 @test unwrap(mtk_struct.ps[d3]) == 2 @test unwrap(mtk_struct.ps[p4]) == Float32(0.5) - @test unwrap(mtk_struct.ps[d4]) == 1//2 - @test unwrap(mtk_struct.ps[p5]) == 3//2 + @test unwrap(mtk_struct.ps[d4]) == 1 // 2 + @test unwrap(mtk_struct.ps[p5]) == 3 // 2 @test unwrap(mtk_struct.ps[d5]) == Float32(1.5) end diff --git a/test/reactionsystem_core/reaction.jl b/test/reactionsystem_core/reaction.jl index 0c936625cd..b5ffa2c22e 100644 --- a/test/reactionsystem_core/reaction.jl +++ b/test/reactionsystem_core/reaction.jl @@ -15,33 +15,33 @@ t = default_t() # Checks that the `Reaction`s have the correct type, and the correct net stoichiometries are generated. let # Declare symbolic variables. - @parameters k n1 n2::Int32 x [isconstantspecies=true] + @parameters k n1 n2::Int32 x [isconstantspecies = true] @species X(t) Y(t) Z(t) @variables A(t) # Tries for different types of rates (should not matter). - for rate in (k, k*A, 2, 3.0, 4//3) + for rate in (k, k * A, 2, 3.0, 4 // 3) # Creates `Reaction`s. rx1 = Reaction(rate, [X], []) rx2 = Reaction(rate, [x], [Y], [1.5], [1]) rx3 = Reaction(rate, [x, X], [], [n1 + n2, n2], []) - rx4 = Reaction(rate, [X, Y], [X, Y, Z], [2//3, 3], [1//3, 1, 2]) + rx4 = Reaction(rate, [X, Y], [X, Y, Z], [2 // 3, 3], [1 // 3, 1, 2]) rx5 = Reaction(rate, [X, Y], [X, Y, Z], [2, 3], [1, n1, n2]) rx6 = Reaction(rate, [X], [x], [n1], [1]) # Check `Reaction` types. - @test rx1 isa Reaction{SymbolicT,Int64} - @test rx2 isa Reaction{SymbolicT,Float64} - @test rx3 isa Reaction{SymbolicT,Any} - @test rx4 isa Reaction{SymbolicT,Rational{Int64}} - @test rx5 isa Reaction{SymbolicT,Any} - @test rx6 isa Reaction{SymbolicT,Any} + @test rx1 isa Reaction{SymbolicT, Int64} + @test rx2 isa Reaction{SymbolicT, Float64} + @test rx3 isa Reaction{SymbolicT, Any} + @test rx4 isa Reaction{SymbolicT, Rational{Int64}} + @test rx5 isa Reaction{SymbolicT, Any} + @test rx6 isa Reaction{SymbolicT, Any} # Check `Reaction` net stoichiometries. issetequal(rx1.netstoich, [X => -1]) issetequal(rx2.netstoich, [x => -1.5, Y => 1.0]) issetequal(rx3.netstoich, [x => -n1 - n2, X => -n2]) - issetequal(rx4.netstoich, [X => -1//3, Y => -2//1, Z => 2//1]) + issetequal(rx4.netstoich, [X => -1 // 3, Y => -2 // 1, Z => 2 // 1]) issetequal(rx5.netstoich, [X => -1, Y => n1 - 3, Z => n2]) issetequal(rx6.netstoich, [X => -n1, x => 1]) end @@ -55,13 +55,13 @@ let @variables A(t) # Tests that the three-argument constructor generates correct result. - @test Reaction(k*A, [X], [Y, Z]) == Reaction(k*A, [X], [Y, Z], [1], [1, 1]) + @test Reaction(k * A, [X], [Y, Z]) == Reaction(k * A, [X], [Y, Z], [1], [1, 1]) # Tests that `[]` and `nothing` can be used interchangeably. - @test Reaction(k*A, [X, Z], nothing) == Reaction(k*A, [X, Z], []) - @test Reaction(k*A, nothing, [Y, Z]) == Reaction(k*A, [], [Y, Z]) - @test Reaction(k*A, [X, Z], nothing, [n1 + n2, 2], nothing) == Reaction(k*A, [X, Z], [], [n1 + n2, 2], []) - @test Reaction(k*A, nothing, [Y, Z], nothing, [n1 + n2, 2]) == Reaction(k*A, [], [Y, Z], [], [n1 + n2, 2]) + @test Reaction(k * A, [X, Z], nothing) == Reaction(k * A, [X, Z], []) + @test Reaction(k * A, nothing, [Y, Z]) == Reaction(k * A, [], [Y, Z]) + @test Reaction(k * A, [X, Z], nothing, [n1 + n2, 2], nothing) == Reaction(k * A, [X, Z], [], [n1 + n2, 2], []) + @test Reaction(k * A, nothing, [Y, Z], nothing, [n1 + n2, 2]) == Reaction(k * A, [], [Y, Z], [], [n1 + n2, 2]) end # Tests that various incorrect inputs yields errors. @@ -72,23 +72,23 @@ let @variables A(t) # Neither substrates nor products. - @test_throws ArgumentError Reaction(k*A, [], []) + @test_throws ArgumentError Reaction(k * A, [], []) # Substrate vector not of equal length to substrate stoichiometry vector. - @test_throws ArgumentError Reaction(k*A, [X, X, Z], [], [1, 2], []) + @test_throws ArgumentError Reaction(k * A, [X, X, Z], [], [1, 2], []) # Product vector not of equal length to product stoichiometry vector. - @test_throws ArgumentError Reaction(k*A, [], [X, X, Z], [], [1, 2]) + @test_throws ArgumentError Reaction(k * A, [], [X, X, Z], [], [1, 2]) # Repeated substrates. - @test_throws ArgumentError Reaction(k*A, [X, X, Z], []) + @test_throws ArgumentError Reaction(k * A, [X, X, Z], []) # Repeated products. - @test_throws ArgumentError Reaction(k*A, [], [Y, Z, Z]) + @test_throws ArgumentError Reaction(k * A, [], [Y, Z, Z]) # Non-valid reactants (parameter or variable). - @test_throws ArgumentError Reaction(k*A, [], [A]) - @test_throws ArgumentError Reaction(k*A, [], [k]) + @test_throws ArgumentError Reaction(k * A, [], [A]) + @test_throws ArgumentError Reaction(k * A, [], [k]) end @@ -135,7 +135,7 @@ let @species X(t) X2(t) metadata = [:noise_scaling => 0.0] - r = Reaction(k, [X], [X2], [2], [1]; metadata=metadata) + r = Reaction(k, [X], [X2], [2], [1]; metadata = metadata) @test Catalyst.getmetadata_dict(r) == [:noise_scaling => 0.0] @test hasmetadata(r, :noise_scaling) @@ -148,7 +148,7 @@ let @test getmetadata(r, :test_metadata) == 1111 metadata_repeated = [:noise_scaling => 0.0, :noise_scaling => 1.0, :metadata_entry => "unused"] - @test_throws Exception Reaction(k, [X], [X2], [2], [1]; metadata=metadata_repeated) + @test_throws Exception Reaction(k, [X], [X2], [2], [1]; metadata = metadata_repeated) end # Tests accessors for system without metadata. @@ -156,12 +156,12 @@ let @parameters k @species X(t) X2(t) - metadata = Pair{Symbol,Any}[] + metadata = Pair{Symbol, Any}[] r1 = Reaction(k, [X], [X2], [2], [1]) - r2 = Reaction(k, [X], [X2], [2], [1]; metadata=metadata) + r2 = Reaction(k, [X], [X2], [2], [1]; metadata = metadata) @test isequal(r1, r2) - @test Catalyst.getmetadata_dict(r1) == Pair{Symbol,Any}[] + @test Catalyst.getmetadata_dict(r1) == Pair{Symbol, Any}[] @test !hasmetadata(r1, :md) end @@ -172,16 +172,16 @@ let @parameters k @species X(t) X2(t) - metadata = Pair{Symbol,Any}[] + metadata = Pair{Symbol, Any}[] push!(metadata, :md_1 => 1.0) push!(metadata, :md_2 => false) push!(metadata, :md_3 => "Hello world") push!(metadata, :md_4 => :sym) - push!(metadata, :md_5 => X + X2^k -1) + push!(metadata, :md_5 => X + X2^k - 1) push!(metadata, :md_6 => (0.1, 2.0)) - r = Reaction(k, [X], [X2], [2], [1]; metadata=metadata) + r = Reaction(k, [X], [X2], [2], [1]; metadata = metadata) - @test Catalyst.getmetadata_dict(r) isa Vector{Pair{Symbol,Any}} + @test Catalyst.getmetadata_dict(r) isa Vector{Pair{Symbol, Any}} @test hasmetadata(r, :md_1) @test hasmetadata(r, :md_2) @test hasmetadata(r, :md_3) @@ -194,7 +194,7 @@ let @test isequal(getmetadata(r, :md_2), false) @test isequal(getmetadata(r, :md_3), "Hello world") @test isequal(getmetadata(r, :md_4), :sym) - @test isequal(getmetadata(r, :md_5), X + X2^k -1) + @test isequal(getmetadata(r, :md_5), X + X2^k - 1) @test isequal(getmetadata(r, :md_6), (0.1, 2.0)) end @@ -204,7 +204,7 @@ let @species X(t) X2(t) r1 = Reaction(k, [X], [X2], [2], [1]) - r2 = Reaction(k, [X], [X2], [2], [1]; metadata=[:noise_scaling => η]) + r2 = Reaction(k, [X], [X2], [2], [1]; metadata = [:noise_scaling => η]) @test !Catalyst.hasnoisescaling(r1) @test Catalyst.hasnoisescaling(r2) @@ -219,7 +219,7 @@ let @species X(t) X2(t) r1 = Reaction(k, [X], [X2], [2], [1]) - r2 = Reaction(k, [X], [X2], [2], [1]; metadata=[:description => "A reaction"]) + r2 = Reaction(k, [X], [X2], [2], [1]; metadata = [:description => "A reaction"]) @test !Catalyst.hasdescription(r1) @test Catalyst.hasdescription(r2) @@ -234,7 +234,7 @@ let @species X(t) X2(t) r1 = Reaction(k, [X], [X2], [2], [1]) - r2 = Reaction(k, [X], [X2], [2], [1]; metadata=[:misc => ('M', :M)]) + r2 = Reaction(k, [X], [X2], [2], [1]; metadata = [:misc => ('M', :M)]) @test !Catalyst.hasmisc(r1) @test Catalyst.hasmisc(r2) @@ -246,10 +246,10 @@ end let t = default_t() @variables E(t) F(t) - @species A(t) B(t) C(t) D(t) + @species A(t) B(t) C(t) D(t) @parameters k1, k2, η - rx = Reaction(k1*E, [A, B], [C], [k2*D, 3], [F], metadata = [:noise_scaling => η]) + rx = Reaction(k1 * E, [A, B], [C], [k2 * D, 3], [F], metadata = [:noise_scaling => η]) us = OrderedSet{Symbolics.SymbolicT}() ps = OrderedSet{Symbolics.SymbolicT}() @test ModelingToolkitBase.eqtype_supports_collect_vars(rx) == true @@ -271,8 +271,10 @@ let rx2 = Reaction(k1, [A], [B], [2], [1]) @test has_physical_scale(rx2) == false - rx3 = Reaction(k1, [A], [B], [2], [1]; - metadata = [:physical_scale => PhysicalScale.Jump, :noise_scaling => 0.1]) + rx3 = Reaction( + k1, [A], [B], [2], [1]; + metadata = [:physical_scale => PhysicalScale.Jump, :noise_scaling => 0.1] + ) @test has_physical_scale(rx3) - @test get_physical_scale(rx3) == PhysicalScale.Jump -end \ No newline at end of file + @test get_physical_scale(rx3) == PhysicalScale.Jump +end diff --git a/test/reactionsystem_core/reactionsystem.jl b/test/reactionsystem_core/reactionsystem.jl index d1136f37f0..c97eb9c022 100644 --- a/test/reactionsystem_core/reactionsystem.jl +++ b/test/reactionsystem_core/reactionsystem.jl @@ -20,7 +20,8 @@ include("../test_functions.jl") # Create the network. @parameters k[1:20] @species A(t) B(t) C(t) D(t) -rxs = [Reaction(k[1], nothing, [A]), # 0 -> A +rxs = [ + Reaction(k[1], nothing, [A]), # 0 -> A Reaction(k[2], [B], nothing), # B -> 0 Reaction(k[3], [A], [C]), # A -> C Reaction(k[4], [C], [A, B]), # C -> A + B @@ -55,18 +56,18 @@ function oderhs(u, kv, t) k = kv[1] du = zeros(eltype(u), 4) du[1] = k[1] - k[3] * A + k[4] * C + 2 * k[5] * C - k[6] * A * B + k[7] * B^2 / 2 - - k[9] * A * B - k[10] * A^2 - k[11] * A^2 / 2 - k[12] * A * B^3 * C^4 / 144 - - 3 * k[13] * A^3 * B / 6 + 2 * k[14] - k[15] * A / (2 + A) - k[16] - - k[19] * t * A + k[9] * A * B - k[10] * A^2 - k[11] * A^2 / 2 - k[12] * A * B^3 * C^4 / 144 - + 3 * k[13] * A^3 * B / 6 + 2 * k[14] - k[15] * A / (2 + A) - k[16] - + k[19] * t * A du[2] = -k[2] * B + k[4] * C - k[6] * A * B - k[7] * B^2 - k[8] * A * B - k[9] * A * B + - k[11] * A^2 / 2 - 3 * k[12] * A * B^3 * C^4 / 144 - k[13] * A^3 * B / 6 + - k[16] + 2 * k[18] * B + k[19] * t * A - 2 * k[20] * t * A * B^2 * C + k[11] * A^2 / 2 - 3 * k[12] * A * B^3 * C^4 / 144 - k[13] * A^3 * B / 6 + + k[16] + 2 * k[18] * B + k[19] * t * A - 2 * k[20] * t * A * B^2 * C du[3] = k[3] * A - k[4] * C - k[5] * C + k[6] * A * B + k[8] * A * B + k[9] * A * B + - k[10] * A^2 / 2 - 2 * k[12] * A * B^3 * C^4 / 144 - - 2 * k[17] * A * exp(B) * C^2 / 2 - k[20] * t * A * B^2 * C + k[10] * A^2 / 2 - 2 * k[12] * A * B^3 * C^4 / 144 - + 2 * k[17] * A * exp(B) * C^2 / 2 - k[20] * t * A * B^2 * C du[4] = k[9] * A * B + k[10] * A^2 / 2 + 3 * k[12] * A * B^3 * C^4 / 144 + - k[17] * A * exp(B) * C^2 / 2 + 2 * k[20] * t * A * B^2 * C - du + k[17] * A * exp(B) * C^2 / 2 + 2 * k[20] * t * A * B^2 * C + return du end # SDE noise coefs. @@ -79,26 +80,28 @@ function sdenoise(u, kv, t) G = zeros(eltype(u), length(k), length(u)) z = zero(eltype(u)) - G = [sqrt(k[1]) z z z; - z -sqrt(k[2] * B) z z; - -sqrt(k[3] * A) z sqrt(k[3] * A) z; - sqrt(k[4] * C) sqrt(k[4] * C) -sqrt(k[4] * C) z; - 2*sqrt(k[5] * C) z -sqrt(k[5] * C) z; - -sqrt(k[6] * A * B) -sqrt(k[6] * A * B) sqrt(k[6] * A * B) z; - sqrt(k[7] * B^2 / 2) -2*sqrt(k[7] * B^2 / 2) z z; - z -sqrt(k[8] * A * B) sqrt(k[8] * A * B) z; - -sqrt(k[9] * A * B) -sqrt(k[9] * A * B) sqrt(k[9] * A * B) sqrt(k[9] * A * B); - -2*sqrt(k[10] * A^2 / 2) z sqrt(k[10] * A^2 / 2) sqrt(k[10] * A^2 / 2); - -sqrt(k[11] * A^2 / 2) sqrt(k[11] * A^2 / 2) z z; - -sqrt(k[12] * A * B^3 * C^4 / 144) -3*sqrt(k[12] * A * B^3 * C^4 / 144) -2*sqrt(k[12] * A * B^3 * C^4 / 144) 3*sqrt(k[12] * A * B^3 * C^4 / 144); - -3*sqrt(k[13] * A^3 * B / 6) -sqrt(k[13] * A^3 * B / 6) z z; - 2*sqrt(k[14]) z z z; - -sqrt(k[15] * A / (2 + A)) z z z; - -sqrt(k[16]) sqrt(k[16]) z z; - z z -2*sqrt(k[17] * A * exp(B) * C^2 / 2) sqrt(k[17] * A * exp(B) * C^2 / 2); - z 2*sqrt(k[18] * B) z z; - -sqrt(k[19] * t * A) sqrt(k[19] * t * A) z z; - z -2*sqrt(k[20] * t * A * B^2 * C) -sqrt(k[20] * t * A * B^2 * C) +2*sqrt(k[20] * t * A * B^2 * C)]' + G = [ + sqrt(k[1]) z z z; + z -sqrt(k[2] * B) z z; + -sqrt(k[3] * A) z sqrt(k[3] * A) z; + sqrt(k[4] * C) sqrt(k[4] * C) -sqrt(k[4] * C) z; + 2 * sqrt(k[5] * C) z -sqrt(k[5] * C) z; + -sqrt(k[6] * A * B) -sqrt(k[6] * A * B) sqrt(k[6] * A * B) z; + sqrt(k[7] * B^2 / 2) -2 * sqrt(k[7] * B^2 / 2) z z; + z -sqrt(k[8] * A * B) sqrt(k[8] * A * B) z; + -sqrt(k[9] * A * B) -sqrt(k[9] * A * B) sqrt(k[9] * A * B) sqrt(k[9] * A * B); + -2 * sqrt(k[10] * A^2 / 2) z sqrt(k[10] * A^2 / 2) sqrt(k[10] * A^2 / 2); + -sqrt(k[11] * A^2 / 2) sqrt(k[11] * A^2 / 2) z z; + -sqrt(k[12] * A * B^3 * C^4 / 144) -3 * sqrt(k[12] * A * B^3 * C^4 / 144) -2 * sqrt(k[12] * A * B^3 * C^4 / 144) 3 * sqrt(k[12] * A * B^3 * C^4 / 144); + -3 * sqrt(k[13] * A^3 * B / 6) -sqrt(k[13] * A^3 * B / 6) z z; + 2 * sqrt(k[14]) z z z; + -sqrt(k[15] * A / (2 + A)) z z z; + -sqrt(k[16]) sqrt(k[16]) z z; + z z -2 * sqrt(k[17] * A * exp(B) * C^2 / 2) sqrt(k[17] * A * exp(B) * C^2 / 2); + z 2 * sqrt(k[18] * B) z z; + -sqrt(k[19] * t * A) sqrt(k[19] * t * A) z z; + z -2 * sqrt(k[20] * t * A * B^2 * C) -sqrt(k[20] * t * A * B^2 * C) +2 * sqrt(k[20] * t * A * B^2 * C) + ]' return G end @@ -120,7 +123,7 @@ let def_p = [k => kvals] def_u0 = [A => 0.5, B => 1.0, C => 1.5, D => 2.0] defs = merge(Dict(def_p), Dict(def_u0)) - defs_typed = convert(Dict{Symbolics.SymbolicT,Symbolics.SymbolicT}, defs) + defs_typed = convert(Dict{Symbolics.SymbolicT, Symbolics.SymbolicT}, defs) @named rs = ReactionSystem(rxs, t, [A, B, C, D], [k]; initial_conditions = defs) rs = complete(rs) @@ -166,7 +169,8 @@ end # Test with jump System. let @species A(t) B(t) C(t) D(t) E(t) F(t) - rxs = [Reaction(k[1], nothing, [A]), # 0 -> A + rxs = [ + Reaction(k[1], nothing, [A]), # 0 -> A Reaction(k[2], [B], nothing), # B -> 0 Reaction(k[3], [A], [C]), # A -> C Reaction(k[4], [C], [A, B]), # C -> A + B @@ -203,8 +207,10 @@ let u0 = rand(rng, 2:10, 6) u0map = unknowns(js) .=> u0 ttt = rand(rng) - jumps = Vector{Union{ConstantRateJump, MassActionJump, VariableRateJump}}(undef, - length(rxs)) + jumps = Vector{Union{ConstantRateJump, MassActionJump, VariableRateJump}}( + undef, + length(rxs) + ) let jumps[1] = MassActionJump(p[1], Vector{Pair{Int, Int}}(), [1 => 1]) @@ -218,24 +224,38 @@ let jumps[9] = MassActionJump(p[9], [1 => 1, 2 => 1], [1 => -1, 2 => -1, 3 => 1, 4 => 1]) jumps[10] = MassActionJump(p[10], [1 => 2], [1 => -2, 3 => 1, 4 => 1]) jumps[11] = MassActionJump(p[11], [1 => 2], [1 => -1, 2 => 1]) - jumps[12] = MassActionJump(p[12], [1 => 1, 2 => 3, 3 => 4], - [1 => -1, 2 => -3, 3 => -2, 4 => 3]) + jumps[12] = MassActionJump( + p[12], [1 => 1, 2 => 3, 3 => 4], + [1 => -1, 2 => -3, 3 => -2, 4 => 3] + ) jumps[13] = MassActionJump(p[13], [1 => 3, 2 => 1], [1 => -3, 2 => -1]) jumps[14] = MassActionJump(p[14], Vector{Pair{Int, Int}}(), [1 => 2]) - jumps[15] = ConstantRateJump((u, p, t) -> p[15] * u[1] / (2 + u[1]), - integrator -> (integrator.u[1] -= 1)) - jumps[16] = ConstantRateJump((u, p, t) -> p[16], - integrator -> (integrator.u[1] -= 1; integrator.u[2] += 1)) - jumps[17] = ConstantRateJump((u, p, t) -> p[17] * u[1] * exp(u[2]) * binomial(u[3], 2), - integrator -> (integrator.u[3] -= 2; integrator.u[4] += 1)) - jumps[18] = ConstantRateJump((u, p, t) -> p[18] * u[2], - integrator -> (integrator.u[2] += 2)) - - jumps[19] = VariableRateJump((u, p, t) -> p[19] * u[4] * t, - integrator -> (integrator.u[4] -= 1; integrator.u[5] += 1)) - jumps[20] = VariableRateJump((u, p, t) -> p[20] * t * u[1] * binomial(u[4], 2) * u[5], - integrator -> (integrator.u[4] -= 2; integrator.u[5] -= 1; integrator.u[6] += 2)) + jumps[15] = ConstantRateJump( + (u, p, t) -> p[15] * u[1] / (2 + u[1]), + integrator -> (integrator.u[1] -= 1) + ) + jumps[16] = ConstantRateJump( + (u, p, t) -> p[16], + integrator -> (integrator.u[1] -= 1; integrator.u[2] += 1) + ) + jumps[17] = ConstantRateJump( + (u, p, t) -> p[17] * u[1] * exp(u[2]) * binomial(u[3], 2), + integrator -> (integrator.u[3] -= 2; integrator.u[4] += 1) + ) + jumps[18] = ConstantRateJump( + (u, p, t) -> p[18] * u[2], + integrator -> (integrator.u[2] += 2) + ) + + jumps[19] = VariableRateJump( + (u, p, t) -> p[19] * u[4] * t, + integrator -> (integrator.u[4] -= 1; integrator.u[5] += 1) + ) + jumps[20] = VariableRateJump( + (u, p, t) -> p[20] * t * u[1] * binomial(u[4], 2) * u[5], + integrator -> (integrator.u[4] -= 2; integrator.u[5] -= 1; integrator.u[6] += 2) + ) unknownoid = Dict(unknown => i for (i, unknown) in enumerate(unknowns(js))) jprob = JumpProblem(js, merge(Dict(u0map), Dict(pmap)), (0.0, 1.0)) @@ -243,8 +263,10 @@ let jspmapper = MT.JumpSysMajParamMapper(js, mtkpars) symbolic_majs = MassActionJump[MT.jumps(js)[i] for i in midxs] symmaj = MT.assemble_maj(symbolic_majs, unknownoid, jspmapper) - maj = MassActionJump(symmaj.param_mapper(mtkpars), symmaj.reactant_stoch, symmaj.net_stoch, - symmaj.param_mapper, scale_rates = false) + maj = MassActionJump( + symmaj.param_mapper(mtkpars), 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] @@ -295,9 +317,9 @@ let rs_dsl = @reaction_network rs begin @parameters p[1:2] k d1 d2 @species (X(t))[1:2] Y1(t) Y2(t) - (p[1],p[2]), 0 --> (X[1],X[2]) - k, (X[1],X[2]) --> (Y1,Y2) - (d1,d2), (Y1,Y2) --> 0 + (p[1], p[2]), 0 --> (X[1], X[2]) + k, (X[1], X[2]) --> (Y1, Y2) + (d1, d2), (Y1, Y2) --> 0 end # Checks equivalence. @@ -309,12 +331,12 @@ let [X[1] => 2.0, X[2] => 5.0, Y1 => 0.2, Y2 => 0.5], [rs_dsl.X => [2.0, 5.0], rs_dsl.Y1 => 0.2, rs_dsl.Y2 => 0.5], [rs_dsl.X[1] => 2.0, X[2] => 5.0, rs_dsl.Y1 => 0.2, rs_dsl.Y2 => 0.5], - [:X => [2.0, 5.0], :Y1 => 0.2, :Y2 => 0.5] + [:X => [2.0, 5.0], :Y1 => 0.2, :Y2 => 0.5], ] ps_alts = [ [p => [1.0, 10.0], d1 => 5.0, d2 => 4.0, k => 2.0], [rs_dsl.p => [1.0, 10.0], rs_dsl.d1 => 5.0, rs_dsl.d2 => 4.0, rs_dsl.k => 2.0], - [:p => [1.0, 10.0], :d1 => 5.0, :d2 => 4.0, :k => 2.0] + [:p => [1.0, 10.0], :d1 => 5.0, :d2 => 4.0, :k => 2.0], ] # Loops through all inputs and check that the correct steady state is reached @@ -322,11 +344,11 @@ let # Technically only one model needs to be check. However, "equivalent" models in MTK can still # have slight differences, so checking for both here to be certain. for rs in [rs_prog, rs_dsl] - oprob = ODEProblem(rs, u0_alts[1], (0.0, 10000.), ps_alts[1]) + oprob = ODEProblem(rs, u0_alts[1], (0.0, 10000.0), ps_alts[1]) for rs in [rs_prog, rs_dsl], u0 in u0_alts, p in ps_alts oprob_remade = remake(oprob; u0, p) - sol = solve(oprob_remade, Vern7(); abstol = 1e-8, reltol = 1e-8) - @test sol[[X[1], X[2], Y1 ,Y2]][end] ≈ [0.5, 5.0, 0.2, 2.5] + sol = solve(oprob_remade, Vern7(); abstol = 1.0e-8, reltol = 1.0e-8) + @test sol[[X[1], X[2], Y1, Y2]][end] ≈ [0.5, 5.0, 0.2, 2.5] end end end @@ -338,7 +360,7 @@ let # Creates a `ReactionSystem`. @parameters x @parameters p d - @species S(t,x) + @species S(t, x) rxs = [ Reaction(p, [], [S]), Reaction(d, [S], []), @@ -363,7 +385,7 @@ let # Creates a reaction system with subsystems. sub_rxs = [ Reaction(k1, [X1], []), - Reaction(k2, [X2], []) + Reaction(k2, [X2], []), ] @named sub_rs = ReactionSystem(sub_rxs, t) sub_eqs = [ @@ -374,7 +396,7 @@ let @named sub_osys = ReactionSystem(sub_eqs, t) rxs = [ Reaction(k2, [X2], []), - Reaction(k3, [X3], []) + Reaction(k3, [X3], []), ] @named rs = ReactionSystem(rxs, t; systems = [sub_rs, sub_osys]) @@ -422,7 +444,7 @@ function f!(du, u, p, t) du[2] = -k1 * C * D + k2 * C * E du[3] = k1 * C * D - k2 * C * E du[4] = -C - nothing + return nothing end function fs!(du, u, p, t) A = p[1] @@ -435,7 +457,7 @@ function fs!(du, u, p, t) du[1] = k1 * A - k2 * B du[2] = -k1 * C * D + k2 * C * E du[3] = k1 * C * D - k2 * C * E - nothing + return nothing end function gs!(dg, u, p, t) A = p[1] @@ -452,7 +474,7 @@ function gs!(dg, u, p, t) dg[2, 4] = sqrt(k2 * C * E) dg[3, 3] = -dg[2, 3] dg[3, 4] = -dg[2, 4] - nothing + return nothing end # Tests for BC and constant species. @@ -460,11 +482,13 @@ let @parameters k1 k2 A [isconstantspecies = true] @species B(t) C(t) [isbcspecies = true] D(t) E(t) Dt = default_time_deriv() - eqs = [(@reaction k1, $A --> B), + eqs = [ + (@reaction k1, $A --> B), (@reaction k2, B --> $A), (@reaction k1, $C + D --> E + $C), Dt(C) ~ -C, - (@reaction k2, E + $C --> $C + D)] + (@reaction k2, E + $C --> $C + D) + ] @named rs = ReactionSystem(eqs, t) rs = complete(rs) @test all(eq -> eq isa Reaction, MT.get_eqs(rs)[1:4]) @@ -485,8 +509,8 @@ let ofun = ODEFunction(f!; sys = MT.SymbolCache(syms)) oprob2 = ODEProblem(ofun, u0, tspan, p) saveat = tspan[2] / 50 - abstol = 1e-10 - reltol = 1e-10 + abstol = 1.0e-10 + reltol = 1.0e-10 sol1 = solve(oprob1, Tsit5(); saveat, abstol, reltol) sol2 = solve(oprob2, Tsit5(); saveat, abstol, reltol) for i in eachindex(sts) @@ -496,11 +520,13 @@ let # Test sde systems. # BC species require a constraint equation to define their dynamics in SDE Systems. let - eqs = [(@reaction k1, $A --> B), + eqs = [ + (@reaction k1, $A --> B), (@reaction k2, B --> $A), (@reaction k1, $C + D --> E + $C), (@reaction k2, E + $C --> $C + D), - Dt(C) ~ 0] # Constraint equation for BC species (constant in time) + Dt(C) ~ 0, + ] # Constraint equation for BC species (constant in time) @named rs = ReactionSystem(eqs, t) rs = complete(rs) ssys = complete(sde_model(rs)) @@ -523,12 +549,14 @@ let let # Test jump systems. - rxs = [(@reaction k1, $A --> B), + rxs = [ + (@reaction k1, $A --> B), (@reaction k2, B --> $A), (@reaction k1, $C + D --> E + $C), (@reaction k2, $C + E --> $C + D), (@reaction k1 * t, $A + $C --> B + $C), - (@reaction k1 * B, 2 * $A + $C --> $C + B)] + (@reaction k1 * B, 2 * $A + $C --> $C + B) + ] @named rs = ReactionSystem(rxs, t) rs = complete(rs) jsys = complete(jump_model(rs)) @@ -562,12 +590,18 @@ end let @parameters k1 A [isconstantspecies = true] @species C(t) [isbcspecies = true] B1(t) B2(t) B3(t) - @named rn = ReactionSystem([(@reaction k1, $C --> B1 + $C), - (@reaction k1, $A --> B2), - (@reaction 10 * k1, ∅ --> B3)], t) + @named rn = ReactionSystem( + [ + (@reaction k1, $C --> B1 + $C), + (@reaction k1, $A --> B2), + (@reaction 10 * k1, ∅ --> B3) + ], t + ) rn = complete(rn) - jprob = JumpProblem(rn, [A => 10, C => 10, B1 => 0, B2 => 0, B3 => 0], (0.0, 10.0), - [k1 => 1.0]; rng, save_positions = (false, false)) + jprob = JumpProblem( + rn, [A => 10, C => 10, B1 => 0, B2 => 0, B3 => 0], (0.0, 10.0), + [k1 => 1.0]; rng, save_positions = (false, false) + ) umean = zeros(4) Nsims = 40000 for i in 1:Nsims @@ -575,8 +609,8 @@ let umean += sol(10.0, idxs = [B1, B2, B3, C]) end umean /= Nsims - @test isapprox(umean[1], umean[2]; rtol = 1e-2) - @test isapprox(umean[1], umean[3]; rtol = 1e-2) + @test isapprox(umean[1], umean[2]; rtol = 1.0e-2) + @test isapprox(umean[1], umean[3]; rtol = 1.0e-2) @test umean[4] == 10 end @@ -585,9 +619,9 @@ end # Test various species related checker functions. let # Creates species and parameters. - @species X(t) Y(t) [isbcspecies=true] - @parameters x y [isconstantspecies=true] - @discretes xt(t) yt(t) [isconstantspecies=true] + @species X(t) Y(t) [isbcspecies = true] + @parameters x y [isconstantspecies = true] + @discretes xt(t) yt(t) [isconstantspecies = true] # Tests properties. @test !isspecies(x) @@ -616,7 +650,7 @@ end # Tests various erroneous `ReactionSystem` creations. let # Prepare model inputs. - @parameters k1 k2 x [isconstantspecies=true] Γ + @parameters k1 k2 x [isconstantspecies = true] Γ @species X1(t) X2(t) @variables V(t) @@ -640,13 +674,13 @@ end let # Conversion of non-autonomous `ReactionSystem` to nonlinear `System`. rs = @reaction_network begin - (p/(1+t),d), 0 <--> X + (p / (1 + t), d), 0 <--> X end @test_throws Exception ss_ode_model(rs) # Conversion of non-complete system to various system types. nc = @network_component begin - (p,d), 0 <--> X + (p, d), 0 <--> X end @test_throws Exception ode_model(nc) @test_throws Exception sde_model(nc) @@ -679,7 +713,7 @@ end ### Specialised ReactionSystem Fields ### -# Checks that correct bindings and (default) iniital conditions are created and stored in various ways and conversions. +# Checks that correct bindings and (default) initial conditions are created and stored in various ways and conversions. let # Declare time differential. D = default_time_deriv() @@ -690,16 +724,16 @@ let @parameters ψ1 ψ2 ψ3 ψ4 ψ5 @parameters χ1 χ2 χ3 χ4 χ5 χ6 χ7 χ8 @parameters a1 a2 a3 a4 - @parameters b1=1.0 b2=2.0 b3=3.0 b4=4.0 - @parameters c1=θ1 c2=θ1+θ2 c3=log(1+θ3) c4=θ4 * θ5^2 + @parameters b1 = 1.0 b2 = 2.0 b3 = 3.0 b4 = 4.0 + @parameters c1 = θ1 c2 = θ1 + θ2 c3 = log(1 + θ3) c4 = θ4 * θ5^2 @parameters d1 d2 d3 d4 @species X1(t) X2(t) X3(t) X4(t) - @species Y1(t)=0.1 Y2(t)=0.2 Y3(t)=0.3 Y4(t)=0.4 - @species Z1(t)=ϕ1 Z2(t)=ϕ1+ϕ2 Z3(t)=log(1+ϕ3) Z4(t)=ϕ5 * ϕ4^2 + @species Y1(t) = 0.1 Y2(t) = 0.2 Y3(t) = 0.3 Y4(t) = 0.4 + @species Z1(t) = ϕ1 Z2(t) = ϕ1 + ϕ2 Z3(t) = log(1 + ϕ3) Z4(t) = ϕ5 * ϕ4^2 @species U1(t) U2(t) U3(t) U4(t) @variables K1(t) K2(t) K3(t) K4(t) - @variables L1(t)=10.0 L2(t)=20.0 L3(t)=30.0 L4(t)=40.0 - @variables M1(t)=ψ1 M2(t)=ψ1+ψ2 M3(t)=log(1+ψ3) M4(t)=ψ4 * ψ5^2 + @variables L1(t) = 10.0 L2(t) = 20.0 L3(t) = 30.0 L4(t) = 40.0 + @variables M1(t) = ψ1 M2(t) = ψ1 + ψ2 M3(t) = log(1 + ψ3) M4(t) = ψ4 * ψ5^2 @variables N1(t) N2(t) N3(t) N4(t) ps = [θ1, θ2, θ3, θ4, θ5, ϕ1, ϕ2, ϕ3, ϕ4, ϕ5, ψ1, ψ2, ψ3, ψ4, ψ5, χ1, χ2, χ3, χ4, χ5, χ6, χ7, χ8, a1, a2, a3, a4, b1, b2, b3, b4, c1, c2, c3, c4, d1, d2, d3, d4] us = [X1, X2, X3, X4, Y1, Y2, Y3, Y4, Z1, Z2, Z3, Z4, U1, U2, U3, U4, K1, K2, K3, K4, L1, L2, L3, L4, M1, M2, M3, M4, N1, N2, N3, N4] @@ -718,10 +752,10 @@ let D(K2) ~ a4 - K2, D(K3) ~ d1 - K3, D(K4) ~ d2 - K4, - L1^2 + X1 ~ b4 ^ 2 + K1^2, - L2^2 + X2 ~ c4 ^ 2 + K2^2, - L3^2 + X3 ~ 1 ^ 2 + K3^2, - L4^2 + X4 ~ 1 ^ 2 + K4^2, + L1^2 + X1 ~ b4^2 + K1^2, + L2^2 + X2 ~ c4^2 + K2^2, + L3^2 + X3 ~ 1^2 + K3^2, + L4^2 + X4 ~ 1^2 + K4^2, D(M1) ~ d3 - M1, D(M2) ~ d4 - M2, D(M3) ~ 1 - M3, @@ -734,12 +768,12 @@ let initial_conditions = [ d1 => 100.0, d2 => χ1, U1 => 200.0, U2 => χ2^3, - N1 => 300.0, N2 => χ3 + log(χ4+ 1), + N1 => 300.0, N2 => χ3 + log(χ4 + 1), ] bindings = [ d3 => 1000.0, d4 => χ5, U3 => 2000.0, U4 => χ6^3, - N3 => 3000.0, N4 => χ7 + log(χ8+ 1), + N3 => 3000.0, N4 => χ7 + log(χ8 + 1), ] @named rs = ReactionSystem(rxs, t, us, ps; initial_conditions, bindings) rs_complete = complete(rs) @@ -750,7 +784,7 @@ let rs, rs_complete, ode_model(rs_complete), complete(ode_model(rs_complete)), mtkcompile(ode_model(rs_complete)), sde_model(rs_complete), complete(sde_model(rs_complete)), mtkcompile(sde_model(rs_complete)), - hybrid_model(rs_complete; default_scale), complete(hybrid_model(rs_complete; default_scale)), mtkcompile(hybrid_model(rs_complete; default_scale)) + hybrid_model(rs_complete; default_scale), complete(hybrid_model(rs_complete; default_scale)), mtkcompile(hybrid_model(rs_complete; default_scale)), ] # Checks that all stored bindings and initial conditions are correct. @@ -761,15 +795,15 @@ let unwrap(L1) => tosym(10.0), unwrap(L2) => tosym(20.0), unwrap(L3) => tosym(30.0), unwrap(L4) => tosym(40.0), unwrap(d1) => tosym(100.0), unwrap(d2) => unwrap(χ1), unwrap(U1) => tosym(200.0), unwrap(U2) => unwrap(χ2^3), - unwrap(N1) => tosym(300.0), unwrap(N2) => unwrap(χ3 + log(χ4+ 1)), + unwrap(N1) => tosym(300.0), unwrap(N2) => unwrap(χ3 + log(χ4 + 1)), ] binds = [ - unwrap(c1) => unwrap(θ1), unwrap(c2) => unwrap(θ1 + θ2), unwrap(c3) => unwrap(log(1+θ3)), unwrap(c4) => unwrap(θ4 * θ5^2), - unwrap(Z1) => unwrap(ϕ1), unwrap(Z2) => unwrap(ϕ1+ϕ2), unwrap(Z3) => unwrap(log(1+ϕ3)), unwrap(Z4) => unwrap(ϕ5 * ϕ4^2), - unwrap(M1) => unwrap(ψ1), unwrap(M2) => unwrap(ψ1+ψ2), unwrap(M3) => unwrap(log(1+ψ3)), unwrap(M4) => unwrap(ψ4 * ψ5^2), + unwrap(c1) => unwrap(θ1), unwrap(c2) => unwrap(θ1 + θ2), unwrap(c3) => unwrap(log(1 + θ3)), unwrap(c4) => unwrap(θ4 * θ5^2), + unwrap(Z1) => unwrap(ϕ1), unwrap(Z2) => unwrap(ϕ1 + ϕ2), unwrap(Z3) => unwrap(log(1 + ϕ3)), unwrap(Z4) => unwrap(ϕ5 * ϕ4^2), + unwrap(M1) => unwrap(ψ1), unwrap(M2) => unwrap(ψ1 + ψ2), unwrap(M3) => unwrap(log(1 + ψ3)), unwrap(M4) => unwrap(ψ4 * ψ5^2), unwrap(d3) => tosym(1000.0), unwrap(d4) => unwrap(χ5), unwrap(U3) => tosym(2000.0), unwrap(U4) => unwrap(χ6^3), - unwrap(N3) => tosym(3000.0), unwrap(N4) => unwrap(χ7 + log(χ8+ 1)), + unwrap(N3) => tosym(3000.0), unwrap(N4) => unwrap(χ7 + log(χ8 + 1)), ] for sys in all_sys @test issetequal((collect(ModelingToolkitBase.get_initial_conditions(sys))), ics) @@ -798,7 +832,7 @@ let Reaction(k1, [Y1], []), Reaction(k2, [Y2], []), Reaction(k3, [Y3], []), - Reaction(k4, [Y4], []) + Reaction(k4, [Y4], []), ] bindings = [d3 => d3_1, d4 => d4_1 + d4_2, X3 => X3_1, X4 => X4_1 + X4_2] initial_conditions = [k3 => k3_1, k4 => k4_1 + k4_2, Y3 => Y3_1, Y4 => Y4_1 + Y4_2] @@ -809,10 +843,10 @@ let d1_1 => 1.0, d2_1 => 0.5, d2_2 => 1.5, d3_1 => 3.0, d4_1 => 1.5, d4_2 => 2.5, X1_1 => 1.0, X2_1 => 0.5, X2_2 => 1.5, X3_1 => 3.0, X4_1 => 1.5, X4_2 => 2.5, k3_1 => 3.0, k4_1 => 1.5, k4_2 => 2.5, - Y3_1 => 3.0, Y4_1 => 1.5, Y4_2 => 2.5 + Y3_1 => 3.0, Y4_1 => 1.5, Y4_2 => 2.5, ] oprob = ODEProblem(rs, [], 10.0, ps) - osol = solve(oprob, Vern7(); saveat = 0.0:0.1:10.0, abstol = 1e-8, reltol = 1e-8) + osol = solve(oprob, Vern7(); saveat = 0.0:0.1:10.0, abstol = 1.0e-8, reltol = 1.0e-8) # Checks that stored parameter values and initial conditions are correct. @test oprob.ps[d1] == osol.ps[d1] == 1.0 @@ -861,14 +895,20 @@ let @parameters k1 k2 @species R(t) - rxs = [Reaction(k1 * S, [S, I], [I], [2, 3], [2]), - Reaction(k2 * R, [I], [R])] + 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]), - k1 * S * binomial(S, 2) * binomial(I, 3)) + @test isequal( + oderatelaw(equations(rs)[1]), + k1 * S * S^2 * I^3 / (factorial(2) * factorial(3)) + ) + @test_skip isequal( + jumpratelaw(equations(eqs)[1]), + k1 * S * binomial(S, 2) * binomial(I, 3) + ) dep = Set() MT.get_variables!(dep, rxs[2], Set(unknowns(rs))) dep2 = Set([R, I]) @@ -880,8 +920,10 @@ let isequal2(a, b) = isequal(simplify(a), simplify(b)) @test isequal2(jumpratelaw(rxs[1]), k1 * S * S * (S - 1) * I * (I - 1) * (I - 2) / 12) - @test isequal2(jumpratelaw(rxs[1]; combinatoric_ratelaw = false), - k1 * S * S * (S - 1) * I * (I - 1) * (I - 2)) + @test isequal2( + jumpratelaw(rxs[1]; combinatoric_ratelaw = false), + k1 * S * S * (S - 1) * I * (I - 1) * (I - 2) + ) @test isequal2(oderatelaw(rxs[1]), k1 * S * S^2 * I^3 / 12) @test isequal2(oderatelaw(rxs[1]; combinatoric_ratelaw = false), k1 * S * S^2 * I^3) @@ -900,19 +942,25 @@ let # Test ConstantRateJump rate scaling. js = complete(jump_model(rs)) - @test isequal2(MT.jumps(js)[1].rate, - k1 * S * S * (S - 1) * I * (I - 1) * (I - 2) / 12) + @test isequal2( + MT.jumps(js)[1].rate, + k1 * S * S * (S - 1) * I * (I - 1) * (I - 2) / 12 + ) js = complete(jump_model(rs; combinatoric_ratelaws = false)) @test isequal2(MT.jumps(js)[1].rate, k1 * S * S * (S - 1) * I * (I - 1) * (I - 2)) js2 = complete(jump_model(rs2)) @test isequal2(MT.jumps(js2)[1].rate, k1 * S * S * (S - 1) * I * (I - 1) * (I - 2)) js3 = complete(jump_model(rs2; combinatoric_ratelaws = true)) - @test isequal2(MT.jumps(js3)[1].rate, - k1 * S * S * (S - 1) * I * (I - 1) * (I - 2) / 12) + @test isequal2( + MT.jumps(js3)[1].rate, + k1 * S * S * (S - 1) * I * (I - 1) * (I - 2) / 12 + ) # Test MassActionJump rate scaling. - rxs = [Reaction(k1, [S, I], [I], [2, 3], [2]), - Reaction(k2, [I], [R])] + rxs = [ + Reaction(k1, [S, I], [I], [2, 3], [2]), + Reaction(k2, [I], [R]), + ] @named rs = ReactionSystem(rxs, t, [S, I, R], [k1, k2]) rs = complete(rs) js = complete(jump_model(rs)) @@ -1006,10 +1054,10 @@ let @test issetequal(parameters(rs), [k, b]) end -# Test parameteric initial conditions. +# Test parametric initial conditions. let @parameters d X0 - @species X(t)=X0 + @species X(t) = X0 rx = Reaction(d, [X], nothing, [1], nothing) @named rs = ReactionSystem([rx], t) rs = complete(rs) @@ -1097,7 +1145,9 @@ end # Additional unsorted tests. let - rn = @reaction_network begin k, X --> 0 end + rn = @reaction_network begin + k, X --> 0 + end isspecies(species(rn)[1]) @test Catalyst.has_species(rn) @test Catalyst.has_rxs(rn) @@ -1141,7 +1191,7 @@ let @test ModelingToolkitBase.getmetadata(complete(ss_ode_model(complete(rs1))), MiscSystemData, nothing) == nothing @test ModelingToolkitBase.getmetadata(complete(ss_ode_model(complete(rs2))), MiscSystemData, nothing) == π - # Check metadata for `ReactionSystem`s where metadata has been udpated + # Check metadata for `ReactionSystem`s where metadata has been updated rs1 = ModelingToolkitBase.setmetadata(rs1, MiscSystemData, "Metadata") rs2 = ModelingToolkitBase.setmetadata(rs2, MiscSystemData, ones(2, 3)) @test ModelingToolkitBase.getmetadata(rs1, MiscSystemData, nothing) == "Metadata" @@ -1166,8 +1216,10 @@ let @test Catalyst.get_parameter_map(rs_plain) === nothing # System with metadata via constructor. - @named rs = ReactionSystem([Reaction(k1, [X], [Y])], t; - metadata = [Catalyst.U0Map => u0map, Catalyst.ParameterMap => pmap]) + @named rs = ReactionSystem( + [Reaction(k1, [X], [Y])], t; + metadata = [Catalyst.U0Map => u0map, Catalyst.ParameterMap => pmap] + ) @test Catalyst.has_u0_map(rs) @test Catalyst.has_parameter_map(rs) @test isequal(Catalyst.get_u0_map(rs), u0map) @@ -1192,8 +1244,10 @@ let # Preservation through flatten (hierarchical system). @named sub = ReactionSystem([Reaction(k2, [Y], [X])], t) - @named parent_rs = ReactionSystem([Reaction(k1, [X], [Y])], t; - systems = [sub], metadata = [Catalyst.U0Map => u0map, Catalyst.ParameterMap => pmap]) + @named parent_rs = ReactionSystem( + [Reaction(k1, [X], [Y])], t; + systems = [sub], metadata = [Catalyst.U0Map => u0map, Catalyst.ParameterMap => pmap] + ) flat = Catalyst.flatten(parent_rs) @test isequal(Catalyst.get_u0_map(flat), u0map) @test isequal(Catalyst.get_parameter_map(flat), pmap) @@ -1229,7 +1283,7 @@ end # there are several places in the code where the `reactionsystem_uptodate` function is called, here # the code might need adaptation to take the updated reaction system into account. let - @test_nowarn Catalyst.reactionsystem_uptodate_check() # Will fix this once most things are actually workin. + @test_nowarn Catalyst.reactionsystem_uptodate_check() # Will fix this once most things are actually working. end # Test that functions using the incidence matrix properly cache it @@ -1244,31 +1298,31 @@ let @test isempty(nps.incidencemat) == true img = incidencematgraph(rn) - @test size(nps.incidencemat) == (3,3) + @test size(nps.incidencemat) == (3, 3) Catalyst.reset!(nps) lcs = linkageclasses(rn) - @test size(nps.incidencemat) == (3,3) + @test size(nps.incidencemat) == (3, 3) Catalyst.reset!(nps) sns = subnetworks(rn) - @test size(nps.incidencemat) == (3,3) + @test size(nps.incidencemat) == (3, 3) Catalyst.reset!(nps) δ = deficiency(rn) - @test size(nps.incidencemat) == (3,3) + @test size(nps.incidencemat) == (3, 3) Catalyst.reset!(nps) δ_l = linkagedeficiencies(rn) - @test size(nps.incidencemat) == (3,3) + @test size(nps.incidencemat) == (3, 3) Catalyst.reset!(nps) rev = isreversible(rn) - @test size(nps.incidencemat) == (3,3) + @test size(nps.incidencemat) == (3, 3) Catalyst.reset!(nps) weakrev = isweaklyreversible(rn, sns) - @test size(nps.incidencemat) == (3,3) + @test size(nps.incidencemat) == (3, 3) end ########## tests related to hybrid systems ########## @@ -1288,10 +1342,12 @@ let @parameters λ k @variables V(t) @species A(t) B(t) C(t) - rxs = [Reaction(k*V, [], [A]), Reaction(λ*A, [B], nothing), - Reaction(k, [A, B], nothing), Reaction(λ, [C], [A])] - eqs = [D(V) ~ λ*V*C] - cevents = [[V ~ 2.0] => [V ~ V/2, A ~ A/2]] + rxs = [ + Reaction(k * V, [], [A]), Reaction(λ * A, [B], nothing), + Reaction(k, [A, B], nothing), Reaction(λ, [C], [A]), + ] + eqs = [D(V) ~ λ * V * C] + cevents = [[V ~ 2.0] => [V ~ V / 2, A ~ A / 2]] @named rs = ReactionSystem(vcat(rxs, eqs), t; continuous_events = cevents) rs = complete(rs) @test_throws ErrorException jump_model(rs) @@ -1304,10 +1360,12 @@ let @variables V(t) @species A(t) B(t) C(t) metadata = [:physical_scale => PhysicalScale.ODE] - rxs = [Reaction(k*V, [], [A]), Reaction(λ*A, [B], nothing; metadata), - Reaction(k, [A, B], nothing), Reaction(λ, [C], [A])] - eqs = [D(V) ~ λ*V*C] - cevents = [[V ~ 2.0] => [V ~ V/2, A ~ A/2]] + rxs = [ + Reaction(k * V, [], [A]), Reaction(λ * A, [B], nothing; metadata), + Reaction(k, [A, B], nothing), Reaction(λ, [C], [A]), + ] + eqs = [D(V) ~ λ * V * C] + cevents = [[V ~ 2.0] => [V ~ V / 2, A ~ A / 2]] @named rs = ReactionSystem(vcat(rxs, eqs), t; continuous_events = cevents) rs = complete(rs) sys = complete(hybrid_model(rs; default_scale = PhysicalScale.Jump)) @@ -1317,7 +1375,7 @@ let @test isempty(constantratejumps(sys)) @test length(variableratejumps(sys)) == 2 @test length(odeeqs(sys)) == 4 - odes = union(eqs, [D(A) ~ 0, D(B) ~ -λ*A*B, D(C) ~ 0]) + odes = union(eqs, [D(A) ~ 0, D(B) ~ -λ * A * B, D(C) ~ 0]) @test issetequal(odes, odeeqs(sys)) @test length(continuous_events(sys)) == 1 end @@ -1330,10 +1388,12 @@ let @species A(t) B(t) C(t) md1 = [:physical_scale => PhysicalScale.ODE] md2 = [:physical_scale => PhysicalScale.VariableRateJump] - rxs = [Reaction(k*V, [], [A]), Reaction(λ*A, [B], nothing; metadata = md1), - Reaction(k, [A, B], nothing), Reaction(λ, [C], [A]; metadata = md2)] - eqs = [D(V) ~ λ*V*C] - cevents = [[V ~ 2.0] => [V ~ V/2, A ~ A/2]] + rxs = [ + Reaction(k * V, [], [A]), Reaction(λ * A, [B], nothing; metadata = md1), + Reaction(k, [A, B], nothing), Reaction(λ, [C], [A]; metadata = md2), + ] + eqs = [D(V) ~ λ * V * C] + cevents = [[V ~ 2.0] => [V ~ V / 2, A ~ A / 2]] @named rs = ReactionSystem(vcat(rxs, eqs), t; continuous_events = cevents) rs = complete(rs) sys = complete(hybrid_model(rs; default_scale = PhysicalScale.Jump)) @@ -1343,7 +1403,7 @@ let @test isempty(constantratejumps(sys)) @test length(variableratejumps(sys)) == 3 @test length(odeeqs(sys)) == 4 - odes = union(eqs, [D(A) ~ 0, D(B) ~ -λ*A*B, D(C) ~ 0]) + odes = union(eqs, [D(A) ~ 0, D(B) ~ -λ * A * B, D(C) ~ 0]) @test issetequal(odes, odeeqs(sys)) @test length(continuous_events(sys)) == 1 end @@ -1368,7 +1428,7 @@ let # Define events continuous_events = [[X ~ 0] => [X ~ -X]] - discrete_events = (X == 1) => [V ~ V/2] + discrete_events = (X == 1) => [V ~ V / 2] # Define metadata metadata = [MiscSystemData => "Comprehensive test system"] @@ -1386,15 +1446,19 @@ let @named sub_rs = ReactionSystem([sub_rx], t) # Create the first reaction system - @named rs1 = ReactionSystem([rx1, rx2, rx3, rx4, eq], t; + @named rs1 = ReactionSystem( + [rx1, rx2, rx3, rx4, eq], t; continuous_events, discrete_events, - metadata, observed = obs, initial_conditions = defs, systems = [sub_rs]) + metadata, observed = obs, initial_conditions = defs, systems = [sub_rs] + ) rs1 = complete(rs1) # Create the second reaction system with the same components - rs2 = ReactionSystem([rx1, rx2, rx3, rx4, eq], t; + rs2 = ReactionSystem( + [rx1, rx2, rx3, rx4, eq], t; continuous_events, discrete_events, - metadata, observed = obs, initial_conditions = defs, systems = [sub_rs], name = :rs1) + metadata, observed = obs, initial_conditions = defs, systems = [sub_rs], name = :rs1 + ) rs2 = complete(rs2) # Check equivalence diff --git a/test/reactionsystem_core/symbolic_stoichiometry.jl b/test/reactionsystem_core/symbolic_stoichiometry.jl index beef92db5b..2e5795e843 100644 --- a/test/reactionsystem_core/symbolic_stoichiometry.jl +++ b/test/reactionsystem_core/symbolic_stoichiometry.jl @@ -42,16 +42,16 @@ let rs1 = complete(ReactionSystem(rxs1, t; name = :rs)) rxs2 = [ - @reaction p, 0 --> $n1*X - @reaction k, n2*X --> n3*Y + @reaction p, 0 --> $n1 * X + @reaction k, n2 * X --> n3 * Y @reaction $d, Y --> 0 ] rs2 = complete(ReactionSystem(rxs2, t; name = :rs)) rs3 = @reaction_network rs begin @parameters d::Float64 n1::Int64 - p, 0 --> n1*X - k, n2*X --> n3*Y + p, 0 --> n1 * X + k, n2 * X --> n3 * Y d, Y --> 0 end @@ -78,8 +78,8 @@ begin g = k + α * C rs = @reaction_network rs begin @parameters k α::Int64 - t*k, 2*(α^2)*A --> $g*B - 1.0, α*A + 2*B --> k*C + α*D + t * k, 2 * (α^2) * A --> $g * B + 1.0, α * A + 2 * B --> k * C + α * D end end @@ -87,8 +87,8 @@ end let # With combinatoric ratelaws. function oderhs(u, p, t) - k,α = p - A,B,C,D = u + k, α = p + A, B, C, D = u n = 2 * α^2 rl = t * k / factorial(n) * A^n rl2 = A^α * B^2 / (2 * factorial(α)) @@ -104,8 +104,8 @@ let # Without combinatoric ratelaws. function oderhs_no_crl(u, p, t) - k,α = p - A,B,C,D = u + k, α = p + A, B, C, D = u n = 2 * α^2 rl = t * k * A^n rl2 = A^α * B^2 @@ -124,34 +124,38 @@ end let # With combinatoric ratelaws. function sdenoise(u, p, t) - k,α = p - A,B,C,D = u + k, α = p + A, B, C, D = u n = 2 * α^2 rl = sqrt(t * k / factorial(n) * A^n) rl2 = sqrt(A^α * B^2 / (2 * factorial(α))) - du = zeros(4,2) - du = [-n*rl (-α*rl2); - (k + α * C)*rl (-2*rl2); - 0.0 k*rl2; - 0.0 α*rl2] + du = zeros(4, 2) + du = [ + -n * rl (-α * rl2); + (k + α * C) * rl (-2 * rl2); + 0.0 k * rl2; + 0.0 α * rl2 + ] return du end @test g_eval(rs, u0_1, ps_1, τ) ≈ sdenoise(u0_2, ps_2, τ) # Without combinatoric ratelaws. function sdenoise_no_crl(u, p, t) - k,α = p - A,B,C,D = u + k, α = p + A, B, C, D = u n = 2 * α^2 rl = sqrt(t * k * A^n) rl2 = sqrt(A^α * B^2) - du = zeros(4,2) - du = [-n*rl (-α*rl2); - (k + α * C)*rl (-2*rl2); - 0.0 k*rl2; - 0.0 α*rl2] + du = zeros(4, 2) + du = [ + -n * rl (-α * rl2); + (k + α * C) * rl (-2 * rl2); + 0.0 k * rl2; + 0.0 α * rl2 + ] return du end @test g_eval(rs, u0_1, ps_1, τ; combinatoric_ratelaws = false) ≈ sdenoise_no_crl(u0_2, ps_2, τ) @@ -163,19 +167,19 @@ let function r1(u, p, t) k, α = p A = u[1] - t * k * binomial(A, 2 * α^2) + return t * k * binomial(A, 2 * α^2) end function affect1!(integrator) k, α = integrator.p C = integrator.u[3] integrator.u[1] -= 2 * α^2 integrator.u[2] += (k + α * C) - nothing + return nothing end function r2(u, p, t) k, α = p - A,B = u[1:2] - binomial(Int64(A), Int64(α)) * B * (B - 1) / 2 + A, B = u[1:2] + return binomial(Int64(A), Int64(α)) * B * (B - 1) / 2 end function affect2!(integrator) k, α = integrator.p @@ -183,7 +187,7 @@ let integrator.u[2] -= 2 integrator.u[3] += k integrator.u[4] += α - nothing + return nothing end jumps = [VariableRateJump(r1, affect1!), VariableRateJump(r2, affect2!)] @@ -218,7 +222,7 @@ let rs = @reaction_network begin @parameters k @variables V(t) - k, X --> V*Y + k, X --> V * Y end # The jump System should have explicit affects (no equations after mtkcompile) @@ -245,19 +249,19 @@ let # parameter vectors as the non-reference ones. rs_int = @reaction_network begin @parameters n::Int64 - (k1, k2), n*X1 <--> X2 + (k1, k2), n * X1 <--> X2 end rs_dec = @reaction_network begin @parameters n::Float64 - (k1, k2), n*X1 <--> X2 + (k1, k2), n * X1 <--> X2 end rs_ref_int = @reaction_network begin @parameters n::Int64 - (k1, k2), 3*X1 <--> X2 + (k1, k2), 3 * X1 <--> X2 end rs_ref_dec = @reaction_network begin @parameters n::Float64 - (k1, k2), 2.5*X1 <--> X2 + (k1, k2), 2.5 * X1 <--> X2 end # Set simulation settings. Initial conditions are design to start, more or less, at @@ -286,21 +290,21 @@ let sprob_int_ref = SDEProblem(rs_ref_int, u0_dec, tspan_stoch, ps_int) ssol_int = solve(sprob_int, ImplicitEM(); seed) ssol_int_ref = solve(sprob_int_ref, ImplicitEM(); seed) - @test mean(ssol_int[:X1]) ≈ mean(ssol_int_ref[:X1]) atol = 2*1e0 + @test mean(ssol_int[:X1]) ≈ mean(ssol_int_ref[:X1]) atol = 2 * 1.0e0 # Test SDE simulations with decimal coefficients. sprob_dec = SDEProblem(rs_dec, u0_dec, tspan_stoch, ps_dec; combinatoric_ratelaws = false) sprob_dec_ref = SDEProblem(rs_ref_dec, u0_dec, tspan_stoch, ps_dec; combinatoric_ratelaws = false) ssol_dec = solve(sprob_dec, ImplicitEM(); seed) ssol_dec_ref = solve(sprob_dec_ref, ImplicitEM(); seed) - @test mean(ssol_dec[:X1]) ≈ mean(ssol_dec_ref[:X1]) atol = 2*1e0 + @test mean(ssol_dec[:X1]) ≈ mean(ssol_dec_ref[:X1]) atol = 2 * 1.0e0 # Test Jump simulations with integer coefficients. jprob_int = JumpProblem(rs_int, u0_int, tspan_stoch, ps_int; rng, save_positions = (false, false)) jprob_int_ref = JumpProblem(rs_ref_int, u0_int, tspan_stoch, ps_int; rng, save_positions = (false, false)) jsol_int = solve(jprob_int, SSAStepper(); seed, saveat = 1.0) jsol_int_ref = solve(jprob_int_ref, SSAStepper(); seed, saveat = 1.0) - @test mean(jsol_int[:X1]) ≈ mean(jsol_int_ref[:X1]) atol = 1e-2 rtol = 1e-2 + @test mean(jsol_int[:X1]) ≈ mean(jsol_int_ref[:X1]) atol = 1.0e-2 rtol = 1.0e-2 end # Check that jump simulations (implemented with and without symbolic stoichiometries) yield simulations @@ -310,16 +314,16 @@ let # Creates the models. sir = @reaction_network begin @parameters n::Int64 k::Int64 - i, S + n*I --> k*I - r, n*I --> n*R + i, S + n * I --> k * I + r, n * I --> n * R end sir_ref = @reaction_network begin i, S + I --> 2I r, I --> R end - ps = [:i => 1e-4, :r => 1e-2, :n => 1.0, :k => 2.0] - ps_ref = [:i => 1e-4, :r => 1e-2] + ps = [:i => 1.0e-4, :r => 1.0e-2, :n => 1.0, :k => 2.0] + ps_ref = [:i => 1.0e-4, :r => 1.0e-2] tspan = (0.0, 250.0) # tspan[2] is selected so that it is in the middle of the outbreak peak. u0 = [:S => 999.0, :I => 1.0, :R => 0.0] @test issetequal(unknowns(sir), unknowns(sir_ref)) @@ -340,6 +344,6 @@ let sols_ref = solve(eprob_ref, SSAStepper(); trajectories = 10000) end_vals = [[sol[s][end] for sol in sols.u] for s in species(sir)] end_vals_ref = [[sol[s][end] for sol in sols_ref.u] for s in species(sir_ref)] - @test mean.(end_vals_ref) ≈ mean.(end_vals) atol=1e-1 rtol = 1e-1 - @test var.(end_vals_ref) ≈ var.(end_vals) atol=1e-1 rtol = 1e-1 + @test mean.(end_vals_ref) ≈ mean.(end_vals) atol = 1.0e-1 rtol = 1.0e-1 + @test var.(end_vals_ref) ≈ var.(end_vals) atol = 1.0e-1 rtol = 1.0e-1 end diff --git a/test/runtests.jl b/test/runtests.jl index 6a02c8a964..0060df0d5d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,77 +9,153 @@ const GROUP = get(ENV, "GROUP", "All") function activate_extensions_env() Pkg.activate("extensions") Pkg.develop(PackageSpec(path = dirname(@__DIR__))) - Pkg.instantiate() + return Pkg.instantiate() end ### Run Tests ### @time begin if GROUP == "All" || GROUP == "Modeling" # Tests the `ReactionSystem` structure and its properties. - @time @safetestset "Reaction Structure" begin include("reactionsystem_core/reaction.jl") end - @time @safetestset "ReactionSystem Structure" begin include("reactionsystem_core/reactionsystem.jl") end - @time @safetestset "Higher Order Reactions" begin include("reactionsystem_core/higher_order_reactions.jl") end - @time @safetestset "Symbolic Stoichiometry" begin include("reactionsystem_core/symbolic_stoichiometry.jl") end - @time @safetestset "Parameter Type Designation" begin include("reactionsystem_core/parameter_type_designation.jl") end - @time @safetestset "Custom CRN Functions" begin include("reactionsystem_core/custom_crn_functions.jl") end - @time @safetestset "Coupled CRN/Equation Systems" begin include("reactionsystem_core/coupled_equation_crn_systems.jl") end - @time @safetestset "Events" begin include("reactionsystem_core/events.jl") end - @time @safetestset "Functional Parameters" begin include("reactionsystem_core/functional_parameters.jl") end + @time @safetestset "Reaction Structure" begin + include("reactionsystem_core/reaction.jl") + end + @time @safetestset "ReactionSystem Structure" begin + include("reactionsystem_core/reactionsystem.jl") + end + @time @safetestset "Higher Order Reactions" begin + include("reactionsystem_core/higher_order_reactions.jl") + end + @time @safetestset "Symbolic Stoichiometry" begin + include("reactionsystem_core/symbolic_stoichiometry.jl") + end + @time @safetestset "Parameter Type Designation" begin + include("reactionsystem_core/parameter_type_designation.jl") + end + @time @safetestset "Custom CRN Functions" begin + include("reactionsystem_core/custom_crn_functions.jl") + end + @time @safetestset "Coupled CRN/Equation Systems" begin + include("reactionsystem_core/coupled_equation_crn_systems.jl") + end + @time @safetestset "Events" begin + include("reactionsystem_core/events.jl") + end + @time @safetestset "Functional Parameters" begin + include("reactionsystem_core/functional_parameters.jl") + end # Tests model creation via the @reaction_network DSL. - @time @safetestset "DSL Basic Model Construction" begin include("dsl/dsl_basic_model_construction.jl") end - @time @safetestset "DSL Advanced Model Construction" begin include("dsl/dsl_advanced_model_construction.jl") end - @time @safetestset "DSL Options" begin include("dsl/dsl_options.jl") end + @time @safetestset "DSL Basic Model Construction" begin + include("dsl/dsl_basic_model_construction.jl") + end + @time @safetestset "DSL Advanced Model Construction" begin + include("dsl/dsl_advanced_model_construction.jl") + end + @time @safetestset "DSL Options" begin + include("dsl/dsl_options.jl") + end # Tests compositional and hierarchical modelling. - @time @safetestset "ReactionSystem Components Based Creation" begin include("compositional_modelling/component_based_model_creation.jl") end # hierarchical modelling broken due to https://github.com/SciML/ModelingToolkit.jl/pull/4101 - @time @safetestset "Brownians, Jumps, and Poissonians Composition" begin include("compositional_modelling/brownians_and_jumps_composition.jl") end + @time @safetestset "ReactionSystem Components Based Creation" begin + include("compositional_modelling/component_based_model_creation.jl") + end # hierarchical modelling broken due to https://github.com/SciML/ModelingToolkit.jl/pull/4101 + @time @safetestset "Brownians, Jumps, and Poissonians Composition" begin + include("compositional_modelling/brownians_and_jumps_composition.jl") + end # Tests various miscellaneous features. - @time @safetestset "API" begin include("miscellaneous_tests/api.jl") end - @time @safetestset "Units" begin include("miscellaneous_tests/units.jl") end # `_validate` currently no longer avaiable, awaiting advice. - @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 "API" begin + include("miscellaneous_tests/api.jl") + end + @time @safetestset "Units" begin + include("miscellaneous_tests/units.jl") + end # `_validate` currently no longer available, awaiting advice. + @time @safetestset "Compound Species" begin + include("miscellaneous_tests/compound_macro.jl") + end + @time @safetestset "Reaction Balancing" begin + include("miscellaneous_tests/reaction_balancing.jl") + end # Tests reaction network analysis features. - @time @safetestset "Conservation Laws" begin include("network_analysis/conservation_laws.jl") end # Multiple issues. https://github.com/SciML/ModelingToolkit.jl/issues/4102 required to start debugging. - @time @safetestset "Network Properties" begin include("network_analysis/network_properties.jl") end - @time @safetestset "CRN Theory" begin include("network_analysis/crn_theory.jl") end + @time @safetestset "Conservation Laws" begin + include("network_analysis/conservation_laws.jl") + end # Multiple issues. https://github.com/SciML/ModelingToolkit.jl/issues/4102 required to start debugging. + @time @safetestset "Network Properties" begin + include("network_analysis/network_properties.jl") + end + @time @safetestset "CRN Theory" begin + include("network_analysis/crn_theory.jl") + end end if GROUP == "All" || GROUP == "Simulation" # Tests ODE, SDE, jump simulations, nonlinear solving, and steady state simulations. - @time @safetestset "ODE System Simulations" begin include("simulation_and_solving/simulate_ODEs.jl") end - @time @safetestset "Automatic Jacobian Construction" begin include("simulation_and_solving/jacobian_construction.jl") end - @time @safetestset "SDE System Simulations" begin include("simulation_and_solving/simulate_SDEs.jl") end - @time @safetestset "Jump System Simulations" begin include("simulation_and_solving/simulate_jumps.jl") end - @time @safetestset "Nonlinear and SteadyState System Solving" begin include("simulation_and_solving/solve_nonlinear.jl") end + @time @safetestset "ODE System Simulations" begin + include("simulation_and_solving/simulate_ODEs.jl") + end + @time @safetestset "Automatic Jacobian Construction" begin + include("simulation_and_solving/jacobian_construction.jl") + end + @time @safetestset "SDE System Simulations" begin + include("simulation_and_solving/simulate_SDEs.jl") + end + @time @safetestset "Jump System Simulations" begin + include("simulation_and_solving/simulate_jumps.jl") + end + @time @safetestset "Nonlinear and SteadyState System Solving" begin + include("simulation_and_solving/solve_nonlinear.jl") + end # Tests upstream SciML and DiffEq stuff. - @time @safetestset "MTK Structure Indexing" begin include("upstream/mtk_structure_indexing.jl") end - @time @safetestset "MTK Problem Inputs" begin include("upstream/mtk_problem_inputs.jl") end # Required to fix lots of these: https://github.com/SciML/ModelingToolkit.jl/issues/4098 + @time @safetestset "MTK Structure Indexing" begin + include("upstream/mtk_structure_indexing.jl") + end + @time @safetestset "MTK Problem Inputs" begin + include("upstream/mtk_problem_inputs.jl") + end # Required to fix lots of these: https://github.com/SciML/ModelingToolkit.jl/issues/4098 end if GROUP == "All" || GROUP == "Hybrid" - @time @safetestset "ReactionSystem Hybrid Solvers" begin include("simulation_and_solving/hybrid_models.jl") end + @time @safetestset "ReactionSystem Hybrid Solvers" begin + include("simulation_and_solving/hybrid_models.jl") + end end if GROUP == "All" || GROUP == "Misc" - @time @safetestset "ReactionSystem Serialisation" begin include("miscellaneous_tests/reactionsystem_serialisation.jl") end - @time @safetestset "Explicit Imports" begin include("miscellaneous_tests/explicit_imports.jl") end + @time @safetestset "ReactionSystem Serialisation" begin + include("miscellaneous_tests/reactionsystem_serialisation.jl") + end + @time @safetestset "Explicit Imports" begin + include("miscellaneous_tests/explicit_imports.jl") + end # BROKEN #@time @safetestset "Latexify" begin include("visualisation/latexify.jl") end # https://github.com/SciML/Catalyst.jl/issues/1352 end if GROUP == "All" || GROUP == "Spatial" # Tests spatial modelling and simulations. - @time @safetestset "PDE Systems Simulations" begin include("spatial_modelling/simulate_PDEs.jl") end - @time @safetestset "Spatial Reactions" begin include("spatial_modelling/spatial_reactions.jl") end - @time @safetestset "Discrete Space Reaction Systems" begin include("spatial_modelling/dspace_reaction_systems.jl") end - @time @safetestset "Spatial Discrete Space Variants" begin include("spatial_modelling/dspace_reaction_systems_space_types.jl") end - @time @safetestset "ODE Discrete Space Systems Simulations" begin include("spatial_modelling/dspace_reaction_systems_ODEs.jl") end - @time @safetestset "Jump Discrete Space Systems Simulations" begin include("spatial_modelling/dspace_reaction_systems_jumps.jl") end - @time @safetestset "Discrete Space Simulation Structure Interfacing" begin include("spatial_modelling/dspace_simulation_struct_interfacing.jl") end + @time @safetestset "PDE Systems Simulations" begin + include("spatial_modelling/simulate_PDEs.jl") + end + @time @safetestset "Spatial Reactions" begin + include("spatial_modelling/spatial_reactions.jl") + end + @time @safetestset "Discrete Space Reaction Systems" begin + include("spatial_modelling/dspace_reaction_systems.jl") + end + @time @safetestset "Spatial Discrete Space Variants" begin + include("spatial_modelling/dspace_reaction_systems_space_types.jl") + end + @time @safetestset "ODE Discrete Space Systems Simulations" begin + include("spatial_modelling/dspace_reaction_systems_ODEs.jl") + end + @time @safetestset "Jump Discrete Space Systems Simulations" begin + include("spatial_modelling/dspace_reaction_systems_jumps.jl") + end + @time @safetestset "Discrete Space Simulation Structure Interfacing" begin + include("spatial_modelling/dspace_simulation_struct_interfacing.jl") + end end # Tests extensions. diff --git a/test/runtests_extensions.jl b/test/runtests_extensions.jl index 78d0362e1a..087f7fa7f6 100644 --- a/test/runtests_extensions.jl +++ b/test/runtests_extensions.jl @@ -8,12 +8,24 @@ using SafeTestsets, Test # runs code in a separate module where outer constants aren't visible. @time begin - @time @safetestset "Graph visualization" begin include(joinpath(@__DIR__, "extensions", "graphmakie.jl")) end - @time @safetestset "BifurcationKit Extension" begin include(joinpath(@__DIR__, "extensions", "bifurcation_kit.jl")) end - @time @safetestset "HomotopyContinuation Extension" begin include(joinpath(@__DIR__, "extensions", "homotopy_continuation.jl")) end - @time @safetestset "Structural Identifiability Extension" begin include(joinpath(@__DIR__, "extensions", "structural_identifiability.jl")) end - @time @safetestset "Steady State Stability Computations" begin include(joinpath(@__DIR__, "extensions", "stability_computation.jl")) end + @time @safetestset "Graph visualization" begin + include(joinpath(@__DIR__, "extensions", "graphmakie.jl")) + end + @time @safetestset "BifurcationKit Extension" begin + include(joinpath(@__DIR__, "extensions", "bifurcation_kit.jl")) + end + @time @safetestset "HomotopyContinuation Extension" begin + include(joinpath(@__DIR__, "extensions", "homotopy_continuation.jl")) + end + @time @safetestset "Structural Identifiability Extension" begin + include(joinpath(@__DIR__, "extensions", "structural_identifiability.jl")) + end + @time @safetestset "Steady State Stability Computations" begin + include(joinpath(@__DIR__, "extensions", "stability_computation.jl")) + end # Test spatial plotting, using CairoMakie and GraphMakie - @time @safetestset "Discrete Space Simulation Plotting" begin include(joinpath(@__DIR__, "extensions", "dspace_simulation_plotting.jl")) end + @time @safetestset "Discrete Space Simulation Plotting" begin + include(joinpath(@__DIR__, "extensions", "dspace_simulation_plotting.jl")) + end end diff --git a/test/simulation_and_solving/hybrid_models.jl b/test/simulation_and_solving/hybrid_models.jl index 9f598ad3c2..bd863cdff7 100644 --- a/test/simulation_and_solving/hybrid_models.jl +++ b/test/simulation_and_solving/hybrid_models.jl @@ -12,9 +12,9 @@ seed = rand(rng, 1:100) let # Time-dependent rate -> VariableRateJump (needs ODE solver) rn = @reaction_network begin - k*(1 + sin(t)), 0 --> A + k * (1 + sin(t)), 0 --> A end - jprob = JumpProblem(rn, [:A => 0], (0.0, 10.0), [:k => .5]; rng) + jprob = JumpProblem(rn, [:A => 0], (0.0, 10.0), [:k => 0.5]; rng) sol = solve(jprob, Tsit5()) @test sol(10.0; idxs = :A) > 0 @@ -22,21 +22,21 @@ let rn = @reaction_network begin k, 0 --> A end - jprob = JumpProblem(rn, [:A => 0], (0.0, 10.0), [:k => .5]; rng) + jprob = JumpProblem(rn, [:A => 0], (0.0, 10.0), [:k => 0.5]; rng) sol = solve(jprob, SSAStepper()) @test sol(10.0; idxs = :A) > 0 # Hybrid model with ODE equations and events - requires HybridProblem rn = @reaction_network begin @parameters λ - k*V, 0 --> A - @equations D(V) ~ λ*V + k * V, 0 --> A + @equations D(V) ~ λ * V @continuous_events begin - [V ~ 2.0] => [V => V/2, A => A/2] + [V ~ 2.0] => [V => V / 2, A => A / 2] end end # JumpProblem no longer supports ODE equations - use HybridProblem instead - jprob = HybridProblem(rn, [:A => 0, :V => 1.0], (0.0, 10.0), [:k => 1.0, :λ => .4]; rng) + jprob = HybridProblem(rn, [:A => 0, :V => 1.0], (0.0, 10.0), [:k => 1.0, :λ => 0.4]; rng) sol = solve(jprob, Tsit5()) end @@ -75,7 +75,7 @@ let function Xf(t, p) local α, β, X₀, Y₀ = p return (α / β) + (α^2 / β^2) + α * (Y₀ - α / β) * t * exp(-β * t) + - (X₀ - α / β - α^2 / β^2) * exp(-β * t) + (X₀ - α / β - α^2 / β^2) * exp(-β * t) end Xact = [Xf(t, p) for t in times] Yact = [Yf(t, p) for t in times] @@ -122,16 +122,16 @@ let 1, X → ∅ end rn_jump = @reaction_network begin - (p,d), 0 <--> V - (k1,k2), V + W <--> VW + (p, d), 0 <--> V + (k1, k2), V + W <--> VW end rn_hybrid = @reaction_network begin A, ∅ → X, [physical_scale = PhysicalScale.ODE] 1, 2X + Y → 3X, [physical_scale = PhysicalScale.ODE] B, X → Y, [physical_scale = PhysicalScale.ODE] 1, X → ∅, [physical_scale = PhysicalScale.ODE] - (p,d), 0 <--> V - (k1,k2), V + W <--> VW + (p, d), 0 <--> V + (k1, k2), V + W <--> VW end # Sets simulation conditions and creates problems corresponding to the different models. @@ -147,13 +147,13 @@ let hybrid_prob = HybridProblem(rn_hybrid, u0_hybrid, tspan, ps_hybrid; save_positions = (false, false), rng) # Performs simulations. Checks that ODE parts are identical. Check that jump parts have similar statistics. - ode_sol = solve(ode_prob, Tsit5(); saveat = 1.0, abstol = 1e-10, reltol = 1e-10) + ode_sol = solve(ode_prob, Tsit5(); saveat = 1.0, abstol = 1.0e-10, reltol = 1.0e-10) jump_sol = solve(jump_prob, SSAStepper(); saveat = 1.0) - hybrid_sol = solve(hybrid_prob, Tsit5(); saveat = 1.0, abstol = 1e-10, reltol = 1e-10) - @test ode_sol[:Y] ≈ hybrid_sol[:Y] atol = 1e-4 rtol = 1e-4 - @test mean(jump_sol[:V]) ≈ mean(hybrid_sol[:V]) atol = 1e-1 rtol = 1e-1 - @test mean(jump_sol[:W]) ≈ mean(hybrid_sol[:W]) atol = 1e-1 rtol = 1e-1 - @test mean(jump_sol[:VW]) ≈ mean(hybrid_sol[:VW]) atol = 1e-1 rtol = 1e-1 + hybrid_sol = solve(hybrid_prob, Tsit5(); saveat = 1.0, abstol = 1.0e-10, reltol = 1.0e-10) + @test ode_sol[:Y] ≈ hybrid_sol[:Y] atol = 1.0e-4 rtol = 1.0e-4 + @test mean(jump_sol[:V]) ≈ mean(hybrid_sol[:V]) atol = 1.0e-1 rtol = 1.0e-1 + @test mean(jump_sol[:W]) ≈ mean(hybrid_sol[:W]) atol = 1.0e-1 rtol = 1.0e-1 + @test mean(jump_sol[:VW]) ≈ mean(hybrid_sol[:VW]) atol = 1.0e-1 rtol = 1.0e-1 end ### Other Tests ### @@ -166,9 +166,9 @@ let @species X(t) = 1.0 @parameters p = 1.0 p, 0 --> X, [physical_scale = PhysicalScale.ODE] - (kB,kD), 2X <--> X2 + (kB, kD), 2X <--> X2 k, X2 --> Y2, [physical_scale = PhysicalScale.ODE] - (kB,kD), 2Y <--> Y2 + (kB, kD), 2Y <--> Y2 d, Y --> 0, [physical_scale = PhysicalScale.ODE] end u0 = [:X2 => 0.0, :Y => 0.0, :Y2 => 0.0] @@ -286,9 +286,9 @@ let @discrete_events [1.0] => [Z1 => Z1 + 1.0] @continuous_events [Y ~ 1.0] => [Y => 5.0] @equations Δ(V) ~ Z1 + X^2 - V - (p,d), 0 <--> X + (p, d), 0 <--> X d, Y --> 0, [physical_scale = PhysicalScale.ODE] - (k*X, k*Y), Z1 <--> Z2, ([physical_scale = PhysicalScale.ODE], [physical_scale = PhysicalScale.Jump]) + (k * X, k * Y), Z1 <--> Z2, ([physical_scale = PhysicalScale.ODE], [physical_scale = PhysicalScale.Jump]) end # Simulates the model. @@ -305,7 +305,7 @@ let @test sol[:V][1] == 1.5 @test sol[:Ztot] ≈ sol[rn.Z1 + rn.Z2] @test minimum(sol[:Y]) ≈ 1.0 - @test maximum(sol[:Y]) ≈ 5.0 atol = 1e-1 rtol = 1e-1 + @test maximum(sol[:Y]) ≈ 5.0 atol = 1.0e-1 rtol = 1.0e-1 @test all(isequal([rn.τ], Symbolics.arguments(Symbolics.value(u))) for u in unknowns(rn)) @test sol(1.0 - eps(); idxs = :Z1) + 1 ≈ sol(1.0 + eps(); idxs = :Z1) end @@ -624,7 +624,7 @@ let sys_hybrid_sde = hybrid_model(rn; default_scale = PhysicalScale.SDE) @test length(equations(sys_sde)) == length(equations(sys_hybrid_sde)) @test length(ModelingToolkitBase.get_brownians(sys_sde)) == - length(ModelingToolkitBase.get_brownians(sys_hybrid_sde)) + length(ModelingToolkitBase.get_brownians(sys_hybrid_sde)) # sde_model with use_legacy_noise=true (default) should use noise_eqs matrix, not brownians. sys_sde_legacy = sde_model(rn; use_legacy_noise = true) @@ -635,7 +635,7 @@ let sys_jump = jump_model(rn) sys_hybrid_jump = hybrid_model(rn; default_scale = PhysicalScale.Jump) @test length(ModelingToolkitBase.get_jumps(sys_jump)) == - length(ModelingToolkitBase.get_jumps(sys_hybrid_jump)) + length(ModelingToolkitBase.get_jumps(sys_hybrid_jump)) end # Tests that the Brownian noise matrix extracted by mtkcompile matches assemble_diffusion. @@ -653,8 +653,10 @@ let # Build the old-style noise matrix via assemble_diffusion for comparison. flatrs = Catalyst.flatten(rn) ists, ispcs = Catalyst.get_indep_sts(flatrs, false) - noise_matrix_old = Catalyst.assemble_diffusion(flatrs, ists, ispcs; - combinatoric_ratelaws = true, remove_conserved = false, expand_catalyst_funs = true) + noise_matrix_old = Catalyst.assemble_diffusion( + flatrs, ists, ispcs; + combinatoric_ratelaws = true, remove_conserved = false, expand_catalyst_funs = true + ) # Both should be 2×2 matrices. Verify they are symbolically equivalent. @test size(noise_matrix_brownian) == size(noise_matrix_old) @@ -694,8 +696,10 @@ let k2, P --> S end - @test_throws ArgumentError hybrid_model(rn; - default_scale = PhysicalScale.Jump, remove_conserved = true) + @test_throws ArgumentError hybrid_model( + rn; + default_scale = PhysicalScale.Jump, remove_conserved = true + ) end # Tests that events pass through to the hybrid system. @@ -806,20 +810,26 @@ let end # Pure ODE → ODEProblem - prob_ode = HybridProblem(rn, [:S => 100.0, :P => 0.0], (0.0, 1.0), [:k1 => 1.0, :k2 => 0.5]; - default_scale = PhysicalScale.ODE) + prob_ode = HybridProblem( + rn, [:S => 100.0, :P => 0.0], (0.0, 1.0), [:k1 => 1.0, :k2 => 0.5]; + default_scale = PhysicalScale.ODE + ) @test prob_ode isa ODEProblem sol = solve(prob_ode, Tsit5()) @test SciMLBase.successful_retcode(sol) # Pure SDE → SDEProblem - prob_sde = HybridProblem(rn, [:S => 100.0, :P => 0.0], (0.0, 1.0), [:k1 => 1.0, :k2 => 0.5]; - default_scale = PhysicalScale.SDE) + prob_sde = HybridProblem( + rn, [:S => 100.0, :P => 0.0], (0.0, 1.0), [:k1 => 1.0, :k2 => 0.5]; + default_scale = PhysicalScale.SDE + ) @test prob_sde isa SDEProblem # Pure Jump → JumpProblem - prob_jump = HybridProblem(rn, [:S => 100, :P => 0], (0.0, 1.0), [:k1 => 1.0, :k2 => 0.5]; - default_scale = PhysicalScale.Jump) + prob_jump = HybridProblem( + rn, [:S => 100, :P => 0], (0.0, 1.0), [:k1 => 1.0, :k2 => 0.5]; + default_scale = PhysicalScale.Jump + ) @test prob_jump isa JumpProcesses.JumpProblem sol = solve(prob_jump, SSAStepper()) @test SciMLBase.successful_retcode(sol) @@ -873,8 +883,10 @@ let k3, S --> 0, [physical_scale = PhysicalScale.Jump] end - prob = HybridProblem(rn, [:S => 100.0, :P => 0.0], (0.0, 1.0), - [:k1 => 1.0, :k2 => 0.5, :k3 => 0.1]) + prob = HybridProblem( + rn, [:S => 100.0, :P => 0.0], (0.0, 1.0), + [:k1 => 1.0, :k2 => 0.5, :k3 => 0.1] + ) @test prob isa JumpProcesses.JumpProblem @test prob.prob isa SciMLBase.SDEProblem @@ -901,8 +913,10 @@ let k1, 0 --> X, [physical_scale = PhysicalScale.SDE] k2, X --> 0, [physical_scale = PhysicalScale.Jump] end - cat_prob = HybridProblem(rn, [:X => X0], tspan, [:k1 => k1, :k2 => k2]; - save_positions = (false, false), rng) + cat_prob = HybridProblem( + rn, [:X => X0], tspan, [:k1 => k1, :k2 => k2]; + save_positions = (false, false), rng + ) # --- Manually coded version using JumpProcesses + StochasticDiffEq --- f_manual!(du, u, p, t) = (du[1] = p[1]) # drift: k1 @@ -954,8 +968,10 @@ let k2, P --> 0, [physical_scale = PhysicalScale.Jump] end - prob = HybridProblem(rn, [:S => 1.0, :P => P0], tspan, [:k1 => k1, :k2 => k2]; - save_positions = (false, false), rng) + prob = HybridProblem( + rn, [:S => 1.0, :P => P0], tspan, [:k1 => k1, :k2 => k2]; + save_positions = (false, false), rng + ) # Run simulations and collect values at all time points Pv = zeros(length(times)) @@ -1002,9 +1018,11 @@ let k4, A --> 0, [physical_scale = PhysicalScale.Jump] k5, D --> 0, [physical_scale = PhysicalScale.Jump] end - cat_prob = HybridProblem(rn, [:A => A0, :B => B0, :C => C0, :D => D0], tspan, + cat_prob = HybridProblem( + rn, [:A => A0, :B => B0, :C => C0, :D => D0], tspan, [:k1 => k1, :k2 => k2, :k3 => k3, :k4 => k4, :k5 => k5]; - save_positions = (false, false), rng) + save_positions = (false, false), rng + ) # --- Manually coded version --- # Species order: [A, B, C, D] @@ -1013,13 +1031,13 @@ let du[1] = p[1] # k1 (A production drift) du[2] = 0.0 du[3] = 0.0 - du[4] = 0.0 + return du[4] = 0.0 end function g_manual!(du, u, p, t) du[1] = sqrt(p[1]) # sqrt(k1) (A production diffusion) du[2] = 0.0 du[3] = 0.0 - du[4] = 0.0 + return du[4] = 0.0 end # Jump 1: A + B -> C at rate k2*A*B @@ -1027,7 +1045,7 @@ let function affect1!(integrator) integrator.u[1] -= 1 # A -= 1 integrator.u[2] -= 1 # B -= 1 - integrator.u[3] += 1 # C += 1 + return integrator.u[3] += 1 # C += 1 end jump1 = VariableRateJump(rate1, affect1!; save_positions = (false, false)) @@ -1036,7 +1054,7 @@ let function affect2!(integrator) integrator.u[3] -= 1 # C -= 1 integrator.u[2] += 1 # B += 1 - integrator.u[4] += 1 # D += 1 + return integrator.u[4] += 1 # D += 1 end jump2 = VariableRateJump(rate2, affect2!; save_positions = (false, false)) @@ -1050,10 +1068,14 @@ let affect4!(integrator) = (integrator.u[4] -= 1) jump4 = VariableRateJump(rate4, affect4!; save_positions = (false, false)) - sde_prob = SciMLBase.SDEProblem(f_manual!, g_manual!, [A0, B0, C0, D0], tspan, - [k1, k2, k3, k4, k5]) - manual_prob = JumpProblem(sde_prob, Direct(), jump1, jump2, jump3, jump4; rng, - save_positions = (false, false)) + sde_prob = SciMLBase.SDEProblem( + f_manual!, g_manual!, [A0, B0, C0, D0], tspan, + [k1, k2, k3, k4, k5] + ) + manual_prob = JumpProblem( + sde_prob, Direct(), jump1, jump2, jump3, jump4; rng, + save_positions = (false, false) + ) # Run simulations and collect values cat_A = zeros(N, length(times)) @@ -1226,8 +1248,10 @@ let n_trials = 500 # Create problem once; the RNG state advances across solves giving different trajectories. - prob = HybridProblem(rn, [:X => 0.0], (0.0, T), [:λ => λ_val]; - save_positions = (false, false), rng = StableRNG(12345)) + prob = HybridProblem( + rn, [:X => 0.0], (0.0, T), [:λ => λ_val]; + save_positions = (false, false), rng = StableRNG(12345) + ) # Pure poissonian system: D(X) ~ dN becomes D(X) ~ 0 after extraction, # mtkcompile reduces to DiscreteProblem → use SSAStepper. final_vals = [solve(prob, SSAStepper(); saveat = T)[X, end] for _ in 1:n_trials] @@ -1260,9 +1284,11 @@ let n_trials = 500 # Create problem once; the RNG state advances across solves. - prob = HybridProblem(rn, [:S => 10.0, :X => 0.0], (0.0, T), + prob = HybridProblem( + rn, [:S => 10.0, :X => 0.0], (0.0, T), [:λ => λ_val, :k => 1.0, :d => 0.1]; - save_positions = (false, false), rng = StableRNG(12345)) + save_positions = (false, false), rng = StableRNG(12345) + ) @test prob isa JumpProblem # Has ODE equations (reactions + D(X) ~ 0 after extraction), so needs ODE solver. @@ -1297,9 +1323,11 @@ let n_trials = 500 # Create problem once; the RNG state advances across solves. - prob = HybridProblem(rn, [:X => 0.0], (0.0, T), + prob = HybridProblem( + rn, [:X => 0.0], (0.0, T), [:λ => λ_val, :σ => σ_val]; - save_positions = (false, false), rng = StableRNG(12345)) + save_positions = (false, false), rng = StableRNG(12345) + ) @test prob isa JumpProblem final_vals = [solve(prob, SRIW1(); saveat = T)[X, end] for _ in 1:n_trials] @@ -1332,8 +1360,10 @@ let # State-dependent rate → VariableRateJump → needs ODE solver. # Create problem once; the RNG state advances across solves. - prob = HybridProblem(rn, [:X => X0], (0.0, T), [:k => k_val]; - save_positions = (false, false), rng = StableRNG(12345)) + prob = HybridProblem( + rn, [:X => X0], (0.0, T), [:k => k_val]; + save_positions = (false, false), rng = StableRNG(12345) + ) @test prob isa JumpProblem final_vals = [solve(prob, Tsit5(); saveat = T)[X, end] for _ in 1:n_trials] @@ -1363,8 +1393,10 @@ let n_trials = 500 # Create problem once; the RNG state advances across solves. - prob = HybridProblem(rn, [:X => 0.0], (0.0, T), [:λ => λ_val]; - save_positions = (false, false), rng = StableRNG(12345)) + prob = HybridProblem( + rn, [:X => 0.0], (0.0, T), [:λ => λ_val]; + save_positions = (false, false), rng = StableRNG(12345) + ) @test prob isa JumpProblem # Pure poissonian: D(X) ~ dN becomes D(X) ~ 0 → DiscreteProblem → SSAStepper. diff --git a/test/simulation_and_solving/jacobian_construction.jl b/test/simulation_and_solving/jacobian_construction.jl index dab99eeb2d..072fb4f129 100644 --- a/test/simulation_and_solving/jacobian_construction.jl +++ b/test/simulation_and_solving/jacobian_construction.jl @@ -34,13 +34,15 @@ let 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 + for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2], repeat in 1:10 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] + 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 @@ -58,13 +60,15 @@ let 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 + for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2], repeat in 1:10 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] + 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 @@ -73,13 +77,13 @@ end let # Creates model (vaguely messy model without conserved quantities). rn = @reaction_network begin - (p,d), 0 <--> (X,Y) - (k1,k2), X + Y <--> XY - (k1,k2), X + 2Y <--> XY2 - (k1,k2), XY + XY2 <--> X2Y3 - d, (XY2,X2Y3) --> 0 - mm(X2Y3,v,K), 0 --> Z - (k3,k4), 3Z <--> Z3 + (p, d), 0 <--> (X, Y) + (k1, k2), X + Y <--> XY + (k1, k2), X + 2Y <--> XY2 + (k1, k2), XY + XY2 <--> X2Y3 + d, (XY2, X2Y3) --> 0 + mm(X2Y3, v, K), 0 --> Z + (k3, k4), 3Z <--> Z3 1.0, X3 --> 0 end @@ -103,8 +107,8 @@ let return J end @test eval_jac(oprob_jac, false) == eval_jac(sprob_jac, false) == eval_jac(nlprob_jac, false) - @test eval_jac(oprob_sjac, true) ≈ eval_jac(sprob_sjac, true) atol = 1e-14 rtol = 1e-14 - @test eval_jac(oprob_sjac, true) ≈ eval_jac(nlprob_sjac, true) atol = 1e-14 rtol = 1e-14 + @test eval_jac(oprob_sjac, true) ≈ eval_jac(sprob_sjac, true) atol = 1.0e-14 rtol = 1.0e-14 + @test eval_jac(oprob_sjac, true) ≈ eval_jac(nlprob_sjac, true) atol = 1.0e-14 rtol = 1.0e-14 end end @@ -115,16 +119,16 @@ let # Creates model (vaguely messy model without conserved quantities). # Model includes a time-dependent reaction. rn = @reaction_network begin - (p,d), 0 <--> (X,Y,Z) + (p, d), 0 <--> (X, Y, Z) k1, X + Y --> XY k2, X + 2Z --> XZ2 - k3, Y3 +X2 --> Y3Z2 + k3, Y3 + X2 --> Y3Z2 k4, X + Y + Z --> XYZ k5, XZ2 + Y3Z2 --> XY3Z4 k6, XYZ + XYZ --> X2Y2Z2 d, (XY3Z4, X2Y2Z2) --> 0 X + Y, V --> 0 - k7/(1 + t), 2V --> V2 + k7 / (1 + t), 2V --> V2 Z, V2 --> 0 end @@ -132,15 +136,15 @@ let for factor in [0.1, 1.0, 10.0] # Creates randomised species and parameter values. Generates jacobians (dense/sparse). u0 = rnd_u0(rn, rng; factor) - t_val = factor*rand() + t_val = factor * rand() ps = rnd_ps(rn, rng; factor) jac = jac_eval(rn, u0, ps, t_val; sparse = false) jac_sparse = jac_eval(rn, u0, ps, t_val; sparse = true) # Check correctness (both by converting to sparse jac to dense, and through multiplication with other matrix). # Approx is due to https://github.com/SciML/ModelingToolkit.jl/issues/3554. - @test Matrix(jac_sparse) ≈ jac atol = 1e-14 rtol = 1e-14 - mat = factor*rand(rng, length(u0), length(u0)) + @test Matrix(jac_sparse) ≈ jac atol = 1.0e-14 rtol = 1.0e-14 + mat = factor * rand(rng, length(u0), length(u0)) @test jac_sparse * mat ≈ jac * mat end end @@ -149,13 +153,13 @@ end let # Creates model (vaguely messy model without conserved quantities). rn = @reaction_network begin - (v0 + mm(X,v,K),d), 0 <--> X + 2Y - (k1,k2), X + Y <--> XY - (k1,k2), X + Y2 <--> XY2 - (k3,k4), XY + XY2 <--> X2Y3 - 1.0, (XY,XY2,X2Y3) --> 0 - mm(X2Y3,v,K), 0 --> Z - (k3*X,k4*Y), 3Z <--> Z3 + (v0 + mm(X, v, K), d), 0 <--> X + 2Y + (k1, k2), X + Y <--> XY + (k1, k2), X + Y2 <--> XY2 + (k3, k4), XY + XY2 <--> X2Y3 + 1.0, (XY, XY2, X2Y3) --> 0 + mm(X2Y3, v, K), 0 --> Z + (k3 * X, k4 * Y), 3Z <--> Z3 d, Z --> 0 end @@ -168,9 +172,9 @@ let oprob_js = ODEProblem(rn, u0, 1.0, ps; jac = true, sparse = true) # Simulates system with implicit solver. Checks that all solutions are identical. - sol = solve(oprob, Rosenbrock23(), saveat = 0.1, abstol = 1e-8, reltol = 1e-8) - sol_j = solve(oprob_j, Rosenbrock23(), saveat = 0.1, abstol = 1e-8, reltol = 1e-8) - sol_s = solve(oprob_s, Rosenbrock23(), saveat = 0.1, abstol = 1e-8, reltol = 1e-8) - sol_js = solve(oprob_js, Rosenbrock23(), saveat = 0.1, abstol = 1e-8, reltol = 1e-8) + sol = solve(oprob, Rosenbrock23(), saveat = 0.1, abstol = 1.0e-8, reltol = 1.0e-8) + sol_j = solve(oprob_j, Rosenbrock23(), saveat = 0.1, abstol = 1.0e-8, reltol = 1.0e-8) + sol_s = solve(oprob_s, Rosenbrock23(), saveat = 0.1, abstol = 1.0e-8, reltol = 1.0e-8) + sol_js = solve(oprob_js, Rosenbrock23(), saveat = 0.1, abstol = 1.0e-8, reltol = 1.0e-8) @test sol ≈ sol_j ≈ sol_s ≈ sol_js end diff --git a/test/simulation_and_solving/simulate_ODEs.jl b/test/simulation_and_solving/simulate_ODEs.jl index e12cac10a2..260f5d78cb 100644 --- a/test/simulation_and_solving/simulate_ODEs.jl +++ b/test/simulation_and_solving/simulate_ODEs.jl @@ -15,17 +15,17 @@ include("../test_networks.jl") # Exponential decay, should be identical to the (known) analytical solution. let - exponential_decay = @reaction_network begin + 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) + for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2] + 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, Vern7(), saveat = t_stops, abstol = 1e-10, reltol = 1e-10) + sol = solve(prob, Vern7(), saveat = t_stops, abstol = 1.0e-10, reltol = 1.0e-10) analytic_sol = [u0[1][2] * exp(-p[1][2] * t) for t in t_stops] @test sol[:X] ≈ analytic_sol end @@ -40,8 +40,8 @@ let (k7, k8), ∅ ↔ X8 end - for factor in [1e-1, 1e0, 1e1] - u0 = rnd_u0(known_equilibrium, rng; factor) + for factor in [1.0e-1, 1.0e0, 1.0e1] + 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, Rosenbrock23()) @@ -60,14 +60,14 @@ let manual_networks = [] u0_syms = [] ps_syms = [] - + function real_functions_1(du, u, p, t) X1, X2, X3 = u p1, p2, p3, k1, k2, k3, k4, d1, d2, d3 = p du[1] = p1 + k1 * X2 - k2 * X1 * X3^2 / factorial(2) - k3 * X1 + k4 * X3 - d1 * X1 du[2] = p2 - k1 * X2 + k2 * X1 * X3^2 / factorial(2) - d2 * X2 - du[3] = p3 + 2 * k1 * X2 - 2 * k2 * X1 * X3^2 / factorial(2) + k3 * X1 - k4 * X3 - - d3 * X3 + return du[3] = p3 + 2 * k1 * X2 - 2 * k2 * X1 * X3^2 / factorial(2) + k3 * X1 - k4 * X3 - + d3 * X3 end push!(catalyst_networks, reaction_networks_standard[1]) push!(manual_networks, real_functions_1) @@ -78,7 +78,7 @@ let X1, X2 = u v1, K1, v2, K2, d = p du[1] = v1 * K1 / (K1 + X2) - d * X1 * X2 - du[2] = v2 * X1 / (K2 + X1) - d * X1 * X2 + return du[2] = v2 * X1 / (K2 + X1) - d * X1 * X2 end push!(catalyst_networks, reaction_networks_standard[2]) push!(manual_networks, real_functions_2) @@ -90,7 +90,7 @@ let v1, v2, v3, K1, K2, K3, n1, n2, n3, d1, d2, d3 = p du[1] = v1 * K1^n1 / (K1^n1 + X3^n1) - d1 * X1 du[2] = v2 * K2^n2 / (K2^n2 + X1^n2) - d2 * X2 - du[3] = v3 * K3^n3 / (K3^n3 + X2^n3) - d3 * X3 + return du[3] = v3 * K3^n3 / (K3^n3 + X2^n3) - d3 * X3 end push!(catalyst_networks, reaction_networks_hill[2]) push!(manual_networks, real_functions_3) @@ -102,7 +102,7 @@ let k1, k2, k3, k4, k5, k6 = p du[1] = -k1 * X1 + k2 * X2 + k5 * X3 - k6 * X1 du[2] = -k3 * X2 + k4 * X3 + k1 * X1 - k2 * X2 - du[3] = -k5 * X3 + k6 * X1 + k3 * X2 - k4 * X3 + return du[3] = -k5 * X3 + k6 * X1 + k3 * X2 - k4 * X3 end push!(catalyst_networks, reaction_networks_conserved[1]) push!(manual_networks, real_functions_4) @@ -114,7 +114,7 @@ let k1, k2, k3, k4 = p du[1] = k1 - k2 * log(12 + X) * X du[2] = k2 * log(12 + X) * X - k3 * log(3 + Y) * Y - du[3] = k3 * log(3 + Y) * Y - log(5, 6 + k4) * Z + return du[3] = k3 * log(3 + Y) * Y - log(5, 6 + k4) * Z end push!(catalyst_networks, reaction_networks_weird[2]) push!(manual_networks, real_functions_5) @@ -122,7 +122,7 @@ let push!(ps_syms, [:k1, :k2, :k3, :k4]) 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] + for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1] # Set input values. u0_1 = rnd_u0(rn_catalyst, rng; factor = factor) ps_1 = rnd_ps(rn_catalyst, rng; factor = factor) @@ -135,7 +135,7 @@ let 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. + # 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()) @@ -148,7 +148,7 @@ end ### Checks Simulations Don't Error ### let for (i, rn) in enumerate(reaction_networks_all) - for factor in [1e-1, 1e0, 1e1] + for factor in [1.0e-1, 1.0e0, 1.0e1] 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]) @@ -165,13 +165,13 @@ end ### Other Tests ### # Checks that solution values have types consistent with their input types. -# Check that both float types are preserved in the solution (and problems), while integers are +# Check that both float types are preserved in the solution (and problems), while integers are # promoted to floats. # Checks that the time types are correct (`Float64` by default or possibly `Float32`). let # Create model. Checks when input type is `Float64` the produced values are also `Float64`. rn = @reaction_network begin - (k1,k2), X1 <--> X2 + (k1, k2), X1 <--> X2 end u0 = [:X1 => 1.0, :X2 => 3.0] ps = [:k1 => 2.0, :k2 => 3.0] @@ -180,7 +180,7 @@ let @test eltype(osol[:X1]) == eltype(osol[:X2]) == typeof(oprob[:X1]) == typeof(oprob[:X2]) == Float64 @test eltype(osol.t) == typeof(oprob.tspan[1]) == typeof(oprob.tspan[2]) == Float64 - # Checks that `Int64` values are promoted to `Float64`. + # Checks that `Int64` values are promoted to `Float64`. u0 = [:X1 => 1, :X2 => 3] ps = [:k1 => 2, :k2 => 3] oprob = ODEProblem(rn, u0, 1, ps) @@ -199,10 +199,10 @@ end # Tests simulating a network without parameters. let - no_param_network = @reaction_network begin - (1.5, 2), ∅ ↔ X + no_param_network = @reaction_network begin + (1.5, 2), ∅ ↔ X end - for factor in [1e0, 1e1, 1e2] + for factor in [1.0e0, 1.0e1, 1.0e2] u0 = rnd_u0(no_param_network, rng; factor) prob = ODEProblem(no_param_network, u0, (0.0, 1000.0)) sol = solve(prob, Rosenbrock23()) @@ -216,19 +216,19 @@ let 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 + return nothing end - rn = @reaction_network begin - k, 2.5 * A --> 3 * B + rn = @reaction_network begin + k, 2.5 * A --> 3 * B end 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) - + # Check equivalence. - du1 = du2 = zeros(2) + du1 = du2 = zeros(2) oprob = ODEProblem(rn, u_1, tspan, p_1; combinatoric_ratelaws = false) oprob.f(du1, oprob.u0, oprob.p, 90.0) oderhs(du2, u_2, p_2, 0.0) diff --git a/test/simulation_and_solving/simulate_SDEs.jl b/test/simulation_and_solving/simulate_SDEs.jl index 3fd300aa08..89c8bbda66 100644 --- a/test/simulation_and_solving/simulate_SDEs.jl +++ b/test/simulation_and_solving/simulate_SDEs.jl @@ -31,7 +31,7 @@ let p, k1, k2, k3, d = p du[1] = 2 * p - k1 * X1 du[2] = k1 * X1 - k2 * X2 - k3 * X2 - du[3] = k2 * X2 + k3 * X2 - d * X3 + return du[3] = k2 * X2 + k3 * X2 - d * X3 end function real_g_1(du, u, p, t) X1, X2, X3 = u @@ -50,7 +50,7 @@ let du[3, 2] = 0 du[3, 3] = sqrt(k2 * X2) du[3, 4] = sqrt(k3 * X2) - du[3, 5] = -sqrt(d * X3) + return du[3, 5] = -sqrt(d * X3) end push!(catalyst_networks, reaction_networks_standard[8]) push!(manual_networks, (f = real_f_1, g = real_g_1, nrp = zeros(3, 5))) @@ -60,13 +60,13 @@ let function real_f_2(du, u, p, t) X1, = u v, K, n, d = p - du[1] = v / 10 + v * X1^n / (X1^n + K^n) - d * X1 + return du[1] = v / 10 + v * X1^n / (X1^n + K^n) - d * X1 end function real_g_2(du, u, p, t) X1, = u v, K, n, d = p du[1, 1] = sqrt(v / 10 + v * X1^n / (X1^n + K^n)) - du[1, 2] = -sqrt(d * X1) + return du[1, 2] = -sqrt(d * X1) end push!(catalyst_networks, reaction_networks_hill[6]) push!(manual_networks, (f = real_f_2, g = real_g_2, nrp = zeros(1, 2))) @@ -82,7 +82,7 @@ let du[4] = -k3 * X3 * X4 + k4 * X5 du[5] = k3 * X3 * X4 - k4 * X5 - k5 * X5 * X6 + k6 * X7 du[6] = -k5 * X5 * X6 + k6 * X7 - du[7] = k5 * X5 * X6 - k6 * X7 + return du[7] = k5 * X5 * X6 - k6 * X7 end function real_g_3(du, u, p, t) X1, X2, X3, X4, X5, X6, X7 = u @@ -105,7 +105,7 @@ let du[6, 5] = -sqrt(k5 * X5 * X6) du[6, 6] = sqrt(k6 * X7) du[7, 5] = sqrt(k5 * X5 * X6) - du[7, 6] = -sqrt(k6 * X7) + return du[7, 6] = -sqrt(k6 * X7) end push!(catalyst_networks, reaction_networks_conserved[9]) push!(manual_networks, (f = real_f_3, g = real_g_3, nrp = zeros(7, 6))) @@ -113,7 +113,7 @@ let push!(ps_syms, [:k1, :k2, :k3, :k4, :k5, :k6]) 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 + for factor in [1.0e-1, 1.0e0], repeat in 1:3 # Set input values. u0_1 = rnd_u0(rn_catalyst, rng; factor, min = 100.0) ps_1 = rnd_ps(rn_catalyst, rng; factor, min = 0.01) @@ -167,7 +167,7 @@ let @parameters η1 η2 @default_noise_scaling η1 p, 0 --> X1 - (k1, k2), X1 ↔ X2, ([noise_scaling=η2],[noise_scaling=2 * η2 + 1]) + (k1, k2), X1 ↔ X2, ([noise_scaling = η2], [noise_scaling = 2 * η2 + 1]) d, X2 --> 0, [noise_scaling = 0.0] end @@ -185,7 +185,7 @@ 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]) + (k1, k2), X1 ↔ X2, ([noise_scaling = η1], [noise_scaling = η2]) end u0 = [:X1 => 1000.0, :X2 => 3000.0] 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]) @@ -201,7 +201,7 @@ let # 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]]) + (k1, k2), X1 ↔ X2, ([noise_scaling = η[1]], [noise_scaling = η[2]]) end @unpack k1, k2, η = noise_scaling_network_2 sprob_2_1 = SDEProblem(noise_scaling_network_2, u0, (0.0, 1000.0), [k1 => 2.0, k2 => 0.66, η => [2.0, 2.0]]) @@ -238,9 +238,9 @@ end # Tests the noise_scaling_parameters getter. let noise_scaling_network = @reaction_network begin - @parameters k1 par1 [description="Parameter par1"] par2 η1 η2=0.0 [description="Parameter η2"] η3=1.0 η4 - (p, d), 0 ↔ X1, ([noise_scaling=η1],[noise_scaling=η2]) - (k1, k2), X1 ↔ X2, ([noise_scaling=η3],[noise_scaling=η4]) + @parameters k1 par1 [description = "Parameter par1"] par2 η1 η2 = 0.0 [description = "Parameter η2"] η3 = 1.0 η4 + (p, d), 0 ↔ X1, ([noise_scaling = η1], [noise_scaling = η2]) + (k1, k2), X1 ↔ X2, ([noise_scaling = η3], [noise_scaling = η4]) end u0 = [:X1 => 500.0, :X2 => 500.0] p = [:p => 20.0, :d => 0.1, :η1 => 0.0, :η3 => 0.0, :η4 => 0.0, :k1 => 2.0, :k2 => 2.0, :par1 => 1000.0, :par2 => 1000.0] @@ -257,11 +257,11 @@ end # tests for default value used in noise scaling parameter. let noise_scaling_network = @reaction_network begin - @parameters η1 η2=0.1 + @parameters η1 η2 = 0.1 @default_noise_scaling η1 - (p,d), 0 <--> X1, ([noise_scaling=η2],[noise_scaling=η2]) - (p,d), 0 <--> X2, ([description="Y"],[description="Y"]) - (p,d), 0 <--> X3 + (p, d), 0 <--> X1, ([noise_scaling = η2], [noise_scaling = η2]) + (p, d), 0 <--> X2, ([description = "Y"], [description = "Y"]) + (p, d), 0 <--> X3 end u0 = [:X1 => 1000.0, :X2 => 1000.0, :X3 => 1000.0] @@ -280,15 +280,15 @@ end return false noise_scaling_network = @reaction_network begin @parameters η1 η2 η3 η4 - @species N1(t) N2(t)=0.5 + @species N1(t) N2(t) = 0.5 @variables N3(t) @default_noise_scaling η1 + N1 + 5.0 p, 0 --> X1 - p, 0 --> X2, [noise_scaling=0.33η2^1.2 + N2] - p, 0 --> X3, [noise_scaling=N3*η3] - p, 0 --> X4, [noise_scaling=exp(-η4) - 0.008] - p, 0 --> X5, [noise_scaling=0.0] - d, (X1, X2, X3, X4, X5) --> 0, ([], [noise_scaling=0.33η2^1.2 + N2], [noise_scaling=N3*η3], [noise_scaling=exp(-η4) - 0.008], [noise_scaling=0.0]) + p, 0 --> X2, [noise_scaling = 0.33η2^1.2 + N2] + p, 0 --> X3, [noise_scaling = N3 * η3] + p, 0 --> X4, [noise_scaling = exp(-η4) - 0.008] + p, 0 --> X5, [noise_scaling = 0.0] + d, (X1, X2, X3, X4, X5) --> 0, ([], [noise_scaling = 0.33η2^1.2 + N2], [noise_scaling = N3 * η3], [noise_scaling = exp(-η4) - 0.008], [noise_scaling = 0.0]) end u0 = [:X1 => 1000.0, :X2 => 1000.0, :X3 => 1000.0, :X4 => 1000.0, :X5 => 1000.0, :N1 => 3.0, :N3 => 0.33] @@ -320,15 +320,15 @@ let @species X(t) H(t) @variables h(t) @parameters p d η - rx1 = Reaction(p, nothing, [X]; metadata = [:noise_scaling => η*H + 1]) - rx2 = @reaction d, X --> 0, [noise_scaling=$h] + rx1 = Reaction(p, nothing, [X]; metadata = [:noise_scaling => η * H + 1]) + rx2 = @reaction d, X --> 0, [noise_scaling = $h] @named rs = ReactionSystem([rx1, rx2], t) # Checks that noise scaling has been added correctly. @test issetequal([X, H], species(rs)) @test issetequal([X, H, h], unknowns(rs)) @test issetequal([p, d, η], parameters(rs)) - @test isequal(getnoisescaling(reactions(rs)[1]), η*H + 1) + @test isequal(getnoisescaling(reactions(rs)[1]), η * H + 1) @test isequal(getnoisescaling(reactions(rs)[2]), h) end @@ -339,7 +339,7 @@ let # Creates noise scaling networks. noise_scaling_network1 = @reaction_network begin @parameters η1 - p, 0 --> X, [noise_scaling=2.0] + p, 0 --> X, [noise_scaling = 2.0] d, X --> 0 end @unpack p, d, η1, X = noise_scaling_network1 @@ -361,11 +361,11 @@ end let # Creates hierarchical model. rn1 = @network_component rn1 begin - p, 0 --> X, [noise_scaling=2.0] + p, 0 --> X, [noise_scaling = 2.0] d, X --> 0 end rn2 = @network_component rn2 begin - k1, X1 --> X2, [noise_scaling=5.0] + k1, X1 --> X2, [noise_scaling = 5.0] k2, X2 --> X1 end rn = compose(rn1, [rn2]) @@ -381,7 +381,6 @@ let end - ### Other Tests ### # Checks that solution values have types consistent with their input types. @@ -392,7 +391,7 @@ end let # Create model. Checks when input type is `Float64` the produced values are also `Float64`. rn = @reaction_network begin - (k1,k2), X1 <--> X2 + (k1, k2), X1 <--> X2 end u0 = [:X1 => 1.0, :X2 => 3.0] ps = [:k1 => 2.0, :k2 => 3.0] @@ -423,7 +422,7 @@ let no_param_network = @reaction_network begin (1.2, 5), X1 ↔ X2 end - for factor in [1e3, 1e4] + for factor in [1.0e3, 1.0e4] u0 = rnd_u0(no_param_network, rng; factor) sprob = SDEProblem(no_param_network, u0, (0.0, 1000.0)) for repeat in 1:5 @@ -511,7 +510,7 @@ end # Test D: `only_use_rate=true` reactions are unaffected by use_jump_ratelaws. let rn = @reaction_network begin - k*X^2, X => Y + k * X^2, X => Y end u0 = [:X => 50.0, :Y => 10.0] ps = [:k => 1.0] @@ -593,23 +592,27 @@ let tspan = (0.0, 20.0) # Build Catalyst SDEProblem with jump rate laws - cat_prob = SDEProblem(rn, [:X => u0_val[1]], tspan, - [:k_prod => k_prod_val, :k_deg => k_deg_val]; use_jump_ratelaws = true) + cat_prob = SDEProblem( + rn, [:X => u0_val[1]], tspan, + [:k_prod => k_prod_val, :k_deg => k_deg_val]; use_jump_ratelaws = true + ) # Hand-coded SDE with jump rate laws function handcoded_f!(du, u, p, t) X = u[1] k_prod, k_deg = p - du[1] = k_prod - k_deg * X * (X - 1) + return du[1] = k_prod - k_deg * X * (X - 1) end function handcoded_g!(du, u, p, t) X = u[1] k_prod, k_deg = p du[1, 1] = sqrt(k_prod) - du[1, 2] = -2 * sqrt(k_deg * X * (X - 1) / 2) + return du[1, 2] = -2 * sqrt(k_deg * X * (X - 1) / 2) end - hand_prob = SDEProblem(handcoded_f!, handcoded_g!, u0_val, tspan, - [k_prod_val, k_deg_val]; noise_rate_prototype = zeros(1, 2)) + hand_prob = SDEProblem( + handcoded_f!, handcoded_g!, u0_val, tspan, + [k_prod_val, k_deg_val]; noise_rate_prototype = zeros(1, 2) + ) # Run ensembles and compare statistics N = 2000 diff --git a/test/simulation_and_solving/simulate_jumps.jl b/test/simulation_and_solving/simulate_jumps.jl index ceeaf6a490..312e8ae92e 100644 --- a/test/simulation_and_solving/simulate_jumps.jl +++ b/test/simulation_and_solving/simulate_jumps.jl @@ -53,8 +53,10 @@ let jump_1_6 = ConstantRateJump(rate_1_6, affect_1_6!) jump_1_7 = ConstantRateJump(rate_1_7, affect_1_7!) 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) + 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!(catalyst_networks, reaction_networks_standard[5]) push!(manual_networks, jumps_1) push!(u0_syms, [:X1, :X2, :X3, :X4]) @@ -128,7 +130,7 @@ let # Loops through all cases, checks that identical simulations are generated with/without Catalyst. for (rn_catalyst, rn_manual, u0_sym, ps_sym, u0_1, ps_1, sp) in - zip(catalyst_networks, manual_networks, u0_syms, ps_syms, u0s, ps, sps) + zip(catalyst_networks, manual_networks, u0_syms, ps_syms, u0s, ps, sps) # Simulates the Catalyst-created model. jprob_1 = JumpProblem(rn_catalyst, u0_1, (0.0, 10000.0), ps_1; aggregator = Direct(), rng) @@ -137,7 +139,7 @@ let # simulate using auto-alg jprob_1b = JumpProblem(rn_catalyst, u0_1, (0.0, 10000.0), ps_1; rng) sol1b = solve(jprob_1; seed, saveat = 1.0) - @test mean(sol1[sp]) ≈ mean(sol1b[sp]) rtol = 1e-1 + @test mean(sol1[sp]) ≈ mean(sol1b[sp]) rtol = 1.0e-1 # Simulates the manually written model u0_2 = map_to_vec(u0_1, u0_sym) @@ -148,7 +150,7 @@ let # Checks that the means are similar (the test have been check that it holds across a large # number of simulates, even without seed). - @test mean(sol1[sp]) ≈ mean(sol2[findfirst(u0_sym .== sp),:]) rtol = 1e-1 + @test mean(sol1[sp]) ≈ mean(sol2[findfirst(u0_sym .== sp), :]) rtol = 1.0e-1 end end @@ -177,25 +179,27 @@ end # this should also help with indirectly testing dep graphs are setup ok let rn = @reaction_network gene_model begin - α*(1 + sin(t)), D --> D + P - μ*(1 + cos(t)), P --> ∅ + α * (1 + sin(t)), D --> D + P + μ * (1 + cos(t)), P --> ∅ k₊, D + P --> D⁻ k₋, D⁻ --> D + P α, D2 --> D2 + P2 μ, P2 --> P3 end - u0map = [rn.D => 1.0, rn.P => 0.0, rn.D⁻ => 0.0, rn.D2 => 1.0, rn.P2 => 0.0, - rn.P3 => 0.0] + u0map = [ + rn.D => 1.0, rn.P => 0.0, rn.D⁻ => 0.0, rn.D2 => 1.0, rn.P2 => 0.0, + rn.P3 => 0.0, + ] pmap = [rn.α => 10.0, rn.μ => 1.0, rn.k₊ => 1.0, rn.k₋ => 2.0] tspan = (0.0, 25.0) # the direct method needs no dep graphs so is good as a baseline for comparison jprobdm = JumpProblem(rn, u0map, tspan, pmap; aggregator = Direct(), save_positions = (false, false), rng) jprobsd = JumpProblem(rn, u0map, tspan, pmap; aggregator = SortingDirect(), save_positions = (false, false), rng) - @test issetequal(jprobsd.discrete_jump_aggregation.dep_gr, [[1,2],[2]]) + @test issetequal(jprobsd.discrete_jump_aggregation.dep_gr, [[1, 2], [2]]) jprobrssa = JumpProblem(rn, u0map, tspan, pmap; aggregator = RSSA(), save_positions = (false, false), rng) - @test issetequal(jprobrssa.discrete_jump_aggregation.vartojumps_map, [[],[],[],[1],[2],[]]) - @test issetequal(jprobrssa.discrete_jump_aggregation.jumptovars_map, [[5],[5,6]]) + @test issetequal(jprobrssa.discrete_jump_aggregation.vartojumps_map, [[], [], [], [1], [2], []]) + @test issetequal(jprobrssa.discrete_jump_aggregation.jumptovars_map, [[5], [5, 6]]) N = 4000 # number of simulations to run function getmean(N, prob) m1 = 0.0 @@ -215,11 +219,11 @@ let # See: https://github.com/SciML/JumpProcesses.jl/issues/545 means1 = zeros(2) means2 = zeros(2) - for (i,prob) in enumerate((jprobdm, jprobsd)) - means1[i],means2[i] = getmean(N, prob) + for (i, prob) in enumerate((jprobdm, jprobsd)) + means1[i], means2[i] = getmean(N, prob) end - @test abs(means1[1] - means1[2]) < .1 * means1[1] - @test abs(means2[1] - means2[2]) < .1 * means2[1] + @test abs(means1[1] - means1[2]) < 0.1 * means1[1] + @test abs(means2[1] - means2[2]) < 0.1 * means2[1] # RSSA simulation tests are broken - see comment above # @test_broken abs(means1[1] - means1[3]) < .1 * means1[1] # @test_broken abs(means2[1] - means2[3]) < .1 * means2[1] @@ -233,7 +237,7 @@ end let # Create model. Checks when input type is `Float64` the produced values are also `Float64`. rn = @reaction_network begin - (k1,k2), X1 <--> X2 + (k1, k2), X1 <--> X2 end u0 = [:X1 => 1.0, :X2 => 3.0] ps = [:k1 => 2.0, :k2 => 3.0] @@ -445,8 +449,10 @@ end @test jprob3.massaction_jump.scaled_rates[1] ≈ 6.0 / factorial(3) # callback + reset_aggregated_jumps! - jprob_cb = JumpProblem(rn, u0, (0.0, 200.0), ps; - aggregator = Direct(), save_positions = (false, false)) + jprob_cb = JumpProblem( + rn, u0, (0.0, 200.0), ps; + aggregator = Direct(), save_positions = (false, false) + ) condit(u, t, integrator) = t == 100.0 setk! = setp(jprob_cb, :k) function affect_cb!(integrator) @@ -495,8 +501,10 @@ end @test jprob3.massaction_jump.scaled_rates[1] ≈ 6.0 # callback + reset_aggregated_jumps! - jprob_cb = JumpProblem(rn, u0, (0.0, 200.0), ps; - aggregator = Direct(), save_positions = (false, false)) + jprob_cb = JumpProblem( + rn, u0, (0.0, 200.0), ps; + aggregator = Direct(), save_positions = (false, false) + ) condit(u, t, integrator) = t == 100.0 setk! = setp(jprob_cb, :k) function affect_cb!(integrator) @@ -541,8 +549,10 @@ end @test jprob2.massaction_jump.scaled_rates[2] ≈ 3.0 # callback changing only the higher-order rate. - jprob_cb = JumpProblem(rn, u0, (0.0, 200.0), ps; - aggregator = Direct(), save_positions = (false, false)) + jprob_cb = JumpProblem( + rn, u0, (0.0, 200.0), ps; + aggregator = Direct(), save_positions = (false, false) + ) condit(u, t, integrator) = t == 100.0 setk1! = setp(jprob_cb, :k1) function affect_cb!(integrator) @@ -641,8 +651,10 @@ end # After callback — both should still agree. for (rn, ps) in [(rn_true, [:k => k_val]), (rn_false, [:k => k_val / factorial(3)])] - jp = JumpProblem(rn, u0, (0.0, 200.0), ps; - aggregator = Direct(), save_positions = (false, false)) + jp = JumpProblem( + rn, u0, (0.0, 200.0), ps; + aggregator = Direct(), save_positions = (false, false) + ) condit(u, t, integrator) = t == 100.0 new_k_true = 24.0 new_k_val = rn === rn_true ? new_k_true : new_k_true / factorial(3) diff --git a/test/simulation_and_solving/solve_nonlinear.jl b/test/simulation_and_solving/solve_nonlinear.jl index a02e9c22a5..041c1cb596 100644 --- a/test/simulation_and_solving/solve_nonlinear.jl +++ b/test/simulation_and_solving/solve_nonlinear.jl @@ -20,7 +20,7 @@ let steady_state_network_1 = @reaction_network begin (k1, k2), ∅ ↔ X1 (k3, k4), ∅ ↔ 3X2 - (k5, k6*X1), ∅ ↔ X3 + (k5, k6 * X1), ∅ ↔ X3 end # Creates NonlinearProblem. @@ -29,16 +29,16 @@ let nlprob = NonlinearProblem(steady_state_network_1, u0, ps) # Solves it using standard algorithm and simulation based algorithm. - sol1 = solve(nlprob; abstol=1e-12, reltol=1e-12) - sol2 = solve(nlprob, DynamicSS(Rosenbrock23()); abstol=1e-12, reltol=1e-12) + sol1 = solve(nlprob; abstol = 1.0e-12, reltol = 1.0e-12) + sol2 = solve(nlprob, DynamicSS(Rosenbrock23()); abstol = 1.0e-12, reltol = 1.0e-12) # Tests solutions are correct. - @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 + @test sol1[:X1] ≈ nlprob.ps[:k1] / nlprob.ps[:k2] atol = 1.0e-10 + @test sol1[:X2]^3 / factorial(3) ≈ nlprob.ps[:k3] / nlprob.ps[:k4] atol = 1.0e-10 + @test sol1[:X3] ≈ (nlprob.ps[:k5] * nlprob.ps[:k2]) / (nlprob.ps[:k6] * nlprob.ps[:k1]) atol = 1.0e-10 + @test sol2[:X1] ≈ nlprob.ps[:k1] / nlprob.ps[:k2] atol = 1.0e-10 + @test sol2[:X2]^3 / factorial(3) ≈ nlprob.ps[:k3] / nlprob.ps[:k4] atol = 1.0e-10 + @test sol2[:X3] ≈ (nlprob.ps[:k5] * nlprob.ps[:k2]) / (nlprob.ps[:k6] * nlprob.ps[:k1]) atol = 1.0e-10 end # Creates a system with multiple steady states. @@ -57,23 +57,23 @@ let nlprob = NonlinearProblem(steady_state_network_2, u0, ps) # Solves it using standard algorithm and simulation based algorithm. - sol1 = solve(nlprob; abstol=1e-12, reltol=1e-12) - sol2 = solve(nlprob, DynamicSS(Rosenbrock23()); abstol=1e-12, reltol=1e-12) + sol1 = solve(nlprob; abstol = 1.0e-12, reltol = 1.0e-12) + sol2 = solve(nlprob, DynamicSS(Rosenbrock23()); abstol = 1.0e-12, reltol = 1.0e-12) # Computes NonlinearFunction (manually and automatically). nlprob_eval_1 = remake(nlprob; u0 = [:X => sol1[1]]) nlprob_eval_2 = remake(nlprob; u0 = [:X => sol2[1]]) - function nf_manual(u,p) + function nf_manual(u, p) X = u[:X] v, K, n, d = p - return v/10 + v * X^n/(X^n + K^n) - d*X + return v / 10 + v * X^n / (X^n + K^n) - d * X end # Tests solutions are correct. - @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 + @test nlprob_eval_1.f(nlprob_eval_1.u0, nlprob_eval_1.p)[1] ≈ 0.0 atol = 1.0e-10 + @test nlprob_eval_2.f(nlprob_eval_2.u0, nlprob_eval_2.p)[1] ≈ 0.0 atol = 1.0e-10 + @test nf_manual(sol1, last.(ps)) ≈ 0.0 atol = 1.0e-10 + @test nf_manual(sol2, last.(ps)) ≈ 0.0 atol = 1.0e-10 end # Checks for system with conservation laws. @@ -81,7 +81,7 @@ end let # Creates steady state network, unpack the parameter values. steady_state_network_3 = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X (k1, k2), 2Y <--> Y2 (k3, k4), X + Y2 <--> XY2 end @@ -89,17 +89,17 @@ let # Creates NonlinearProblem. u0 = [steady_state_network_3.X => rand(), steady_state_network_3.Y => rand() + 1.0, steady_state_network_3.Y2 => rand() + 3.0, steady_state_network_3.XY2 => 0.0] - p = [:p => rand()+1.0, :d => 0.5, :k1 => 1.0, :k2 => 2.0, :k3 => 3.0, :k4 => 4.0] + p = [:p => rand() + 1.0, :d => 0.5, :k1 => 1.0, :k2 => 2.0, :k3 => 3.0, :k4 => 4.0] nl_prob_1 = NonlinearProblem(steady_state_network_3, u0, p; remove_conserved = true, mtkcompile = true) nl_prob_2 = NonlinearProblem(steady_state_network_3, u0, p) # Solves it using standard algorithm and simulation based algorithm. - sol1 = solve(nl_prob_1; abstol=1e-12, reltol=1e-12) - sol2 = solve(nl_prob_2, DynamicSS(Rosenbrock23()); abstol=1e-12, reltol=1e-12) + sol1 = solve(nl_prob_1; abstol = 1.0e-12, reltol = 1.0e-12) + sol2 = solve(nl_prob_2, DynamicSS(Rosenbrock23()); abstol = 1.0e-12, reltol = 1.0e-12) # Checks output using the ODE's drift function - @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 + @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 = 1.0e-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 = 1.0e-10 end ### Other Tests ### @@ -110,7 +110,7 @@ end let # Create model. Checks when input type is `Float64` that the problem and solution types are `Float64`. rn = @reaction_network begin - (k1,k2), X1 <--> X2 + (k1, k2), X1 <--> X2 end u0 = [:X1 => 1.0, :X2 => 3.0] ps = [:k1 => 2.0, :k2 => 3.0] diff --git a/test/spatial_modelling/dspace_reaction_systems.jl b/test/spatial_modelling/dspace_reaction_systems.jl index 648c8659eb..d88070872b 100644 --- a/test/spatial_modelling/dspace_reaction_systems.jl +++ b/test/spatial_modelling/dspace_reaction_systems.jl @@ -34,7 +34,7 @@ end # Test case 2. let rs = @reaction_network begin - @parameters p1 p2 [edgeparameter=true] + @parameters p1 p2 [edgeparameter = true] end @unpack p1, p2 = rs @@ -45,7 +45,7 @@ end # Test case 3. let rs = @reaction_network begin - @parameters pX pY dX [edgeparameter=true] dY + @parameters pX pY dX [edgeparameter = true] dY (pX, 1), 0 <--> X (pY, 1), 0 <--> Y end @@ -87,7 +87,7 @@ end let rs = @reaction_network begin @species W(t) - @parameters pX pY dX [edgeparameter=true] dY + @parameters pX pY dX [edgeparameter = true] dY (pX, 1), 0 <--> X (pY, 1), 0 <--> Y (pZ, 1), 0 <--> Z @@ -178,7 +178,7 @@ let @species Y(t) (p, d), 0 <--> X end - tr = @transport_reaction D*Y X + tr = @transport_reaction D * Y X for grid in grids @test_throws ErrorException DiscreteSpaceReactionSystem(rs, [tr], grid) end @@ -187,7 +187,7 @@ end # Network with edge parameter in non-spatial reaction rate. let rs = @reaction_network begin - @parameters p [edgeparameter=true] + @parameters p [edgeparameter = true] (p, d), 0 <--> X end tr = @transport_reaction D X @@ -199,7 +199,7 @@ end # Network where metadata has been added in rs (which is not seen in transport reaction). let rs = @reaction_network begin - @species X(t) [description="Species with added metadata"] + @species X(t) [description = "Species with added metadata"] (p, d), 0 <--> X end tr = @transport_reaction D X @@ -207,7 +207,7 @@ let @test_throws ErrorException DiscreteSpaceReactionSystem(rs, [tr], grid) rs = @reaction_network begin - @parameters D [description="Parameter with added metadata"] + @parameters D [description = "Parameter with added metadata"] (p, d), 0 <--> X end tr = @transport_reaction D X @@ -234,30 +234,30 @@ let # Variable unknowns. rs1 = @reaction_network begin @variables V(t) - (p,d), 0 <--> X + (p, d), 0 <--> X end @test_throws ArgumentError DiscreteSpaceReactionSystem(rs1, [tr], short_path) # Non-reaction equations. rs2 = @reaction_network begin @equations D(V) ~ X - V - (p,d), 0 <--> X + (p, d), 0 <--> X end @test_throws ArgumentError DiscreteSpaceReactionSystem(rs2, [tr], short_path) # Events. rs3 = @reaction_network begin @discrete_events [1.0] => [p => p + 1] - (p,d), 0 <--> X + (p, d), 0 <--> X end @test_throws ArgumentError DiscreteSpaceReactionSystem(rs3, [tr], short_path) # Observables (only generates a warning). rs4 = @reaction_network begin @observables X2 ~ 2X - (p,d), 0 <--> X + (p, d), 0 <--> X end - @test_logs (:warn, r"The `ReactionSystem` used as input to `DiscreteSpaceReactionSystem` contain observables. It *") match_mode=:any DiscreteSpaceReactionSystem(rs4, [tr], short_path) + @test_logs (:warn, r"The `ReactionSystem` used as input to `DiscreteSpaceReactionSystem` contain observables. It *") match_mode = :any DiscreteSpaceReactionSystem(rs4, [tr], short_path) end # Tests for hierarchical input system (should yield a warning). @@ -269,16 +269,16 @@ let @named rs1 = ReactionSystem(rxs, t) @named rs2 = ReactionSystem(rxs, t; systems = [rs1]) rs2 = complete(rs2) - @test_logs (:warn, r"The `ReactionSystem` used as input to `DiscreteSpaceReactionSystem` was originally created as a hierarchical model. While *") match_mode=:any DiscreteSpaceReactionSystem(rs2, [TransportReaction(D, X)], CartesianGrid((2,2))) + @test_logs (:warn, r"The `ReactionSystem` used as input to `DiscreteSpaceReactionSystem` was originally created as a hierarchical model. While *") match_mode = :any DiscreteSpaceReactionSystem(rs2, [TransportReaction(D, X)], CartesianGrid((2, 2))) end # Tests for non-complete input `ReactionSystem`. let tr = @transport_reaction D X rs = @network_component begin - (p,d), 0 <--> X + (p, d), 0 <--> X end - @test_throws ArgumentError DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,2))) + @test_throws ArgumentError DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2, 2))) end # Tests for array parameters/species. @@ -287,17 +287,17 @@ let rs1 = @reaction_network begin @species X(t)[1:2] Y(t) - (k1,k2), X[1] <--> X[2] + (k1, k2), X[1] <--> X[2] end - @test_throws ArgumentError DiscreteSpaceReactionSystem(rs1, [tr], CartesianGrid((2,2))) + @test_throws ArgumentError DiscreteSpaceReactionSystem(rs1, [tr], CartesianGrid((2, 2))) - rs2 = @reaction_network begin + rs2 = @reaction_network begin @species Y(t) - @parameters k[1:2,1:2] - (k[1,1],k[1,2]), X11 <--> X12 - (k[2,1],k[2,2]), X21 <--> X22 + @parameters k[1:2, 1:2] + (k[1, 1], k[1, 2]), X11 <--> X12 + (k[2, 1], k[2, 2]), X21 <--> X22 end - @test_throws ArgumentError DiscreteSpaceReactionSystem(rs2, [tr], CartesianGrid((2,2))) + @test_throws ArgumentError DiscreteSpaceReactionSystem(rs2, [tr], CartesianGrid((2, 2))) end ### Tests Grid Vertex and Edge Number Computation ### @@ -307,15 +307,17 @@ let # Function counting the values in an iterator by stepping through it. function iterator_count(iterator) count = 0 - foreach(e -> count+=1, iterator) + foreach(e -> count += 1, iterator) return count end # Cartesian and masked grid (test diagonal edges as well). - for space in [small_1d_cartesian_grid, small_2d_cartesian_grid, small_3d_cartesian_grid, - random_1d_masked_grid, random_2d_masked_grid, random_3d_masked_grid] + for space in [ + small_1d_cartesian_grid, small_2d_cartesian_grid, small_3d_cartesian_grid, + random_1d_masked_grid, random_2d_masked_grid, random_3d_masked_grid, + ] dsrs1 = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_1, space) - dsrs2 = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_1, space; diagonal_connections=true) + dsrs2 = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_1, space; diagonal_connections = true) @test num_edges(dsrs1) == iterator_count(edge_iterator(dsrs1)) @test num_edges(dsrs2) == iterator_count(edge_iterator(dsrs2)) end @@ -333,7 +335,7 @@ end let # Prepares the model and the function that determines the edge values. rn = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X end tr = @transport_reaction D X function make_edge_p_value(src_vert, dst_vert) @@ -341,9 +343,11 @@ let end # Loops through a variety of grids, checks that `make_edge_p_values` yields the correct values. - for space in [small_1d_cartesian_grid, small_2d_cartesian_grid, small_3d_cartesian_grid, - small_1d_masked_grid, small_2d_masked_grid, small_3d_masked_grid, - random_1d_masked_grid, random_2d_masked_grid, random_3d_masked_grid] + for space in [ + small_1d_cartesian_grid, small_2d_cartesian_grid, small_3d_cartesian_grid, + small_1d_masked_grid, small_2d_masked_grid, small_3d_masked_grid, + random_1d_masked_grid, random_2d_masked_grid, random_3d_masked_grid, + ] dsrs = DiscreteSpaceReactionSystem(rn, [tr], space) fspat_to_grid_idx = Catalyst.get_index_converters(dspace(dsrs), num_verts(dsrs))[1] edge_values = make_edge_p_values(dsrs, make_edge_p_value) @@ -369,39 +373,39 @@ let dsrs = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid(n)) ps = [:D => make_directed_edge_values(dsrs, (10.0, 0.0))] oprob = ODEProblem(dsrs, u0, tspan, ps) - @test isapprox(solve(oprob, Tsit5()).u[end][5], n, rtol=1e-6) + @test isapprox(solve(oprob, Tsit5()).u[end][5], n, rtol = 1.0e-6) # Checks the 2d case (both with 1d and 2d flow). - dsrs = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid((n,n))) + dsrs = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid((n, n))) ps = [:D => make_directed_edge_values(dsrs, (1.0, 0.0), (0.0, 0.0))] oprob = ODEProblem(dsrs, u0, tspan, ps) - @test all(isapprox.(solve(oprob, Tsit5()).u[end][5:5:25], n, rtol=1e-6)) + @test all(isapprox.(solve(oprob, Tsit5()).u[end][5:5:25], n, rtol = 1.0e-6)) ps = [:D => make_directed_edge_values(dsrs, (1.0, 0.0), (1.0, 0.0))] oprob = ODEProblem(dsrs, u0, tspan, ps) - @test isapprox(solve(oprob, Tsit5()).u[end][25], n^2, rtol=1e-6) + @test isapprox(solve(oprob, Tsit5()).u[end][25], n^2, rtol = 1.0e-6) # Checks the 3d case (both with 1d and 2d flow). - dsrs = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid((n,n,n))) + dsrs = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid((n, n, n))) ps = [:D => make_directed_edge_values(dsrs, (1.0, 0.0), (0.0, 0.0), (0.0, 0.0))] oprob = ODEProblem(dsrs, u0, tspan, ps) - @test all(isapprox.(solve(oprob, Tsit5()).u[end][5:5:125], n, rtol=1e-6)) + @test all(isapprox.(solve(oprob, Tsit5()).u[end][5:5:125], n, rtol = 1.0e-6)) ps = [:D => make_directed_edge_values(dsrs, (1.0, 0.0), (1.0, 0.0), (0.0, 0.0))] oprob = ODEProblem(dsrs, u0, tspan, ps) - @test all(isapprox.(solve(oprob, Tsit5()).u[end][25:25:125], n^2, rtol=1e-6)) + @test all(isapprox.(solve(oprob, Tsit5()).u[end][25:25:125], n^2, rtol = 1.0e-6)) ps = [:D => make_directed_edge_values(dsrs, (1.0, 0.0), (1.0, 0.0), (1.0, 0.0))] oprob = ODEProblem(dsrs, u0, tspan, ps) - @test isapprox(solve(oprob, Tsit5()).u[end][125], n^3, rtol=1e-6) + @test isapprox(solve(oprob, Tsit5()).u[end][125], n^3, rtol = 1.0e-6) end # Checks that erroneous input yields errors. let rn = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X end tr = @transport_reaction D X tspan = (0.0, 10000.0) @@ -409,13 +413,13 @@ let # Graph grids. dsrs = DiscreteSpaceReactionSystem(rn, [tr], path_graph(5)) - @test_throws Exception make_edge_p_values(dsrs, make_edge_p_value,) + @test_throws Exception make_edge_p_values(dsrs, make_edge_p_value) @test_throws Exception make_directed_edge_values(dsrs, (1.0, 0.0)) # Wrong dimensions to `make_directed_edge_values`. dsrs_1d = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid(5)) - dsrs_2d = DiscreteSpaceReactionSystem(rn, [tr], fill(true,5,5)) - dsrs_3d = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid((5,5,5))) + dsrs_2d = DiscreteSpaceReactionSystem(rn, [tr], fill(true, 5, 5)) + dsrs_3d = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid((5, 5, 5))) @test_throws Exception make_directed_edge_values(dsrs_1d, (1.0, 0.0), (1.0, 0.0)) @test_throws Exception make_directed_edge_values(dsrs_1d, (1.0, 0.0), (1.0, 0.0), (1.0, 0.0)) diff --git a/test/spatial_modelling/dspace_reaction_systems_ODEs.jl b/test/spatial_modelling/dspace_reaction_systems_ODEs.jl index c3eff6c156..9772f5f0f8 100644 --- a/test/spatial_modelling/dspace_reaction_systems_ODEs.jl +++ b/test/spatial_modelling/dspace_reaction_systems_ODEs.jl @@ -55,8 +55,8 @@ let dsrs = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_2, very_small_2d_cartesian_grid) u0 = [:S => 990.0, :I => rand_v_vals(dsrs), :R => 0.0] ps_1 = [:α => 0.1, :β => 0.01, :dS => 0.01, :dI => 0.01, :dR => 0.01] - ps_2 = [:α => 1//10, :β => 1//100, :dS => 1//100, :dI => 1//100, :dR => 1//100] - ps_3 = [:α => 1//10, :β => 0.01, :dS => 0.01, :dI => 1//100, :dR => 0.01] + ps_2 = [:α => 1 // 10, :β => 1 // 100, :dS => 1 // 100, :dI => 1 // 100, :dR => 1 // 100] + ps_3 = [:α => 1 // 10, :β => 0.01, :dS => 0.01, :dI => 1 // 100, :dR => 0.01] sol_base = solve(ODEProblem(dsrs, u0, (0.0, 100.0), ps_1), Rosenbrock23(); saveat = 0.1) for ps in [ps_1, ps_2, ps_3] for jac in [true, false], sparse in [true, false] @@ -72,19 +72,19 @@ let rs = @reaction_network begin pX, 0 --> X d, X --> 0 - pY*X, 0 --> Y + pY * X, 0 --> Y d, Y --> 0 end tr = @transport_reaction D X space = path_graph(3) - dsrs = DiscreteSpaceReactionSystem(rs, [tr], space); + dsrs = DiscreteSpaceReactionSystem(rs, [tr], space) - D_vals = spzeros(3,3) - D_vals[1,2] = 0.2; D_vals[2,1] = 0.2; - D_vals[2,3] = 0.3; D_vals[3,2] = 0.3; + D_vals = spzeros(3, 3) + D_vals[1, 2] = 0.2; D_vals[2, 1] = 0.2 + D_vals[2, 3] = 0.3; D_vals[3, 2] = 0.3 u0 = [:X => [1.0, 2.0, 3.0], :Y => 1.0] ps = [:pX => [2.0, 2.5, 3.0], :d => 0.1, :pY => 0.5, :D => D_vals] - oprob = ODEProblem(dsrs, u0, (0.0, 0.0), ps; jac=true, sparse=true) + oprob = ODEProblem(dsrs, u0, (0.0, 0.0), ps; jac = true, sparse = true) # Creates manual f and jac functions. function f_manual!(du, u, p, t) @@ -93,14 +93,14 @@ let pX1, pX2, pX3 = pX pY, = pY d, = d - D1 = D_vals[1,2]; D2 = D_vals[2,1]; - D3 = D_vals[2,3]; D4 = D_vals[3,2]; - du[1] = pX1 - d*X1 - D1*X1 + D2*X2 - du[2] = pY*X1 - d*Y1 - du[3] = pX2 - d*X2 + D1*X1 - (D2+D3)*X2 + D4*X3 - du[4] = pY*X2 - d*Y2 - du[5] = pX3 - d*X3 + D3*X2 - D4*X3 - du[6] = pY*X3 - d*Y3 + D1 = D_vals[1, 2]; D2 = D_vals[2, 1] + D3 = D_vals[2, 3]; D4 = D_vals[3, 2] + du[1] = pX1 - d * X1 - D1 * X1 + D2 * X2 + du[2] = pY * X1 - d * Y1 + du[3] = pX2 - d * X2 + D1 * X1 - (D2 + D3) * X2 + D4 * X3 + du[4] = pY * X2 - d * Y2 + du[5] = pX3 - d * X3 + D3 * X2 - D4 * X3 + return du[6] = pY * X3 - d * Y3 end function jac_manual!(J, u, p, t) X1, Y1, X2, Y2, X3, Y3 = u @@ -108,30 +108,30 @@ let pX1, pX2, pX3 = pX pY, = pY d, = d - D1 = D_vals[1,2]; D2 = D_vals[2,1]; - D3 = D_vals[2,3]; D4 = D_vals[3,2]; + D1 = D_vals[1, 2]; D2 = D_vals[2, 1] + D3 = D_vals[2, 3]; D4 = D_vals[3, 2] J .= 0.0 - J[1,1] = - d - D1 - J[1,2] = 0 - J[2,1] = pY - J[2,2] = - d - - J[3,3] = - d - D2 - D3 - J[3,4] = 0 - J[4,3] = pY - J[4,4] = - d - - J[5,5] = - d - D4 - J[5,6] = 0 - J[6,5] = pY - J[6,6] = - d - - J[1,3] = D1 - J[3,1] = D2 - J[3,5] = D3 - J[5,3] = D4 + J[1, 1] = - d - D1 + J[1, 2] = 0 + J[2, 1] = pY + J[2, 2] = - d + + J[3, 3] = - d - D2 - D3 + J[3, 4] = 0 + J[4, 3] = pY + J[4, 4] = - d + + J[5, 5] = - d - D4 + J[5, 6] = 0 + J[6, 5] = pY + J[6, 6] = - d + + J[1, 3] = D1 + J[3, 1] = D2 + J[3, 5] = D3 + return J[5, 3] = D4 end # Sets test input values. @@ -159,7 +159,7 @@ let u0 = [ :X => 1.0 .+ rand_v_vals(dsrs), :Y => 2.0 * rand_v_vals(dsrs), - :XY => 0.5 + :XY => 0.5, ] oprob = ODEProblem(dsrs, u0, (0.0, 1000.0), binding_p; tstops = 0.1:0.1:1000.0) ss = solve(oprob, Tsit5()).u[end] @@ -180,10 +180,10 @@ let oprob_jac = ODEProblem(dsrs, u0, (0.0, 5.0), [pV; pE]; jac = true, sparse = false) oprob_sparse_jac = ODEProblem(dsrs, u0, (0.0, 5.0), [pV; pE]; jac = true, sparse = true) - ss = solve(oprob, Rosenbrock23(); abstol = 1e-10, reltol = 1e-10).u[end] - @test all(isapprox.(ss, solve(oprob_sparse, Rosenbrock23(); abstol = 1e-10, reltol = 1e-10).u[end]; rtol = 0.0001)) - @test all(isapprox.(ss, solve(oprob_jac, Rosenbrock23(); abstol = 1e-10, reltol = 1e-10).u[end]; rtol = 0.0001)) - @test all(isapprox.(ss, solve(oprob_sparse_jac, Rosenbrock23(); abstol = 1e-10, reltol = 1e-10).u[end]; rtol = 0.0001)) + ss = solve(oprob, Rosenbrock23(); abstol = 1.0e-10, reltol = 1.0e-10).u[end] + @test all(isapprox.(ss, solve(oprob_sparse, Rosenbrock23(); abstol = 1.0e-10, reltol = 1.0e-10).u[end]; rtol = 0.0001)) + @test all(isapprox.(ss, solve(oprob_jac, Rosenbrock23(); abstol = 1.0e-10, reltol = 1.0e-10).u[end]; rtol = 0.0001)) + @test all(isapprox.(ss, solve(oprob_sparse_jac, Rosenbrock23(); abstol = 1.0e-10, reltol = 1.0e-10).u[end]; rtol = 0.0001)) end # Compares Catalyst-generated to hand-written one for the Brusselator for a line of cells. @@ -201,6 +201,7 @@ let for i in 3:2:(length(u) - 3) du[i] += p[3] * (u[i - 2] + u[i + 2] - 2u[i]) end + return end function spatial_brusselator_jac(J, u, p, t) J .= 0 @@ -222,6 +223,7 @@ let J[i, i - 2] += p[3] J[i, i + 2] += p[3] end + return end function spatial_brusselator_jac_sparse(J, u, p, t) # Spatial @@ -242,7 +244,7 @@ let J.nzval[end - 3] = u[end - 1] * u[end] - 1 - p[end - 1] J.nzval[end - 2] = 0.5 * (u[end - 1]^2) J.nzval[end - 1] = p[2] - u[end - 1] * u[end] - J.nzval[end] = -0.5 * (u[end - 1]^2) + return J.nzval[end] = -0.5 * (u[end - 1]^2) end function make_jac_prototype(u0) jac_prototype_pre = zeros(length(u0), length(u0)) @@ -260,13 +262,15 @@ let end num_verts = 100 - u0 = 2 * rand(rng, 2*num_verts) + u0 = 2 * rand(rng, 2 * num_verts) p = [1.0, 4.0, 0.1] tspan = (0.0, 100.0) ofun_hw_dense = ODEFunction(spatial_brusselator_f; jac = spatial_brusselator_jac) - ofun_hw_sparse = ODEFunction(spatial_brusselator_f; jac = spatial_brusselator_jac, - jac_prototype = make_jac_prototype(u0)) + ofun_hw_sparse = ODEFunction( + spatial_brusselator_f; jac = spatial_brusselator_jac, + jac_prototype = make_jac_prototype(u0) + ) dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, path_graph(num_verts)) u0_map = [:X => u0[1:2:(end - 1)], :Y => u0[2:2:end]] @@ -307,7 +311,7 @@ end ### Test Grid Types ### # Tests that identical spaces (using different types of spaces) give identical results. -let +let # Declares the diffusion parameters. sigmaB_p_spat = [:DσB => 0.05, :Dw => 0.04, :Dv => 0.03] @@ -316,9 +320,9 @@ let dsrs1_masked = DiscreteSpaceReactionSystem(sigmaB_system, sigmaB_srs_2, very_small_1d_masked_grid) dsrs1_graph = DiscreteSpaceReactionSystem(sigmaB_system, sigmaB_srs_2, very_small_1d_graph_grid) - oprob1_cartesian = ODEProblem(dsrs1_cartesian, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) - oprob1_masked = ODEProblem(dsrs1_masked, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) - oprob1_graph = ODEProblem(dsrs1_graph, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) + oprob1_cartesian = ODEProblem(dsrs1_cartesian, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) + oprob1_masked = ODEProblem(dsrs1_masked, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) + oprob1_graph = ODEProblem(dsrs1_graph, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) @test solve(oprob1_cartesian, QNDF()) ≈ solve(oprob1_masked, QNDF()) ≈ solve(oprob1_graph, QNDF()) # 2d spaces. @@ -326,9 +330,9 @@ let dsrs2_masked = DiscreteSpaceReactionSystem(sigmaB_system, sigmaB_srs_2, very_small_2d_masked_grid) dsrs2_graph = DiscreteSpaceReactionSystem(sigmaB_system, sigmaB_srs_2, very_small_2d_graph_grid) - oprob2_cartesian = ODEProblem(dsrs2_cartesian, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) - oprob2_masked = ODEProblem(dsrs2_masked, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) - oprob2_graph = ODEProblem(dsrs2_graph, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) + oprob2_cartesian = ODEProblem(dsrs2_cartesian, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) + oprob2_masked = ODEProblem(dsrs2_masked, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) + oprob2_graph = ODEProblem(dsrs2_graph, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) @test solve(oprob2_cartesian, QNDF()) ≈ solve(oprob2_masked, QNDF()) ≈ solve(oprob2_graph, QNDF()) # 3d spaces. @@ -336,31 +340,31 @@ let dsrs3_masked = DiscreteSpaceReactionSystem(sigmaB_system, sigmaB_srs_2, very_small_3d_masked_grid) dsrs3_graph = DiscreteSpaceReactionSystem(sigmaB_system, sigmaB_srs_2, very_small_3d_graph_grid) - oprob3_cartesian = ODEProblem(dsrs3_cartesian, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) - oprob3_masked = ODEProblem(dsrs3_masked, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) - oprob3_graph = ODEProblem(dsrs3_graph, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) + oprob3_cartesian = ODEProblem(dsrs3_cartesian, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) + oprob3_masked = ODEProblem(dsrs3_masked, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) + oprob3_graph = ODEProblem(dsrs3_graph, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) @test solve(oprob3_cartesian, QNDF()) ≈ solve(oprob3_masked, QNDF()) ≈ solve(oprob3_graph, QNDF()) end # Tests that input parameter and u0 values can be given using different types of input for 2d spaces. -# Tries both for cartesian and masked (where all vertices are `true`). +# Tries both for cartesian and masked (where all vertices are `true`). # Tries for Vector, Tuple, and Dictionary inputs. let - for space in [CartesianGrid((4,3)), fill(true, 4, 3)] + for space in [CartesianGrid((4, 3)), fill(true, 4, 3)] dsrs = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_1, space) # Initial condition values. - S_vals_vec = [100., 100., 200., 300., 200., 100., 200., 300., 300., 100., 200., 300.] - S_vals_mat = [100. 200. 300.; 100. 100. 100.; 200. 200. 200.; 300. 300. 300.] + S_vals_vec = [100.0, 100.0, 200.0, 300.0, 200.0, 100.0, 200.0, 300.0, 300.0, 100.0, 200.0, 300.0] + S_vals_mat = [100.0 200.0 300.0; 100.0 100.0 100.0; 200.0 200.0 200.0; 300.0 300.0 300.0] SIR_u0_vec = [:S => S_vals_vec, :I => 1.0, :R => 0.0] SIR_u0_mat = [:S => S_vals_mat, :I => 1.0, :R => 0.0] - + # Parameter values. β_vals_vec = [0.01, 0.01, 0.02, 0.03, 0.02, 0.01, 0.02, 0.03, 0.03, 0.01, 0.02, 0.03] β_vals_mat = [0.01 0.02 0.03; 0.01 0.01 0.01; 0.02 0.02 0.02; 0.03 0.03 0.03] SIR_p_vec = [:α => 0.1 / 1000, :β => β_vals_vec, :dS => 0.01] SIR_p_mat = [:α => 0.1 / 1000, :β => β_vals_mat, :dS => 0.01] - + oprob = ODEProblem(dsrs, SIR_u0_vec, (0.0, 10.0), SIR_p_vec) sol_base = solve(oprob, Tsit5()) for u0_base in [SIR_u0_vec, SIR_u0_mat], ps_base in [SIR_p_vec, SIR_p_mat] @@ -377,7 +381,7 @@ end let space = [true true false; true false false; true true true; false true true] dsrs = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_1, space) - + # Initial condition values. 999 is used for empty points. S_vals_vec = [100.0, 100.0, 200.0, 200.0, 200.0, 300.0, 200.0, 300.0] S_vals_mat = [100.0 200.0 999.0; 100.0 999.0 999.0; 200.0 200.0 200.0; 999.0 300.0 300.0] @@ -385,15 +389,15 @@ let SIR_u0_vec = [:S => S_vals_vec, :I => 1.0, :R => 0.0] SIR_u0_mat = [:S => S_vals_mat, :I => 1.0, :R => 0.0] SIR_u0_sparse_mat = [:S => S_vals_sparse_mat, :I => 1.0, :R => 0.0] - - # Parameter values. 9.99 is used for empty points. + + # Parameter values. 9.99 is used for empty points. β_vals_vec = [0.01, 0.01, 0.02, 0.02, 0.02, 0.03, 0.02, 0.03] β_vals_mat = [0.01 0.02 9.99; 0.01 9.99 9.99; 0.02 0.02 0.02; 9.99 0.03 0.03] β_vals_sparse_mat = sparse(β_vals_mat .* space) SIR_p_vec = [:α => 0.1 / 1000, :β => β_vals_vec, :dS => 0.01] SIR_p_mat = [:α => 0.1 / 1000, :β => β_vals_mat, :dS => 0.01] SIR_p_sparse_mat = [:α => 0.1 / 1000, :β => β_vals_sparse_mat, :dS => 0.01] - + oprob = ODEProblem(dsrs, SIR_u0_vec, (0.0, 10.0), SIR_p_vec) sol = solve(oprob, Tsit5()) for u0 in [SIR_u0_vec, SIR_u0_mat, SIR_u0_sparse_mat] @@ -425,19 +429,19 @@ let end # Tries non-trivial diffusion rates. -let - SIR_tr_S_alt = @transport_reaction dS1+dS2 S - SIR_tr_I_alt = @transport_reaction dI1*dI2 I - SIR_tr_R_alt = @transport_reaction log(dR1)+dR2 R +let + SIR_tr_S_alt = @transport_reaction dS1 + dS2 S + SIR_tr_I_alt = @transport_reaction dI1 * dI2 I + SIR_tr_R_alt = @transport_reaction log(dR1) + dR2 R SIR_srs_2_alt = [SIR_tr_S_alt, SIR_tr_I_alt, SIR_tr_R_alt] dsrs_1 = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_2, small_2d_graph_grid) dsrs_2 = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_2_alt, small_2d_graph_grid) - + u0 = [:S => 990.0, :I => 20.0 * rand_v_vals(dsrs_1), :R => 0.0] pV = [:α => 0.1 / 1000, :β => 0.01] pE_1 = [:dS => 0.01, :dI => 0.01, :dR => 0.01] pE_2 = [:dS1 => 0.003, :dS2 => 0.007, :dI1 => 2, :dI2 => 0.005, :dR1 => 1.010050167084168, :dR2 => 1.0755285551056204e-16] - + ss_1 = solve(ODEProblem(dsrs_1, u0, (0.0, 500.0), [pV; pE_1]), Tsit5()).u[end] ss_2 = solve(ODEProblem(dsrs_2, u0, (0.0, 500.0), [pV; pE_2]), Tsit5()).u[end] @test ss_1 == ss_2 @@ -447,7 +451,7 @@ end let CuH_Amination_system_alt_1 = @reaction_network begin @species Newspecies1(t) Newspecies2(t) - @parameters dCuoAc [edgeparameter=true] dLigand dSilane dStyrene dCu_ELigand + @parameters dCuoAc [edgeparameter = true] dLigand dSilane dStyrene dCu_ELigand 10.0^kp1, CuoAc + Ligand --> CuoAcLigand 10.0^kp2, CuoAcLigand + Silane --> CuHLigand + SilaneOAc 10.0^k1, CuHLigand + Styrene --> AlkylCuLigand @@ -460,7 +464,7 @@ let end @unpack dLigand, dSilane, Silane = CuH_Amination_system_alt_1 @parameters dAmine_E dNewspecies1 - @species Ligand(t) Amine_E(t) Newspecies1(t) + @species Ligand(t) Amine_E(t) Newspecies1(t) tr_alt_1_1 = TransportReaction(dLigand, Ligand) tr_alt_1_2 = TransportReaction(dSilane, Silane) tr_alt_1_3 = TransportReaction(dAmine_E, Amine_E) @@ -473,7 +477,7 @@ let CuH_Amination_system_alt_2 = @reaction_network begin @species Newspecies1(t) Newspecies2(t) - @parameters dCuoAc [edgeparameter=true] dLigand dSilane dStyrene dCu_ELigand + @parameters dCuoAc [edgeparameter = true] dLigand dSilane dStyrene dCu_ELigand 10.0^kp1, CuoAc + Ligand --> CuoAcLigand 10.0^kp2, CuoAcLigand + Silane --> CuHLigand + SilaneOAc 10.0^k1, CuHLigand + Styrene --> AlkylCuLigand @@ -484,9 +488,9 @@ let 10.0^kam, CuHLigand + Amine_E --> Amine + Cu_ELigand 10.0^kdc, CuHLigand + CuHLigand --> Decomposition end - @unpack Decomposition, dCu_ELigand, Cu_ELigand = CuH_Amination_system_alt_2 + @unpack Decomposition, dCu_ELigand, Cu_ELigand = CuH_Amination_system_alt_2 @parameters dNewspecies2 dDecomposition - @species Newspecies2(t) + @species Newspecies2(t) tr_alt_2_1 = @transport_reaction dLigand Ligand tr_alt_2_2 = @transport_reaction dSilane Silane tr_alt_2_3 = @transport_reaction dAmine_E Amine_E @@ -496,10 +500,10 @@ let tr_alt_2_7 = TransportReaction(dNewspecies2, Newspecies2) CuH_Amination_srs_alt_2 = [tr_alt_2_1, tr_alt_2_2, tr_alt_2_3, tr_alt_2_4, tr_alt_2_5, tr_alt_2_6, tr_alt_2_7] dsrs_2 = DiscreteSpaceReactionSystem(CuH_Amination_system_alt_2, CuH_Amination_srs_alt_2, small_2d_graph_grid) - + u0 = [CuH_Amination_u0; :Newspecies1 => 0.1; :Newspecies2 => 0.1] - pV = [CuH_Amination_p; :dLigand => 0.01; :dSilane => 0.01; :dCu_ELigand => 0.009; :dStyrene => -10000.0] - pE = [:dAmine_E => 0.011, :dNewspecies1 => 0.013, :dDecomposition => 0.015, :dNewspecies2 => 0.016, :dCuoAc => -10000.0] + pV = [CuH_Amination_p; :dLigand => 0.01; :dSilane => 0.01; :dCu_ELigand => 0.009; :dStyrene => -10000.0] + pE = [:dAmine_E => 0.011, :dNewspecies1 => 0.013, :dDecomposition => 0.015, :dNewspecies2 => 0.016, :dCuoAc => -10000.0] ss_1 = solve(ODEProblem(dsrs_1, u0, (0.0, 500.0), [pV; pE]), Tsit5()).u[end] ss_2 = solve(ODEProblem(dsrs_2, u0, (0.0, 500.0), [pV; pE]), Tsit5()).u[end] @@ -514,7 +518,7 @@ let dsrs_graph = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_2, complete_graph(3)) u0 = [:S => 990.0, :I => 20.0 * rand_v_vals(dsrs_digraph), :R => 0.0] pV = SIR_p - pE = [:dS => 0.10, :dI => 0.01, :dR => 0.01] + pE = [:dS => 0.1, :dI => 0.01, :dR => 0.01] oprob_digraph = ODEProblem(dsrs_digraph, u0, (0.0, 500.0), [pV; pE]) oprob_graph = ODEProblem(dsrs_graph, u0, (0.0, 500.0), [pV; pE]) @@ -555,18 +559,18 @@ let :p2 => rand_v_vals(dsrs_alt), ] oprob_alt = ODEProblem(dsrs_alt, u0_alt, (0.0, 10.0), p_alt) - ss_alt = solve(oprob_alt, Tsit5(); abstol=1e-9, reltol=1e-9).u[end] - + ss_alt = solve(oprob_alt, Tsit5(); abstol = 1.0e-9, reltol = 1.0e-9).u[end] + binding_srs_main = [TransportReaction(dX, X), TransportReaction(dXY, XY)] dsrs = DiscreteSpaceReactionSystem(binding_system, binding_srs_main, small_2d_graph_grid) u0 = u0_alt[1:3] p = p_alt[1:4] oprob = ODEProblem(dsrs, u0, (0.0, 10.0), p) - ss = solve(oprob, Tsit5(); abstol=1e-9, reltol=1e-9).u[end] - + ss = solve(oprob, Tsit5(); abstol = 1.0e-9, reltol = 1.0e-9).u[end] + i = 3 ss_alt[((i - 1) * 6 + 1):((i - 1) * 6 + 3)] ≈ ss[((i - 1) * 3 + 1):((i - 1) * 3 + 3)] - + for i in 1:25 @test ss_alt[((i - 1) * 6 + 1):((i - 1) * 6 + 3)] ≈ ss[((i - 1) * 3 + 1):((i - 1) * 3 + 3)] end @@ -579,8 +583,8 @@ let dsrs = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_2, very_small_2d_cartesian_grid) u0 = [:S => 990.0, :I => rand_v_vals(dsrs), :R => 0.0] ps_1 = [:α => 0.1, :β => 0.01, :dS => 0.01, :dI => 0.01, :dR => 0.01] - ps_2 = [:α => 1//10, :β => 1//100, :dS => 1//100, :dI => 1//100, :dR => 1//100] - ps_3 = [:α => 1//10, :β => 0.01, :dS => 0.01, :dI => 1//100, :dR => 0.01] + ps_2 = [:α => 1 // 10, :β => 1 // 100, :dS => 1 // 100, :dI => 1 // 100, :dR => 1 // 100] + ps_3 = [:α => 1 // 10, :β => 0.01, :dS => 0.01, :dI => 1 // 100, :dR => 0.01] sol_base = solve(ODEProblem(dsrs, u0, (0.0, 100.0), ps_1), Rosenbrock23(); saveat = 0.1) for ps in [ps_1, ps_2, ps_3] for jac in [true, false], sparse in [true, false] @@ -596,19 +600,19 @@ let u0_Int64 = [:X => 2, :Y => [1, 1, 1, 2]] u0_Float64 = [:X => 2.0, :Y => [1.0, 1.0, 1.0, 2.0]] u0_Int32 = [:X => Int32(2), :Y => Int32.([1, 1, 1, 2])] - u0_Any = Pair{Symbol,Any}[:X => 2.0, :Y => [1.0, 1.0, 1.0, 2.0]] + u0_Any = Pair{Symbol, Any}[:X => 2.0, :Y => [1.0, 1.0, 1.0, 2.0]] u0s = (u0_Int64, u0_Float64, u0_Int32, u0_Any) # Declare parameter versions. - dY_vals = spzeros(4,4) - dY_vals[1,2] = 1; dY_vals[2,1] = 1; - dY_vals[1,3] = 1; dY_vals[3,1] = 1; - dY_vals[2,4] = 1; dY_vals[4,2] = 1; - dY_vals[3,4] = 2; dY_vals[4,3] = 2; + dY_vals = spzeros(4, 4) + dY_vals[1, 2] = 1; dY_vals[2, 1] = 1 + dY_vals[1, 3] = 1; dY_vals[3, 1] = 1 + dY_vals[2, 4] = 1; dY_vals[4, 2] = 1 + dY_vals[3, 4] = 2; dY_vals[4, 3] = 2 p_Int64 = (:A => [1, 1, 1, 2], :B => 4, :dX => 1, :dY => Int64.(dY_vals)) p_Float64 = (:A => [1.0, 1.0, 1.0, 2.0], :B => 4.0, :dX => 1.0, :dY => Float64.(dY_vals)) p_Int32 = (:A => Int32.([1, 1, 1, 2]), :B => Int32(4), :dX => Int32(1), :dY => Int32.(dY_vals)) - p_Any = Pair{Symbol,Any}[:A => [1.0, 1.0, 1.0, 2.0], :B => 4.0, :dX => 1.0, :dY => dY_vals] + p_Any = Pair{Symbol, Any}[:A => [1.0, 1.0, 1.0, 2.0], :B => 4.0, :dX => 1.0, :dY => dY_vals] ps = (p_Int64, p_Float64, p_Int32, p_Any) # Creates a base solution to compare all solution to. @@ -622,7 +626,7 @@ let for u0 in [u0_base, Tuple(u0_base), Dict(u0_base)], p in [p_base, Dict(p_base)] oprob = ODEProblem(dsrs, u0, (0.0, 1.0), p; sparse = true, jac = true) sol = solve(oprob, QNDF(); saveat = 0.01) - @test sol.u ≈ sol_base.u atol = 1e-6 rtol = 1e-6 + @test sol.u ≈ sol_base.u atol = 1.0e-6 rtol = 1.0e-6 end end end @@ -639,7 +643,7 @@ end # Checks that various erroneous inputs to `ODEProblem` yields errors. let # Create `DiscreteSpaceReactionSystem`. - @parameters d1 d2 D [edgeparameter=true] + @parameters d1 d2 D [edgeparameter = true] @species X1(t) X2(t) rxs = [Reaction(d1, [X1], [])] @named rs = ReactionSystem(rxs, t) @@ -664,5 +668,5 @@ let @test_throws ArgumentError ODEProblem(dsrs, u0, tspan, [d1 => 1.0, D => bad_D_vals_2]) @test_throws ArgumentError ODEProblem(dsrs, u0, tspan, [d1 => 1.0]) @test_throws ArgumentError ODEProblem(dsrs, [X1 => [1.0, 2.0, 3.0]], tspan, ps) - @test_throws ArgumentError ODEProblem(dsrs, [X1 => ones(3,1)], tspan, ps) + @test_throws ArgumentError ODEProblem(dsrs, [X1 => ones(3, 1)], tspan, ps) end diff --git a/test/spatial_modelling/dspace_reaction_systems_jumps.jl b/test/spatial_modelling/dspace_reaction_systems_jumps.jl index 0dd2bd34ac..fa655ed6c9 100644 --- a/test/spatial_modelling/dspace_reaction_systems_jumps.jl +++ b/test/spatial_modelling/dspace_reaction_systems_jumps.jl @@ -30,7 +30,7 @@ let ] for pV in [pV_1, pV_2, pV_3] pE_1 = [sp => 0.01 for sp in spatial_param_syms(dsrs)] - pE_2 = [sp => rand_e_vals(dsrs)/50.0 for sp in spatial_param_syms(dsrs)] + pE_2 = [sp => rand_e_vals(dsrs) / 50.0 for sp in spatial_param_syms(dsrs)] for pE in [pE_1, pE_2] isempty(spatial_param_syms(dsrs)) && (pE = Vector{Pair{Symbol, Float64}}()) jprob = JumpProblem(dsrs, u0, (0.0, 1.0), [pV; pE]) @@ -54,7 +54,7 @@ let # Prepares various u0 input types. u0_1 = [:I => 2.0, :S => 1.0, :R => 3.0] - u0_2 = [:I => fill(2., nv(grid)), :S => 1.0, :R => 3.0] + u0_2 = [:I => fill(2.0, nv(grid)), :S => 1.0, :R => 3.0] # Prepare various (compartment) parameter input types. pV_1 = [:β => 0.2, :α => 0.1] @@ -68,7 +68,7 @@ let # Checks hopping rates and u0 are correct. true_u0 = [fill(1.0, 1, 25); fill(2.0, 1, 25); fill(3.0, 1, 25)] - true_hopping_rates = cumsum.([fill(dval, length(v)) for dval in [0.01,0.02,0.03], v in grid.fadjlist]) + true_hopping_rates = cumsum.([fill(dval, length(v)) for dval in [0.01, 0.02, 0.03], v in grid.fadjlist]) true_maj_scaled_rates = [0.1, 0.2] true_maj_reactant_stoch = [[1 => 1, 2 => 1], [2 => 1]] true_maj_net_stoch = [[1 => -1, 2 => 1], [2 => -1, 3 => 1]] @@ -77,7 +77,7 @@ let jprob = JumpProblem(dsrs, u0, (0.0, 100.0), [pE; pV]) @test jprob.prob.u0 == true_u0 @test jprob.discrete_jump_aggregation.hop_rates.hop_const_cumulative_sums == true_hopping_rates - @test jprob.massaction_jump.reactant_stoch == true_maj_reactant_stoch + @test jprob.massaction_jump.reactant_stoch == true_maj_reactant_stoch @test all(issetequal(ns1, ns2) for (ns1, ns2) in zip(jprob.massaction_jump.net_stoch, true_maj_net_stoch)) end end @@ -87,7 +87,7 @@ end ### SpatialMassActionJump Testing ### # Checks that the correct structures are produced. -let +let # Network for reference: # A, ∅ → X # 1, 2X + Y → 3X @@ -104,8 +104,8 @@ let jprob = JumpProblem(dsrs, u0, tspan, ps) # Checks internal structures. - jprob.massaction_jump.uniform_rates == [1.0, 0.5 ,10.] # 0.5 is due to combinatoric /2! in (2X + Y). - jprob.massaction_jump.spatial_rates[1,:] == ps[2][2] + jprob.massaction_jump.uniform_rates == [1.0, 0.5, 10.0] # 0.5 is due to combinatoric /2! in (2X + Y). + jprob.massaction_jump.spatial_rates[1, :] == ps[2][2] # Test when new SII functions are ready, or we implement them in Catalyst. # @test isequal(to_int(getfield.(reactions(reactionsystem(dsrs)), :netstoich)), jprob.massaction_jump.net_stoch) # @test isequal(to_int(Pair.(getfield.(reactions(reactionsystem(dsrs)), :substrates),getfield.(reactions(reactionsystem(dsrs)), :substoich))), jprob.massaction_jump.net_stoch) @@ -116,24 +116,24 @@ end # Checks that heterogeneous vertex parameters work. Checks that birth-death system with different # birth rates produce different means. -let +let # Create model. birth_death_network = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X end srs = [(@transport_reaction D X)] dsrs = DiscreteSpaceReactionSystem(birth_death_network, srs, very_small_2d_graph_grid) - + # Create JumpProblem. u0 = [:X => 1] tspan = (0.0, 100.0) - ps = [:p => [0.1, 1.0, 10.0, 100.0], :d => 1.0, :D => 0.0] + ps = [:p => [0.1, 1.0, 10.0, 100.0], :d => 1.0, :D => 0.0] jprob = JumpProblem(dsrs, u0, tspan, ps) # Simulate model (a few repeats to ensure things don't succeed by change for uniform rates). # Check that higher p gives higher mean. - for i = 1:5 - sol = solve(jprob, SSAStepper(); saveat = 1.) + for i in 1:5 + sol = solve(jprob, SSAStepper(); saveat = 1.0) @test mean(getindex.(sol.u, 1)) < mean(getindex.(sol.u, 2)) < mean(getindex.(sol.u, 3)) < mean(getindex.(sol.u, 4)) end end @@ -142,7 +142,7 @@ end ### Tests taken from JumpProcesses ### # ABC Model Test -let +let # Preparations (stuff used in JumpProcesses examples ported over here, could be written directly into code). Nsims = 100 reltol = 0.05 @@ -159,7 +159,7 @@ let # Make model. rn = @reaction_network begin - (kB,kD), A + B <--> C + (kB, kD), A + B <--> C end tr_1 = @transport_reaction D A tr_2 = @transport_reaction D B @@ -168,13 +168,17 @@ let dsrs = DiscreteSpaceReactionSystem(rn, [tr_1, tr_2, tr_3], space) # Set simulation parameters and create problems. - u0 = [:A => [0,0,500,0,0], :B => [0,0,500,0,0], :C => 0] + u0 = [:A => [0, 0, 500, 0, 0], :B => [0, 0, 500, 0, 0], :C => 0] tspan = (0.0, 10.0) pV = [:kB => rates[1], :kD => rates[2]] pE = [:D => diffusivity] # NRM could be added, but doesn't work. Might need Cartesian grid. - jump_problems = [JumpProblem(dsrs, u0, tspan, [pV; pE]; aggregator = alg(), - save_positions = (false, false)) for alg in [NSM, DirectCRDirect]] + jump_problems = [ + JumpProblem( + dsrs, u0, tspan, [pV; pE]; aggregator = alg(), + save_positions = (false, false) + ) for alg in [NSM, DirectCRDirect] + ] # Run tests. function get_mean_end_state(jump_prob, Nsims) @@ -183,7 +187,7 @@ let sol = solve(jump_prob, SSAStepper()) end_state .+= sol.u[end] end - end_state / Nsims + return end_state / Nsims end for jprob in jump_problems solution = solve(jprob, SSAStepper()) @@ -194,4 +198,4 @@ let @test abs(d) < reltol * non_spatial_mean[i] end end -end \ No newline at end of file +end diff --git a/test/spatial_modelling/dspace_reaction_systems_space_types.jl b/test/spatial_modelling/dspace_reaction_systems_space_types.jl index e6cc8879f5..32ca1f8afc 100644 --- a/test/spatial_modelling/dspace_reaction_systems_space_types.jl +++ b/test/spatial_modelling/dspace_reaction_systems_space_types.jl @@ -10,13 +10,13 @@ include("../spatial_test_networks.jl") ### Run Tests ### # Test errors when attempting to create networks with dimensions > 3. -let +let @test_throws Exception DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, CartesianGrid((5, 5, 5, 5))) @test_throws Exception DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, fill(true, 5, 5, 5, 5)) end # Checks that getter functions give the correct output. -let +let # Create DiscreteSpaceReactionSystems. cartesian_1d_dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, small_1d_cartesian_grid) cartesian_2d_dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, small_2d_cartesian_grid) @@ -54,23 +54,23 @@ let # Checks grid sizes. @test grid_size(cartesian_1d_dsrs) == (5,) - @test grid_size(cartesian_2d_dsrs) == (5,5) - @test grid_size(cartesian_3d_dsrs) == (5,5,5) + @test grid_size(cartesian_2d_dsrs) == (5, 5) + @test grid_size(cartesian_3d_dsrs) == (5, 5, 5) @test grid_size(masked_1d_dsrs) == (5,) - @test grid_size(masked_2d_dsrs) == (5,5) - @test grid_size(masked_3d_dsrs) == (5,5,5) + @test grid_size(masked_2d_dsrs) == (5, 5) + @test grid_size(masked_3d_dsrs) == (5, 5, 5) @test_throws ArgumentError grid_size(graph_dsrs) end # Checks grid dimensions for 2d and 3d grids where some dimension is equal to 1. let # Creates DiscreteSpaceReactionSystems - cartesian_2d_dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, CartesianGrid((1,5))) - cartesian_3d_dsrs_1 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, CartesianGrid((1,5,5))) - cartesian_3d_dsrs_2 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, CartesianGrid((1,1,5))) + cartesian_2d_dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, CartesianGrid((1, 5))) + cartesian_3d_dsrs_1 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, CartesianGrid((1, 5, 5))) + cartesian_3d_dsrs_2 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, CartesianGrid((1, 1, 5))) masked_2d_dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, fill(true, 1, 5)) - masked_3d_dsrs_1 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, fill(true, 1, 5,5)) - masked_3d_dsrs_2 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, fill(true, 1, 1,5)) + masked_3d_dsrs_1 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, fill(true, 1, 5, 5)) + masked_3d_dsrs_2 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, fill(true, 1, 1, 5)) # Check grid dimensions. @test grid_dims(cartesian_2d_dsrs) == 2 @@ -83,7 +83,7 @@ end # Checks that some grids, created using different approaches, generates the same spatial structures. # Checks that some grids, created using different approaches, generates the same simulation output. -let +let # Create DiscreteSpaceReactionSystems. cartesian_grid = CartesianGrid((5, 5)) masked_grid = fill(true, 5, 5) @@ -100,8 +100,8 @@ let @test num_verts(cartesian_dsrs) == num_verts(masked_dsrs) == num_verts(graph_dsrs) @test num_edges(cartesian_dsrs) == num_edges(masked_dsrs) == num_edges(graph_dsrs) @test num_species(cartesian_dsrs) == num_species(masked_dsrs) == num_species(graph_dsrs) - @test isequal(spatial_species(cartesian_dsrs), spatial_species(masked_dsrs)) - @test isequal(spatial_species(masked_dsrs), spatial_species(graph_dsrs)) + @test isequal(spatial_species(cartesian_dsrs), spatial_species(masked_dsrs)) + @test isequal(spatial_species(masked_dsrs), spatial_species(graph_dsrs)) @test isequal(parameters(cartesian_dsrs), parameters(masked_dsrs)) @test isequal(parameters(masked_dsrs), parameters(graph_dsrs)) @test isequal(vertex_parameters(cartesian_dsrs), vertex_parameters(masked_dsrs)) @@ -124,9 +124,9 @@ let masked_oprob = ODEProblem(masked_dsrs, u0_masked, (0.0, 100.0), [pV_masked; pE]) graph_oprob = ODEProblem(graph_dsrs, u0_graph, (0.0, 100.0), [pV_graph; pE]) - cartesian_sol = solve(cartesian_oprob, FBDF(); saveat=0.1) - masked_sol = solve(masked_oprob, FBDF(); saveat=0.1) - graph_sol = solve(graph_oprob, FBDF(); saveat=0.1) + cartesian_sol = solve(cartesian_oprob, FBDF(); saveat = 0.1) + masked_sol = solve(masked_oprob, FBDF(); saveat = 0.1) + graph_sol = solve(graph_oprob, FBDF(); saveat = 0.1) @test cartesian_sol.u == masked_sol.u == graph_sol.u end @@ -136,41 +136,41 @@ let # Create DiscreteSpaceReactionSystems. masked_grid = [true true true; true false true; true true true] graph_grid = SimpleGraph(8) - add_edge!(graph_grid, 1, 2); add_edge!(graph_grid, 2, 1); - add_edge!(graph_grid, 2, 3); add_edge!(graph_grid, 3, 2); - add_edge!(graph_grid, 3, 5); add_edge!(graph_grid, 5, 3); - add_edge!(graph_grid, 5, 8); add_edge!(graph_grid, 8, 5); - add_edge!(graph_grid, 8, 7); add_edge!(graph_grid, 7, 8); - add_edge!(graph_grid, 7, 6); add_edge!(graph_grid, 6, 7); - add_edge!(graph_grid, 6, 4); add_edge!(graph_grid, 4, 6); - add_edge!(graph_grid, 4, 1); add_edge!(graph_grid, 1, 4); + add_edge!(graph_grid, 1, 2); add_edge!(graph_grid, 2, 1) + add_edge!(graph_grid, 2, 3); add_edge!(graph_grid, 3, 2) + add_edge!(graph_grid, 3, 5); add_edge!(graph_grid, 5, 3) + add_edge!(graph_grid, 5, 8); add_edge!(graph_grid, 8, 5) + add_edge!(graph_grid, 8, 7); add_edge!(graph_grid, 7, 8) + add_edge!(graph_grid, 7, 6); add_edge!(graph_grid, 6, 7) + add_edge!(graph_grid, 6, 4); add_edge!(graph_grid, 4, 6) + add_edge!(graph_grid, 4, 1); add_edge!(graph_grid, 1, 4) masked_dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, masked_grid) graph_dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, graph_grid) - + # Check internal structures. @test num_verts(masked_dsrs) == num_verts(graph_dsrs) @test num_edges(masked_dsrs) == num_edges(graph_dsrs) @test issetequal(edge_iterator(masked_dsrs), edge_iterator(graph_dsrs)) - + # Checks that simulations yields the same output. - u0_masked_grid = [:X => [1. 4. 6.; 2. 0. 7.; 3. 5. 8.], :Y => 2.0] - u0_graph_grid = [:X => [1., 2., 3., 4., 5., 6., 7., 8.], :Y => 2.0] - pV_masked_grid = [:A => 0.5 .+ [1. 4. 6.; 2. 0. 7.; 3. 5. 8.], :B => 4.0] - pV_graph_grid = [:A => 0.5 .+ [1., 2., 3., 4., 5., 6., 7., 8.], :B => 4.0] + u0_masked_grid = [:X => [1.0 4.0 6.0; 2.0 0.0 7.0; 3.0 5.0 8.0], :Y => 2.0] + u0_graph_grid = [:X => [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], :Y => 2.0] + pV_masked_grid = [:A => 0.5 .+ [1.0 4.0 6.0; 2.0 0.0 7.0; 3.0 5.0 8.0], :B => 4.0] + pV_graph_grid = [:A => 0.5 .+ [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], :B => 4.0] pE = [:dX => 0.2] - + base_oprob = ODEProblem(masked_dsrs, u0_masked_grid, (0.0, 100.0), [pV_masked_grid; pE]) - base_osol = solve(base_oprob, QNDF(); saveat=0.1, abstol=1e-9, reltol=1e-9) + base_osol = solve(base_oprob, QNDF(); saveat = 0.1, abstol = 1.0e-9, reltol = 1.0e-9) for jac in [false, true], sparse in [false, true] masked_oprob = ODEProblem(masked_dsrs, u0_masked_grid, (0.0, 100.0), [pV_masked_grid; pE]; jac, sparse) graph_oprob = ODEProblem(graph_dsrs, u0_graph_grid, (0.0, 100.0), [pV_graph_grid; pE]; jac, sparse) - masked_sol = solve(masked_oprob, QNDF(); saveat=0.1, abstol=1e-9, reltol=1e-9) - graph_sol = solve(graph_oprob, QNDF(); saveat=0.1, abstol=1e-9, reltol=1e-9) - @test base_osol ≈ masked_sol atol = 1e-6 rtol = 1e-6 - @test base_osol ≈ graph_sol atol = 1e-6 rtol = 1e-6 - @test masked_sol ≈ graph_sol atol = 1e-6 rtol = 1e-6 + masked_sol = solve(masked_oprob, QNDF(); saveat = 0.1, abstol = 1.0e-9, reltol = 1.0e-9) + graph_sol = solve(graph_oprob, QNDF(); saveat = 0.1, abstol = 1.0e-9, reltol = 1.0e-9) + @test base_osol ≈ masked_sol atol = 1.0e-6 rtol = 1.0e-6 + @test base_osol ≈ graph_sol atol = 1.0e-6 rtol = 1.0e-6 + @test masked_sol ≈ graph_sol atol = 1.0e-6 rtol = 1.0e-6 end end @@ -193,19 +193,19 @@ let # Declares the grids (1d, 2d, and 3d). For each dimension, there are a 2 Cartesian grids (length 4 and 3). cart_grid_1d_1 = CartesianGrid(4) cart_grid_1d_2 = CartesianGrid(3) - cart_grid_2d_1 = CartesianGrid((4,1)) - cart_grid_2d_2 = CartesianGrid((3,1)) - cart_grid_3d_1 = CartesianGrid((1,4,1)) - cart_grid_3d_2 = CartesianGrid((1,3,1)) + cart_grid_2d_1 = CartesianGrid((4, 1)) + cart_grid_2d_2 = CartesianGrid((3, 1)) + cart_grid_3d_1 = CartesianGrid((1, 4, 1)) + cart_grid_3d_2 = CartesianGrid((1, 3, 1)) masked_grid_1d = [true, true, true, true, false, true, true, true] - masked_grid_2d = reshape(masked_grid_1d,8,1) - masked_grid_3d = reshape(masked_grid_1d,1,8,1) + masked_grid_2d = reshape(masked_grid_1d, 8, 1) + masked_grid_3d = reshape(masked_grid_1d, 1, 8, 1) - # Creaets a base solution to which we will compare all simulations. + # Creates a base solution to which we will compare all simulations. dsrs_base = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_1, masked_grid_1d) oprob_base = ODEProblem(dsrs_base, [:S => S_vals, :I => I_val, :R => R_val], (0.0, 100.0), [:α => α_vals, :β => β_val, :dS => dS_val]) - sol_base = solve(oprob_base, Tsit5(); saveat = 1.0, abstol = 1e-9, reltol = 1e-9) + sol_base = solve(oprob_base, Tsit5(); saveat = 1.0, abstol = 1.0e-9, reltol = 1.0e-9) # Checks simulations for the masked grid (covering all 7 vertices, with a gap in the middle). for grid in [masked_grid_1d, masked_grid_2d, masked_grid_3d] @@ -214,14 +214,14 @@ let u0 = [:S => [S_vals[1:4]; S_vals[6:8]], :I => I_val, :R => R_val] ps = [:α => [α_vals[1:4]; α_vals[6:8]], :β => β_val, :dS => dS_val] oprob = ODEProblem(dsrs, u0, (0.0, 100.0), ps) - sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1e-9, reltol = 1e-9) + sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1.0e-9, reltol = 1.0e-9) @test sol ≈ sol_base # Checks where the values are arrays of size equal to the grid. u0 = [:S => reshape(S_vals, grid_size(dsrs)), :I => I_val, :R => R_val] ps = [:α => reshape(α_vals, grid_size(dsrs)), :β => β_val, :dS => dS_val] oprob = ODEProblem(dsrs, u0, (0.0, 100.0), ps) - sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1e-9, reltol = 1e-9) + sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1.0e-9, reltol = 1.0e-9) @test sol ≈ sol_base end @@ -232,15 +232,15 @@ let u0 = [:S => S_vals[1:4], :I => I_val, :R => R_val] ps = [:α => α_vals[1:4], :β => β_val, :dS => dS_val] oprob = ODEProblem(dsrs, u0, (0.0, 100.0), ps) - sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1e-9, reltol = 1e-9) - @test hcat(sol.u...) ≈ sol_base[1:12,:] + sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1.0e-9, reltol = 1.0e-9) + @test hcat(sol.u...) ≈ sol_base[1:12, :] # Checks where the values are arrays of size equal to the grid. u0 = [:S => reshape(S_vals[1:4], grid_size(dsrs)), :I => I_val, :R => R_val] ps = [:α => reshape(α_vals[1:4], grid_size(dsrs)), :β => β_val, :dS => dS_val] oprob = ODEProblem(dsrs, u0, (0.0, 100.0), ps) - sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1e-9, reltol = 1e-9) - @test hcat(sol.u...) ≈ sol_base[1:12,:] + sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1.0e-9, reltol = 1.0e-9) + @test hcat(sol.u...) ≈ sol_base[1:12, :] end # Checks simulations for the second Cartesian grids (covering vertices 6 to 8). @@ -250,14 +250,14 @@ let u0 = [:S => S_vals[6:8], :I => I_val, :R => R_val] ps = [:α => α_vals[6:8], :β => β_val, :dS => dS_val] oprob = ODEProblem(dsrs, u0, (0.0, 100.0), ps) - sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1e-9, reltol = 1e-9) - @test hcat(sol.u...) ≈ sol_base[13:end,:] + sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1.0e-9, reltol = 1.0e-9) + @test hcat(sol.u...) ≈ sol_base[13:end, :] # Checks where the values are arrays of size equal to the grid. u0 = [:S => reshape(S_vals[6:8], grid_size(dsrs)), :I => I_val, :R => R_val] ps = [:α => reshape(α_vals[6:8], grid_size(dsrs)), :β => β_val, :dS => dS_val] oprob = ODEProblem(dsrs, u0, (0.0, 100.0), ps) - sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1e-9, reltol = 1e-9) - @test hcat(sol.u...) ≈ sol_base[13:end,:] + sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1.0e-9, reltol = 1.0e-9) + @test hcat(sol.u...) ≈ sol_base[13:end, :] end end diff --git a/test/spatial_modelling/dspace_simulation_struct_interfacing.jl b/test/spatial_modelling/dspace_simulation_struct_interfacing.jl index e5dbd44745..bfdccfc29a 100644 --- a/test/spatial_modelling/dspace_simulation_struct_interfacing.jl +++ b/test/spatial_modelling/dspace_simulation_struct_interfacing.jl @@ -14,7 +14,7 @@ include("../spatial_test_networks.jl") # Checks for symbol and symbolic variables input. let # Declares various types of spaces and corresponding initial values of `X`. - dspace_cartesian = CartesianGrid((2,2,2)) + dspace_cartesian = CartesianGrid((2, 2, 2)) dspace_masked = [true true; false true] dspace_graph = cycle_graph(5) val0_cartesian = fill(1.0, 2, 2, 2) @@ -35,13 +35,13 @@ let jprob = JumpProblem(dsrs, deepcopy(u0), (0.0, 1.0), ps) oint = init(deepcopy(oprob), Tsit5()) jint = init(deepcopy(jprob), SSAStepper()) - + # Check that `spat_getu` retrieves the correct values. @test spat_getu(oprob, :X, dsrs) == spat_getu(oprob, X, dsrs) == spat_getu(oprob, brusselator_system.X, dsrs) == val0 @test spat_getu(oint, :X, dsrs) == spat_getu(oint, X, dsrs) == spat_getu(oint, brusselator_system.X, dsrs) == val0 @test spat_getu(jprob, :X, dsrs) == spat_getu(jprob, X, dsrs) == spat_getu(jprob, brusselator_system.X, dsrs) == val0 @test spat_getu(jint, :X, dsrs) == spat_getu(jint, X, dsrs) == spat_getu(jint, brusselator_system.X, dsrs) == val0 - + # Updates Y and checks its content. spat_setu!(oprob, :Y, dsrs, val0) @test spat_getu(oprob, :Y, dsrs) == spat_getu(oprob, Y, dsrs) == spat_getu(oprob, brusselator_system.Y, dsrs) == val0 @@ -70,7 +70,7 @@ end # Checks for symbol and symbolic variables input. let # Declares various types of spaces and corresponding initial values of `A`. - dspace_cartesian = CartesianGrid((2,2,2)) + dspace_cartesian = CartesianGrid((2, 2, 2)) dspace_masked = [true true; false true] dspace_graph = cycle_graph(5) val0_cartesian = fill(1.0, 2, 2, 2) @@ -89,11 +89,11 @@ let ps = [:A => val0, :B => 2.0, :dX => 0.1] oprob = ODEProblem(dsrs, u0, (0.0, 1.0), deepcopy(ps)) oint = init(deepcopy(oprob), Tsit5()) - + # Check that `spat_getp` retrieves the correct values. @test spat_getp(oprob, :A, dsrs) == spat_getp(oprob, A, dsrs) == spat_getp(oprob, brusselator_system.A, dsrs) == val0 @test spat_getp(oint, :A, dsrs) == spat_getp(oint, A, dsrs) == spat_getp(oint, brusselator_system.A, dsrs) == val0 - + # Updates Y and checks its content. spat_setp!(oprob, :B, dsrs, val0) @test spat_getp(oprob, :B, dsrs) == spat_getp(oprob, B, dsrs) == spat_getp(oprob, brusselator_system.B, dsrs) == val0 @@ -137,19 +137,19 @@ end ### Simulation `spat_getu` Tests ### # Basic test. For simulations without change in system, check that the solution corresponds to known -# initial condition throughout the solution. +# initial condition throughout the solution. # Checks using both `t` sampling` and normal time step sampling. # Checks for both ODE and jump simulations. # Checks for all discrete space types. -let +let # Prepare `DiscreteSpaceReactionSystem`s. rs = @reaction_network begin - (k1,k2), X1 <--> X2 + (k1, k2), X1 <--> X2 end tr = @transport_reaction D X1 dsrs1 = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,))) - dsrs2 = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,3))) - dsrs3 = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,3,2))) + dsrs2 = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2, 3))) + dsrs3 = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2, 3, 2))) dsrs4 = DiscreteSpaceReactionSystem(rs, [tr], [true, true, false, true]) dsrs5 = DiscreteSpaceReactionSystem(rs, [tr], [true false; true true]) dsrs6 = DiscreteSpaceReactionSystem(rs, [tr], cycle_graph(4)) @@ -165,7 +165,7 @@ let ps = [:k1 => 0.0, :k2 => 0.0, :D => 0.0] # Loops through all discrete space cases and check that they are correct. - for (u0,dsrs) in zip([u0_1, u0_2, u0_3, u0_4, u0_5, u0_6], [dsrs1, dsrs2, dsrs3, dsrs4, dsrs5, dsrs6]) + for (u0, dsrs) in zip([u0_1, u0_2, u0_3, u0_4, u0_5, u0_6], [dsrs1, dsrs2, dsrs3, dsrs4, dsrs5, dsrs6]) # Simulates ODE version and checks `spat_getu` on its solution. oprob = ODEProblem(dsrs, u0, tspan, ps) osol = solve(oprob, Tsit5(), saveat = 0.5) @@ -188,7 +188,7 @@ end let # Prepare `DiscreteSpaceReactionSystem`s. rs = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X end tr = @transport_reaction D X dsrs = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,))) @@ -200,26 +200,26 @@ let oprob = ODEProblem(dsrs, u0, tspan, ps) # Simulates the ODE. Checks that the start/end points are correct. - # Check that the first vertex is monotonously increasing in values, and that the second one is + # Check that the first vertex is monotonously increasing in values, and that the second one is # monotonously decreasing. The non evenly spaced `saveat` is so that non-monotonicity is # not produced due to numeric errors. saveat = [0.0, 1.0, 5.0, 10.0, 50.0] - sol = solve(oprob, Vern7(); abstol = 1e-8, reltol = 1e-8) + sol = solve(oprob, Vern7(); abstol = 1.0e-8, reltol = 1.0e-8) vals = spat_getu(sol, :X, dsrs) @test vals[1] == [1.0, 3.0] @test vals[end] ≈ [2.0, 2.0] - for i = 1:(length(saveat) - 1) + for i in 1:(length(saveat) - 1) @test vals[i][1] < vals[i + 1][1] @test vals[i][2] > vals[i + 1][2] end end -# Checks interpolation when sampling at time point. Check that values at `t` is in between the +# Checks interpolation when sampling at time point. Check that values at `t` is in between the # sample points. Does so by checking that in simulation which is monotonously decreasing/increasing. let # Prepare `DiscreteSpaceReactionSystem`s. rs = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X end tr = @transport_reaction D X dsrs = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,))) @@ -241,7 +241,7 @@ end let # Prepare `DiscreteSpaceReactionSystem`s. rs = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X end tr = @transport_reaction D X dsrs = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,))) @@ -262,7 +262,7 @@ end let # Prepare `DiscreteSpaceReactionSystem`s. rs = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X end tr = @transport_reaction D X dsrs = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,))) @@ -283,7 +283,7 @@ end let # Prepare `DiscreteSpaceReactionSystem`s. rs = @reaction_network begin - (p,d), 0 <--> X + (p, d), 0 <--> X end tr = @transport_reaction D X dsrs = DiscreteSpaceReactionSystem(rs, [tr], rand([false, true], 2, 3, 4)) @@ -307,7 +307,7 @@ let @named rs = ReactionSystem([Reaction(d, [X], [])], t) rs = complete(rs) tr = @transport_reaction D X - dsrs = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid(2,)) + dsrs = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid(2)) # Solved a corresponding ODEProblem. u0 = [:X => 1.0] @@ -332,20 +332,20 @@ let for jac in [false, true], sparse in [false, true] # Creates an initial ODEProblem. u0 = [:X => 1.0, :Y => [1.0 2.0; 3.0 4.0]] - dY_vals = spzeros(4,4) - dY_vals[1,2] = 0.1; dY_vals[2,1] = 0.1; - dY_vals[1,3] = 0.2; dY_vals[3,1] = 0.2; - dY_vals[2,4] = 0.3; dY_vals[4,2] = 0.3; - dY_vals[3,4] = 0.4; dY_vals[4,3] = 0.4; + dY_vals = spzeros(4, 4) + dY_vals[1, 2] = 0.1; dY_vals[2, 1] = 0.1 + dY_vals[1, 3] = 0.2; dY_vals[3, 1] = 0.2 + dY_vals[2, 4] = 0.3; dY_vals[4, 2] = 0.3 + dY_vals[3, 4] = 0.4; dY_vals[4, 3] = 0.4 ps = [:A => 1.0, :B => [4.0 5.0; 6.0 7.0], :dX => 0.1, :dY => dY_vals] oprob_1 = ODEProblem(dsrs, u0, (0.0, 10.0), ps; jac, sparse) # Creates an alternative version of the ODEProblem. - dX_vals = spzeros(4,4) - dX_vals[1,2] = 0.01; dX_vals[2,1] = 0.01; - dX_vals[1,3] = 0.02; dX_vals[3,1] = 0.02; - dX_vals[2,4] = 0.03; dX_vals[4,2] = 0.03; - dX_vals[3,4] = 0.04; dX_vals[4,3] = 0.04; + dX_vals = spzeros(4, 4) + dX_vals[1, 2] = 0.01; dX_vals[2, 1] = 0.01 + dX_vals[1, 3] = 0.02; dX_vals[3, 1] = 0.02 + dX_vals[2, 4] = 0.03; dX_vals[4, 2] = 0.03 + dX_vals[3, 4] = 0.04; dX_vals[4, 3] = 0.04 ps = [:A => [1.1 1.2; 1.3 1.4], :B => 5.0, :dX => dX_vals, :dY => 0.01] oprob_2 = ODEProblem(dsrs, u0, (0.0, 10.0), ps; jac, sparse) @@ -372,16 +372,16 @@ let B1 = [4.0 5.0; 6.0 7.0] A2 = [1.1 1.2; 1.3 1.4] B2 = 5.0 - dY_vals = spzeros(4,4) - dY_vals[1,2] = 0.1; dY_vals[2,1] = 0.1; - dY_vals[1,3] = 0.2; dY_vals[3,1] = 0.2; - dY_vals[2,4] = 0.3; dY_vals[4,2] = 0.3; - dY_vals[3,4] = 0.4; dY_vals[4,3] = 0.4; - dX_vals = spzeros(4,4) - dX_vals[1,2] = 0.01; dX_vals[2,1] = 0.01; - dX_vals[1,3] = 0.02; dX_vals[3,1] = 0.02; - dX_vals[2,4] = 0.03; dX_vals[4,2] = 0.03; - dX_vals[3,4] = 0.04; dX_vals[4,3] = 0.04; + dY_vals = spzeros(4, 4) + dY_vals[1, 2] = 0.1; dY_vals[2, 1] = 0.1 + dY_vals[1, 3] = 0.2; dY_vals[3, 1] = 0.2 + dY_vals[2, 4] = 0.3; dY_vals[4, 2] = 0.3 + dY_vals[3, 4] = 0.4; dY_vals[4, 3] = 0.4 + dX_vals = spzeros(4, 4) + dX_vals[1, 2] = 0.01; dX_vals[2, 1] = 0.01 + dX_vals[1, 3] = 0.02; dX_vals[3, 1] = 0.02 + dX_vals[2, 4] = 0.03; dX_vals[4, 2] = 0.03 + dX_vals[3, 4] = 0.04; dX_vals[4, 3] = 0.04 dX1 = 0.1 dY1 = dY_vals dX2 = dX_vals @@ -391,10 +391,10 @@ let # Creates simulation through two different separate simulations. oprob_1_1 = ODEProblem(dsrs, u0, (0.0, 5.0), ps_1; jac = true, sparse = true) - sol_1_1 = solve(oprob_1_1, Rosenbrock23(); saveat = 1.0, abstol = 1e-8, reltol = 1e-8) + sol_1_1 = solve(oprob_1_1, Rosenbrock23(); saveat = 1.0, abstol = 1.0e-8, reltol = 1.0e-8) u0_1_2 = [:X => sol_1_1.u[end][1:2:end], :Y => sol_1_1.u[end][2:2:end]] oprob_1_2 = ODEProblem(dsrs, u0_1_2, (0.0, 5.0), ps_2; jac = true, sparse = true) - sol_1_2 = solve(oprob_1_2, Rosenbrock23(); saveat = 1.0, abstol = 1e-8, reltol = 1e-8) + sol_1_2 = solve(oprob_1_2, Rosenbrock23(); saveat = 1.0, abstol = 1.0e-8, reltol = 1.0e-8) # Creates simulation through a single simulation with a callback oprob_2 = ODEProblem(dsrs, u0, (0.0, 10.0), ps_1; jac = true, sparse = true) @@ -404,10 +404,10 @@ let spat_setp!(integrator, :B, dsrs, B2) integrator.ps[:dX] = dX2 integrator.ps[:dY] = [dY2] - rebuild_spat_internals!(integrator) + return rebuild_spat_internals!(integrator) end callback = DiscreteCallback(condition, affect!) - sol_2 = solve(oprob_2, Rosenbrock23(); saveat = 1.0, tstops = [5.0], callback, abstol = 1e-8, reltol = 1e-8) + sol_2 = solve(oprob_2, Rosenbrock23(); saveat = 1.0, tstops = [5.0], callback, abstol = 1.0e-8, reltol = 1.0e-8) # Check that trajectories are equivalent. @test [sol_1_1.u; sol_1_2.u] ≈ sol_2.u @@ -417,10 +417,10 @@ end let # Prepare `DiscreteSpaceReactionSystem`. rs = @reaction_network begin - (k1,k2), X1 <--> X2 + (k1, k2), X1 <--> X2 end - tr = @transport_reaction D X1 - grid = CartesianGrid((2,2)) + tr = @transport_reaction D X1 + grid = CartesianGrid((2, 2)) dsrs = DiscreteSpaceReactionSystem(rs, [tr], grid) # Create problems. diff --git a/test/spatial_modelling/simulate_PDEs.jl b/test/spatial_modelling/simulate_PDEs.jl index 9e4a1dd9b9..3bcbc605a4 100644 --- a/test/spatial_modelling/simulate_PDEs.jl +++ b/test/spatial_modelling/simulate_PDEs.jl @@ -11,7 +11,7 @@ rng = StableRNG(12345) # Test function. function icfun(n, x, y, A) - float(rand(rng, Poisson(round(n * A * 10))) / A / 10) + return float(rand(rng, Poisson(round(n * A * 10))) / A / 10) end ### Run Tests ### @@ -21,13 +21,15 @@ let @parameters k[1:7] D[1:3] n0[1:3] A @parameters x y @species U(x, y, t) V(x, y, t) W(x, y, t) - rxs = [Reaction(k[1], [U, W], [V, W]), + rxs = [ + Reaction(k[1], [U, W], [V, W]), Reaction(k[2], [V], [W], [2], [1]), Reaction(k[3], [W], [V], [1], [2]), Reaction(k[4], [U], nothing), Reaction(k[5], nothing, [U]), Reaction(k[6], [V], nothing), - Reaction(k[7], nothing, [V])] + Reaction(k[7], nothing, [V]), + ] pars = vcat(MT.scalarize(k), MT.scalarize(D), MT.scalarize(n0), [A]) @named bpm = ReactionSystem(rxs, t, [U, V, W], pars; spatial_ivs = [x, y]) @@ -36,17 +38,23 @@ let @test isspatial(bpm) rxeqs = Catalyst.assemble_oderhs(bpm, unknowns(bpm), combinatoric_ratelaws = false) - eqs = Dict((U => (k[5] - k[4] * U - k[1] * U * W), - V => (2 * k[3] * W + k[1] * U * W + k[7] - k[6] * V - 2 * (V^2) * k[2]), - W => ((V^2) * k[2] - k[3] * W))) + eqs = Dict( + ( + U => (k[5] - k[4] * U - k[1] * U * W), + V => (2 * k[3] * W + k[1] * U * W + k[7] - k[6] * V - 2 * (V^2) * k[2]), + W => ((V^2) * k[2] - k[3] * W), + ) + ) @test all(isequal.((MT.unwrap(eqs[st]) for st in unknowns(bpm)), rxeqs)) @test issetequal(species(bpm), [MT.unwrap(U), MT.unwrap(V), MT.unwrap(W)]) # Test a few API functions. - ns = [-1 0 0 -1 1 0 0; - 1 -2 2 0 0 -1 1; - 0 1 -1 0 0 0 0] + ns = [ + -1 0 0 -1 1 0 0; + 1 -2 2 0 0 -1 1; + 0 1 -1 0 0 0 0 + ] @test ns == netstoichmat(bpm) bpm2 = deepcopy(bpm) @test Catalyst.isequivalent(bpm, bpm2) @@ -62,28 +70,34 @@ let evalat(u, a, b, t) = (operation(ModelingToolkitBase.unwrap(u)))(a, b, t) @register_symbolic icfun(n, x, y, A) L = 32.0 - tstop = 5e4 + tstop = 5.0e4 for (i, st) in enumerate(unknowns(bpm)) idx = smap[st] eqs[i] = ∂t(st) ~ D[idx] * Δ(st) + rxeqs[idx] - newbcs = [evalat(st, x, y, 0.0) ~ icfun(n0[idx], x, y, A), + newbcs = [ + evalat(st, x, y, 0.0) ~ icfun(n0[idx], x, y, A), evalat(st, 0.0, y, t) ~ evalat(st, L, y, t), - evalat(st, x, 0.0, t) ~ evalat(st, x, L, t)] + evalat(st, x, 0.0, t) ~ evalat(st, x, L, t), + ] append!(bcs, newbcs) end - domains = [x ∈ Interval(0.0, L), + domains = [ + x ∈ Interval(0.0, L), y ∈ Interval(0.0, L), - t ∈ Interval(0.0, tstop)] + t ∈ Interval(0.0, tstop), + ] pmap = collect(MT.initial_conditions(bpm)) @named bpmpdes = PDESystem(eqs, bcs, domains, [x, y, t], [U, V, W], pmap) - rxs = [Reaction(k[1] * x, [U, W], [V, W]), + rxs = [ + Reaction(k[1] * x, [U, W], [V, W]), Reaction(k[2] * y, [V], [W], [2], [1]), Reaction(k[3] + t, [W], [V], [1], [2]), Reaction(k[4], [U], nothing), Reaction(k[5], nothing, [U]), Reaction(k[6], [V], nothing), - Reaction(k[7], nothing, [V])] + Reaction(k[7], nothing, [V]), + ] @named bpm4 = ReactionSystem(rxs, t, [U, V, W], pars; spatial_ivs = [x, y]) @test !ismassaction(rxs[1], bpm4) @test ismassaction(rxs[1], bpm4; ivset = Set([t, y])) diff --git a/test/spatial_modelling/spatial_reactions.jl b/test/spatial_modelling/spatial_reactions.jl index bdda4c25a9..a74ee7f7b5 100644 --- a/test/spatial_modelling/spatial_reactions.jl +++ b/test/spatial_modelling/spatial_reactions.jl @@ -9,13 +9,13 @@ using Catalyst, Test # Tests TransportReaction with non-trivial rate. let rs = @reaction_network begin - @parameters dV dE [edgeparameter=true] - (p,1), 0 <--> X + @parameters dV dE [edgeparameter = true] + (p, 1), 0 <--> X end @unpack dV, dE, X = rs - tr = TransportReaction(dV*dE, X) - @test isequal(tr.rate, dV*dE) + tr = TransportReaction(dV * dE, X) + @test isequal(tr.rate, dV * dE) end # Test reactions with constants in rate. let @@ -38,7 +38,7 @@ end # Test case 1. let tr_1 = @transport_reaction dX X - tr_2 = @transport_reaction dY1*dY2 Y + tr_2 = @transport_reaction dY1 * dY2 Y # @test ModelingToolkitBase.getname.(species(tr_1)) == ModelingToolkitBase.getname.(spatial_species(tr_1)) == [:X] # species(::TransportReaction) currently not supported. # @test ModelingToolkitBase.getname.(species(tr_2)) == ModelingToolkitBase.getname.(spatial_species(tr_2)) == [:Y] @@ -61,7 +61,7 @@ let end @unpack X, Y, dX, dY1, dY2 = rs tr_1 = TransportReaction(dX, X) - tr_2 = TransportReaction(dY1*dY2, Y) + tr_2 = TransportReaction(dY1 * dY2, Y) # @test isequal(species(tr_1), [X]) # @test isequal(species(tr_1), [X]) @test issetequal(spatial_species(tr_2), [Y]) @@ -79,7 +79,7 @@ let t = default_t() @species X(t) Y(t) @parameters D1 D2 D3 - @test_throws ErrorException TransportReaction(D1 + D2*(D3 + Y), X) + @test_throws ErrorException TransportReaction(D1 + D2 * (D3 + Y), X) @test_throws ErrorException TransportReaction(Y, X) @test_throws Exception @eval @transport_reaction ∅ X end @@ -95,10 +95,10 @@ let end @unpack X, Y, Z, dX, dY1, dY2, dZ = rs rate1 = dX - rate2 = dY1*dY2 + rate2 = dY1 * dY2 species3 = Z tr_1 = TransportReaction(dX, X) - tr_2 = TransportReaction(dY1*dY2, Y) + tr_2 = TransportReaction(dY1 * dY2, Y) tr_3 = TransportReaction(dZ, Z) tr_macro_1 = @transport_reaction $dX X tr_macro_2 = @transport_reaction $(rate2) Y diff --git a/test/spatial_test_networks.jl b/test/spatial_test_networks.jl index af4be2a183..c04c503cab 100644 --- a/test/spatial_test_networks.jl +++ b/test/spatial_test_networks.jl @@ -1,7 +1,7 @@ ### Fetch packages ### using Catalyst, Graphs using Catalyst: reactionsystem, spatial_reactions, dspace, num_verts, num_edges, num_species, - spatial_species, vertex_parameters, edge_parameters, edge_iterator + spatial_species, vertex_parameters, edge_parameters, edge_iterator const MT = ModelingToolkitBase # Sets rnd number. @@ -16,7 +16,7 @@ rand_v_vals(dsrs::DiscreteSpaceReactionSystem) = rand_v_vals(dspace(dsrs)) function rand_v_vals(grid::DiGraph) return rand(rng, nv(grid)) end -function rand_v_vals(grid::Catalyst.CartesianGridRej{N,T}) where {N,T} +function rand_v_vals(grid::Catalyst.CartesianGridRej{N, T}) where {N, T} return rand(rng, grid.dims) end function rand_v_vals(grid::Array{Bool, N}) where {N} @@ -43,12 +43,12 @@ end # Gets a symbol list of spatial parameters. function spatial_param_syms(dsrs::DiscreteSpaceReactionSystem) - MT.getname.(edge_parameters(dsrs)) + return MT.getname.(edge_parameters(dsrs)) end # Converts to integer value (for JumpProcess simulations). function make_values_int(values::Vector{<:Pair}) - [val[1] => round.(Int64, val[2]) for val in values] + return [val[1] => round.(Int64, val[2]) for val in values] end make_values_int(values::Matrix{<:Number}) = round.(Int64, values) make_values_int(values::Vector{<:Number}) = round.(Int64, values) @@ -71,7 +71,9 @@ SIR_srs_1 = [SIR_tr_S] SIR_srs_2 = [SIR_tr_S, SIR_tr_I, SIR_tr_R] # Small non-stiff system. -binding_system = @reaction_network begin (k1, k2), X + Y <--> XY end +binding_system = @reaction_network begin + (k1, k2), X + Y <--> XY +end binding_tr_X = @transport_reaction dX X binding_tr_Y = @transport_reaction dY Y binding_tr_XY = @transport_reaction dXY XY @@ -165,11 +167,13 @@ sigmaB_system = @reaction_network begin λW * v0 * ((1 + F * σB) / (K + σB)), ∅ ⟶ w λV * v0 * ((1 + F * σB) / (K + σB)), ∅ ⟶ v end -sigmaB_p = [:kBw => 3600, :kDw => 18, :kB1 => 3600, :kB2 => 3600, :kB3 => 3600, +sigmaB_p = [ + :kBw => 3600, :kDw => 18, :kB1 => 3600, :kB2 => 3600, :kB3 => 3600, :kB4 => 1800, :kB5 => 3600, :kD1 => 18, :kD2 => 18, :kD3 => 18, :kD4 => 1800, :kD5 => 18, :kK1 => 36, :kK2 => 6, :kP => 180, :kDeg => 0.7, - :v0 => 0.4, :F => 30, :K => 0.2, :λW => 4, :λV => 4.5] + :v0 => 0.4, :F => 30, :K => 0.2, :λW => 4, :λV => 4.5, +] sigmaB_u0 = [ :w => 1.0, :w2 => 1.0, @@ -193,16 +197,16 @@ sigmaB_srs_2 = [sigmaB_tr_σB, sigmaB_tr_w, sigmaB_tr_v] # Cartesian grids. very_small_1d_cartesian_grid = CartesianGrid(2) -very_small_2d_cartesian_grid = CartesianGrid((2,2)) -very_small_3d_cartesian_grid = CartesianGrid((2,2,2)) +very_small_2d_cartesian_grid = CartesianGrid((2, 2)) +very_small_3d_cartesian_grid = CartesianGrid((2, 2, 2)) small_1d_cartesian_grid = CartesianGrid(5) -small_2d_cartesian_grid = CartesianGrid((5,5)) -small_3d_cartesian_grid = CartesianGrid((5,5,5)) +small_2d_cartesian_grid = CartesianGrid((5, 5)) +small_3d_cartesian_grid = CartesianGrid((5, 5, 5)) large_1d_cartesian_grid = CartesianGrid(100) -large_2d_cartesian_grid = CartesianGrid((100,100)) -large_3d_cartesian_grid = CartesianGrid((100,100,100)) +large_2d_cartesian_grid = CartesianGrid((100, 100)) +large_3d_cartesian_grid = CartesianGrid((100, 100, 100)) # Masked grids. very_small_1d_masked_grid = fill(true, 2) @@ -227,16 +231,16 @@ very_small_2d_graph_grid = Graphs.grid([2, 2]) very_small_3d_graph_grid = Graphs.grid([2, 2, 2]) small_1d_graph_grid = path_graph(5) -small_2d_graph_grid = Graphs.grid([5,5]) -small_3d_graph_grid = Graphs.grid([5,5,5]) +small_2d_graph_grid = Graphs.grid([5, 5]) +small_3d_graph_grid = Graphs.grid([5, 5, 5]) medium_1d_graph_grid = path_graph(20) -medium_2d_graph_grid = Graphs.grid([20,20]) -medium_3d_graph_grid = Graphs.grid([20,20,20]) +medium_2d_graph_grid = Graphs.grid([20, 20]) +medium_3d_graph_grid = Graphs.grid([20, 20, 20]) large_1d_graph_grid = path_graph(100) -large_2d_graph_grid = Graphs.grid([100,100]) -large_3d_graph_grid = Graphs.grid([100,100,100]) +large_2d_graph_grid = Graphs.grid([100, 100]) +large_3d_graph_grid = Graphs.grid([100, 100, 100]) # Graph - paths. short_path = path_graph(100) diff --git a/test/test_functions.jl b/test/test_functions.jl index 3977a7de04..85f6608261 100644 --- a/test/test_functions.jl +++ b/test/test_functions.jl @@ -17,7 +17,7 @@ end # Generates a random parameter set (in the form of a map). Each value is a Float64. function rnd_ps(sys, rng; factor = 1.0, min = 0.0) - return [p => ( min .+ factor .* rand(rng, size(p)...)) for p in parameters(sys)] + return [p => (min .+ factor .* rand(rng, size(p)...)) for p in parameters(sys)] end # Generates a random parameter set (in the form of a map). Each value is a Float64. @@ -37,8 +37,10 @@ end # Evaluates the the drift function of the ODE corresponding to a reaction network. # Also checks that in place and out of place evaluations are identical. -function f_eval(rs::ReactionSystem, u, p, t; combinatoric_ratelaws = true, mtkcompile = false, - use_jump_ratelaws = false) +function f_eval( + rs::ReactionSystem, u, p, t; combinatoric_ratelaws = true, mtkcompile = false, + use_jump_ratelaws = false + ) prob = ODEProblem(rs, u, 0.0, p; combinatoric_ratelaws, mtkcompile, use_jump_ratelaws) du = zeros(length(u)) prob.f(du, prob.u0, prob.p, t) @@ -52,14 +54,16 @@ function jac_eval(rs::ReactionSystem, u, p, t; combinatoric_ratelaws = true, spa prob = ODEProblem(rs, u, 0.0, p; jac = true, combinatoric_ratelaws, sparse, mtkcompile) J = sparse ? deepcopy(prob.f.jac_prototype) : zeros(length(u), length(u)) prob.f.jac(J, prob.u0, prob.p, t) - @test J ≈ prob.f.jac(prob.u0, prob.p, t) atol = 1e-14 rtol = 1e-14 + @test J ≈ prob.f.jac(prob.u0, prob.p, t) atol = 1.0e-14 rtol = 1.0e-14 return J end # Evaluates the the diffusion function of the SDE corresponding to a reaction network. # Also checks that in place and out of place evaluations are identical. -function g_eval(rs::ReactionSystem, u, p, t; combinatoric_ratelaws = true, mtkcompile = false, - use_jump_ratelaws = false) +function g_eval( + rs::ReactionSystem, u, p, t; combinatoric_ratelaws = true, mtkcompile = false, + use_jump_ratelaws = false + ) prob = SDEProblem(rs, u, 0.0, p; combinatoric_ratelaws, mtkcompile, use_jump_ratelaws) dW = zeros(length(u), numreactions(rs) + length(ModelingToolkitBase.get_brownians(rs))) prob.g(dW, prob.u0, prob.p, t) diff --git a/test/test_networks.jl b/test/test_networks.jl index a77fa75a12..38705adbfd 100644 --- a/test/test_networks.jl +++ b/test/test_networks.jl @@ -79,10 +79,10 @@ end reaction_networks_standard[10] = @reaction_network rns10 begin p, ∅ ⟶ X1 - (k1, k2), (X1,X1) → X2 - (k3, k4), (X2,X2) → X3 - (k5, k6), (X3,X3) → X4 - (k7, k8), (X4,X4) → X5 + (k1, k2), (X1, X1) → X2 + (k3, k4), (X2, X2) → X3 + (k5, k6), (X3, X3) → X4 + (k7, k8), (X4, X4) → X5 d, X5 ⟶ ∅ end @@ -220,8 +220,10 @@ reaction_networks_conserved[9] = @reaction_network rnc9 begin (k3, k4), X3 + X4 ↔ X5 (k5, k6), X5 + X6 ↔ X7 end -reaction_network_conslaws[9] = [1 0 1 0 1 0 1; -1 1 0 0 0 0 0; 0 0 0 1 1 0 1; - 0 0 0 0 0 1 1] +reaction_network_conslaws[9] = [ + 1 0 1 0 1 0 1; -1 1 0 0 0 0 0; 0 0 0 1 1 0 1; + 0 0 0 0 0 1 1 +] reaction_networks_conserved[10] = @reaction_network rnc10 begin kDeg, (w, w2, w2v, v, w2v2, vP, σB, w2σB) ⟶ ∅ @@ -349,8 +351,10 @@ reaction_networks_weird[10] = @reaction_network rnw10 begin end ### Gathers all networks in a single array ### -reaction_networks_all = [reaction_networks_standard..., +reaction_networks_all = [ + reaction_networks_standard..., reaction_networks_hill..., reaction_networks_conserved..., reaction_networks_real..., - reaction_networks_weird...] + reaction_networks_weird..., +] diff --git a/test/upstream/mtk_structure_indexing.jl b/test/upstream/mtk_structure_indexing.jl index f9f066f790..38d94f0666 100644 --- a/test/upstream/mtk_structure_indexing.jl +++ b/test/upstream/mtk_structure_indexing.jl @@ -19,8 +19,8 @@ begin # Creates the model and unpacks its context. model = @reaction_network begin @observables XY ~ X + Y - (kp,kd), 0 <--> X - (k1,k2), X <--> Y + (kp, kd), 0 <--> X + (k1, k2), X <--> Y end @unpack XY, X, Y, kp, kd, k1, k2 = model @@ -31,7 +31,7 @@ begin # Creates problems. oprob = ODEProblem(model, u0_vals, tspan, p_vals) - sprob = SDEProblem(model,u0_vals, tspan, p_vals) + sprob = SDEProblem(model, u0_vals, tspan, p_vals) jprob = JumpProblem(model, u0_vals, tspan, p_vals; rng) nprob = NonlinearProblem(model, u0_vals, p_vals) ssprob = SteadyStateProblem(model, u0_vals, p_vals) @@ -64,16 +64,16 @@ end # Tests problem indexing and updating. let - for prob in deepcopy([oprob, sprob, jprob, nprob, ssprob, eoprob, esprob, ejprob, enprob, essprob]) + for prob in deepcopy([oprob, sprob, jprob, nprob, ssprob, eoprob, esprob, ejprob, enprob, essprob]) # Get u values (including observables). @test prob[X] == prob[model.X] == prob[:X] == 4 @test prob[XY] == prob[model.XY] == prob[:XY] == 9 - @test prob[[XY,Y]] == prob[[model.XY,model.Y]] == prob[[:XY,:Y]] == [9, 5] - @test prob[(XY,Y)] == prob[(model.XY,model.Y)] == prob[(:XY,:Y)] == (9, 5) + @test prob[[XY, Y]] == prob[[model.XY, model.Y]] == prob[[:XY, :Y]] == [9, 5] + @test prob[(XY, Y)] == prob[(model.XY, model.Y)] == prob[(:XY, :Y)] == (9, 5) @test getu(prob, X)(prob) == getu(prob, model.X)(prob) == getu(prob, :X)(prob) == 4 @test getu(prob, XY)(prob) == getu(prob, model.XY)(prob) == getu(prob, :XY)(prob) == 9 - @test getu(prob, [XY,Y])(prob) == getu(prob, [model.XY,model.Y])(prob) == getu(prob, [:XY,:Y])(prob) == [9, 5] - @test getu(prob, (XY,Y))(prob) == getu(prob, (model.XY,model.Y))(prob) == getu(prob, (:XY,:Y))(prob) == (9, 5) + @test getu(prob, [XY, Y])(prob) == getu(prob, [model.XY, model.Y])(prob) == getu(prob, [:XY, :Y])(prob) == [9, 5] + @test getu(prob, (XY, Y))(prob) == getu(prob, (model.XY, model.Y))(prob) == getu(prob, (:XY, :Y))(prob) == (9, 5) # Set u values. prob[X] = 20 @@ -91,11 +91,11 @@ let # Get p values. @test prob.ps[kp] == prob.ps[model.kp] == prob.ps[:kp] == 1.0 - @test prob.ps[[k1,k2]] == prob.ps[[model.k1,model.k2]] == prob.ps[[:k1,:k2]] == [0.25, 0.5] - @test prob.ps[(k1,k2)] == prob.ps[(model.k1,model.k2)] == prob.ps[(:k1,:k2)] == (0.25, 0.5) + @test prob.ps[[k1, k2]] == prob.ps[[model.k1, model.k2]] == prob.ps[[:k1, :k2]] == [0.25, 0.5] + @test prob.ps[(k1, k2)] == prob.ps[(model.k1, model.k2)] == prob.ps[(:k1, :k2)] == (0.25, 0.5) @test getp(prob, kp)(prob) == getp(prob, model.kp)(prob) == getp(prob, :kp)(prob) == 1.0 - @test getp(prob, [k1,k2])(prob) == getp(prob, [model.k1,model.k2])(prob) == getp(prob, [:k1,:k2])(prob) == [0.25, 0.5] - @test getp(prob, (k1,k2))(prob) == getp(prob, (model.k1,model.k2))(prob) == getp(prob, (:k1,:k2))(prob) == (0.25, 0.5) + @test getp(prob, [k1, k2])(prob) == getp(prob, [model.k1, model.k2])(prob) == getp(prob, [:k1, :k2])(prob) == [0.25, 0.5] + @test getp(prob, (k1, k2))(prob) == getp(prob, (model.k1, model.k2))(prob) == getp(prob, (:k1, :k2))(prob) == (0.25, 0.5) # Set p values. prob.ps[kp] = 2.0 @@ -156,12 +156,12 @@ let # Get u values. @test int[X] == int[model.X] == int[:X] == 4 @test int[XY] == int[model.XY] == int[:XY] == 9 - @test int[[XY,Y]] == int[[model.XY,model.Y]] == int[[:XY,:Y]] == [9, 5] - @test int[(XY,Y)] == int[(model.XY,model.Y)] == int[(:XY,:Y)] == (9, 5) + @test int[[XY, Y]] == int[[model.XY, model.Y]] == int[[:XY, :Y]] == [9, 5] + @test int[(XY, Y)] == int[(model.XY, model.Y)] == int[(:XY, :Y)] == (9, 5) @test getu(int, X)(int) == getu(int, model.X)(int) == getu(int, :X)(int) == 4 @test getu(int, XY)(int) == getu(int, model.XY)(int) == getu(int, :XY)(int) == 9 - @test getu(int, [XY,Y])(int) == getu(int, [model.XY,model.Y])(int) == getu(int, [:XY,:Y])(int) == [9, 5] - @test getu(int, (XY,Y))(int) == getu(int, (model.XY,model.Y))(int) == getu(int, (:XY,:Y))(int) == (9, 5) + @test getu(int, [XY, Y])(int) == getu(int, [model.XY, model.Y])(int) == getu(int, [:XY, :Y])(int) == [9, 5] + @test getu(int, (XY, Y))(int) == getu(int, (model.XY, model.Y))(int) == getu(int, (:XY, :Y))(int) == (9, 5) # Set u values. int[X] = 20 @@ -179,11 +179,11 @@ let # Get p values. @test int.ps[kp] == int.ps[model.kp] == int.ps[:kp] == 1.0 - @test int.ps[[k1,k2]] == int.ps[[model.k1,model.k2]] == int.ps[[:k1,:k2]] == [0.25, 0.5] - @test int.ps[(k1,k2)] == int.ps[(model.k1,model.k2)] == int.ps[(:k1,:k2)] == (0.25, 0.5) + @test int.ps[[k1, k2]] == int.ps[[model.k1, model.k2]] == int.ps[[:k1, :k2]] == [0.25, 0.5] + @test int.ps[(k1, k2)] == int.ps[(model.k1, model.k2)] == int.ps[(:k1, :k2)] == (0.25, 0.5) @test getp(int, kp)(int) == getp(int, model.kp)(int) == getp(int, :kp)(int) == 1.0 - @test getp(int, [k1,k2])(int) == getp(int, [model.k1,model.k2])(int) == getp(int, [:k1,:k2])(int) == [0.25, 0.5] - @test getp(int, (k1,k2))(int) == getp(int, (model.k1,model.k2))(int) == getp(int, (:k1,:k2))(int) == (0.25, 0.5) + @test getp(int, [k1, k2])(int) == getp(int, [model.k1, model.k2])(int) == getp(int, [:k1, :k2])(int) == [0.25, 0.5] + @test getp(int, (k1, k2))(int) == getp(int, (model.k1, model.k2))(int) == getp(int, (:k1, :k2))(int) == (0.25, 0.5) # Set p values. int.ps[kp] = 2.0 @@ -208,24 +208,24 @@ let # Save single variable if !(solver isa SSAStepper) - @test solve(prob, solver; seed, save_idxs=X)[X][1] == 4 - @test solve(prob, solver; seed, save_idxs=model.X)[X][1] == 4 - @test solve(prob, solver; seed, save_idxs=:X)[X][1] == 4 + @test solve(prob, solver; seed, save_idxs = X)[X][1] == 4 + @test solve(prob, solver; seed, save_idxs = model.X)[X][1] == 4 + @test solve(prob, solver; seed, save_idxs = :X)[X][1] == 4 else - @test_broken solve(prob, solver; seed, save_idxs=X)[X][1] == 4 - @test_broken solve(prob, solver; seed, save_idxs=model.X)[X][1] == 4 - @test_broken solve(prob, solver; seed, save_idxs=:X)[X][1] == 4 + @test_broken solve(prob, solver; seed, save_idxs = X)[X][1] == 4 + @test_broken solve(prob, solver; seed, save_idxs = model.X)[X][1] == 4 + @test_broken solve(prob, solver; seed, save_idxs = :X)[X][1] == 4 end # Save observable. - @test_broken solve(prob, solver; seed, save_idxs=XY)[XY][1] == 9 - @test_broken solve(prob, solver; seed, save_idxs=model.XY)[XY][1] == 9 - @test_broken solve(prob, solver; seed, save_idxs=:XY)[XY][1] == 9 + @test_broken solve(prob, solver; seed, save_idxs = XY)[XY][1] == 9 + @test_broken solve(prob, solver; seed, save_idxs = model.XY)[XY][1] == 9 + @test_broken solve(prob, solver; seed, save_idxs = :XY)[XY][1] == 9 # Save vector of stuff. - @test_broken solve(prob, solver; seed, save_idxs=[XY,Y])[[XY,Y]][1] == [9, 5] - @test_broken solve(prob, solver; seed, save_idxs=[model.XY,model.Y])[[model.XY,model.Y]][1] == [9, 5] - @test_broken solve(prob, solver; seed, save_idxs=[:XY,:Y])[[:XY,:Y]][1] == [9, 5] + @test_broken solve(prob, solver; seed, save_idxs = [XY, Y])[[XY, Y]][1] == [9, 5] + @test_broken solve(prob, solver; seed, save_idxs = [model.XY, model.Y])[[model.XY, model.Y]][1] == [9, 5] + @test_broken solve(prob, solver; seed, save_idxs = [:XY, :Y])[[:XY, :Y]][1] == [9, 5] end end @@ -235,26 +235,26 @@ let # Get u values. @test sol[X][1] == sol[model.X][1] == sol[:X][1] == 4 @test sol[XY][1] == sol[model.XY][1] == sol[:XY][1] == 9 - @test sol[[XY,Y]][1] == sol[[model.XY,model.Y]][1] == sol[[:XY,:Y]][1] == [9, 5] - @test sol[(XY,Y)][1] == sol[(model.XY,model.Y)][1] == sol[(:XY,:Y)][1] == (9, 5) + @test sol[[XY, Y]][1] == sol[[model.XY, model.Y]][1] == sol[[:XY, :Y]][1] == [9, 5] + @test sol[(XY, Y)][1] == sol[(model.XY, model.Y)][1] == sol[(:XY, :Y)][1] == (9, 5) @test getu(sol, X)(sol)[1] == getu(sol, model.X)(sol)[1] == getu(sol, :X)(sol)[1] == 4 @test getu(sol, XY)(sol)[1] == getu(sol, model.XY)(sol)[1] == getu(sol, :XY)(sol)[1] == 9 - @test getu(sol, [XY,Y])(sol)[1] == getu(sol, [model.XY,model.Y])(sol)[1] == getu(sol, [:XY,:Y])(sol)[1] == [9, 5] - @test getu(sol, (XY,Y))(sol)[1] == getu(sol, (model.XY,model.Y))(sol)[1] == getu(sol, (:XY,:Y))(sol)[1] == (9, 5) + @test getu(sol, [XY, Y])(sol)[1] == getu(sol, [model.XY, model.Y])(sol)[1] == getu(sol, [:XY, :Y])(sol)[1] == [9, 5] + @test getu(sol, (XY, Y))(sol)[1] == getu(sol, (model.XY, model.Y))(sol)[1] == getu(sol, (:XY, :Y))(sol)[1] == (9, 5) # Get u values via idxs and functional call. - @test sol(0.0; idxs=X) == sol(0.0; idxs=model.X) == sol(0.0; idxs=:X) == 4 - @test sol(0.0; idxs=XY) == sol(0.0; idxs=model.XY) == sol(0.0; idxs=:XY) == 9 - @test sol(0.0; idxs = [XY,Y]) == sol(0.0; idxs = [model.XY,model.Y]) == sol(0.0; idxs = [:XY,:Y]) == [9, 5] - @test_broken sol(0.0; idxs = (XY,Y)) == sol(0.0; idxs = (model.XY,model.Y)) == sol(0.0; idxs = (:XY,:Y)) == (9, 5) # https://github.com/SciML/SciMLBase.jl/issues/711 + @test sol(0.0; idxs = X) == sol(0.0; idxs = model.X) == sol(0.0; idxs = :X) == 4 + @test sol(0.0; idxs = XY) == sol(0.0; idxs = model.XY) == sol(0.0; idxs = :XY) == 9 + @test sol(0.0; idxs = [XY, Y]) == sol(0.0; idxs = [model.XY, model.Y]) == sol(0.0; idxs = [:XY, :Y]) == [9, 5] + @test_broken sol(0.0; idxs = (XY, Y)) == sol(0.0; idxs = (model.XY, model.Y)) == sol(0.0; idxs = (:XY, :Y)) == (9, 5) # https://github.com/SciML/SciMLBase.jl/issues/711 # Get p values. @test sol.ps[kp] == sol.ps[model.kp] == sol.ps[:kp] == 1.0 - @test sol.ps[[k1,k2]] == sol.ps[[model.k1,model.k2]] == sol.ps[[:k1,:k2]] == [0.25, 0.5] - @test sol.ps[(k1,k2)] == sol.ps[(model.k1,model.k2)] == sol.ps[(:k1,:k2)] == (0.25, 0.5) + @test sol.ps[[k1, k2]] == sol.ps[[model.k1, model.k2]] == sol.ps[[:k1, :k2]] == [0.25, 0.5] + @test sol.ps[(k1, k2)] == sol.ps[(model.k1, model.k2)] == sol.ps[(:k1, :k2)] == (0.25, 0.5) @test getp(sol, kp)(sol) == getp(sol, model.kp)(sol) == getp(sol, :kp)(sol) == 1.0 - @test getp(sol, [k1,k2])(sol) == getp(sol, [model.k1,model.k2])(sol) == getp(sol, [:k1,:k2])(sol) == [0.25, 0.5] - @test getp(sol, (k1,k2))(sol) == getp(sol, (model.k1,model.k2))(sol) == getp(sol, (:k1,:k2))(sol) == (0.25, 0.5) + @test getp(sol, [k1, k2])(sol) == getp(sol, [model.k1, model.k2])(sol) == getp(sol, [:k1, :k2])(sol) == [0.25, 0.5] + @test getp(sol, (k1, k2))(sol) == getp(sol, (model.k1, model.k2))(sol) == getp(sol, (:k1, :k2))(sol) == (0.25, 0.5) end # Handles nonlinear and steady state solutions differently. @@ -263,20 +263,20 @@ let # Get u values. @test sol[X] == sol[model.X] == sol[:X] @test sol[XY] == sol[model.XY][1] == sol[:XY] - @test sol[[XY,Y]] == sol[[model.XY,model.Y]] == sol[[:XY,:Y]] - @test sol[(XY,Y)] == sol[(model.XY,model.Y)] == sol[(:XY,:Y)] + @test sol[[XY, Y]] == sol[[model.XY, model.Y]] == sol[[:XY, :Y]] + @test sol[(XY, Y)] == sol[(model.XY, model.Y)] == sol[(:XY, :Y)] @test getu(sol, X)(sol) == getu(sol, model.X)(sol)[1] == getu(sol, :X)(sol) @test getu(sol, XY)(sol) == getu(sol, model.XY)(sol)[1] == getu(sol, :XY)(sol) - @test getu(sol, [XY,Y])(sol) == getu(sol, [model.XY,model.Y])(sol) == getu(sol, [:XY,:Y])(sol) - @test getu(sol, (XY,Y))(sol) == getu(sol, (model.XY,model.Y))(sol) == getu(sol, (:XY,:Y))(sol) + @test getu(sol, [XY, Y])(sol) == getu(sol, [model.XY, model.Y])(sol) == getu(sol, [:XY, :Y])(sol) + @test getu(sol, (XY, Y))(sol) == getu(sol, (model.XY, model.Y))(sol) == getu(sol, (:XY, :Y))(sol) # Get p values. @test sol.ps[kp] == sol.ps[model.kp] == sol.ps[:kp] - @test sol.ps[[k1,k2]] == sol.ps[[model.k1,model.k2]] == sol.ps[[:k1,:k2]] - @test sol.ps[(k1,k2)] == sol.ps[(model.k1,model.k2)] == sol.ps[(:k1,:k2)] + @test sol.ps[[k1, k2]] == sol.ps[[model.k1, model.k2]] == sol.ps[[:k1, :k2]] + @test sol.ps[(k1, k2)] == sol.ps[(model.k1, model.k2)] == sol.ps[(:k1, :k2)] @test getp(sol, kp)(sol) == getp(sol, model.kp)(sol) == getp(sol, :kp)(sol) - @test getp(sol, [k1,k2])(sol) == getp(sol, [model.k1,model.k2])(sol) == getp(sol, [:k1,:k2])(sol) - @test getp(sol, (k1,k2))(sol) == getp(sol, (model.k1,model.k2))(sol) == getp(sol, (:k1,:k2))(sol) + @test getp(sol, [k1, k2])(sol) == getp(sol, [model.k1, model.k2])(sol) == getp(sol, [:k1, :k2])(sol) + @test getp(sol, (k1, k2))(sol) == getp(sol, (model.k1, model.k2))(sol) == getp(sol, (:k1, :k2))(sol) end end end @@ -293,12 +293,12 @@ let @test length(plot(sol; idxs = :XY).series_list) == 1 # As vector. - @test length(plot(sol; idxs = [X,Y]).series_list) == 2 - @test length(plot(sol; idxs = [XY,Y]).series_list) == 2 - @test length(plot(sol; idxs = [model.X,model.Y]).series_list) == 2 - @test length(plot(sol; idxs = [model.XY,model.Y]).series_list) == 2 - @test length(plot(sol; idxs = [:X,:Y]).series_list) == 2 - @test length(plot(sol; idxs = [:XY,:Y]).series_list) == 2 + @test length(plot(sol; idxs = [X, Y]).series_list) == 2 + @test length(plot(sol; idxs = [XY, Y]).series_list) == 2 + @test length(plot(sol; idxs = [model.X, model.Y]).series_list) == 2 + @test length(plot(sol; idxs = [model.XY, model.Y]).series_list) == 2 + @test length(plot(sol; idxs = [:X, :Y]).series_list) == 2 + @test length(plot(sol; idxs = [:XY, :Y]).series_list) == 2 # As tuple. @test length(plot(sol; idxs = (X, Y)).series_list) == 1 @@ -351,10 +351,14 @@ end prob6 = remake(prob1, u0 = [Y2 => 40.0], p = [k1 => 0.4]) prob7 = remake(prob1, u0 = [X1 => 10.0, X2 => 20.0], p = [V0 => 50.0]) prob8 = remake(prob1, u0 = [W => 60.0]) - prob9 = remake(prob2; u0 = [X2 => nothing, Y2 => nothing], - p = [Γ => [10.0, 20.0]]) - prob10 = remake(prob1; u0 = [Y1 => 20.0, Y2 => nothing, X2 => nothing], - p = [Γ => [20.0, 30.0], k1 => 0.4]) + prob9 = remake( + prob2; u0 = [X2 => nothing, Y2 => nothing], + p = [Γ => [10.0, 20.0]] + ) + prob10 = remake( + prob1; u0 = [Y1 => 20.0, Y2 => nothing, X2 => nothing], + p = [Γ => [20.0, 30.0], k1 => 0.4] + ) prob11 = remake(prob10, u0 = [X1 => 10.0], p = [k2 => 0.5]) # Creates a testing function. @@ -376,39 +380,61 @@ end # Checks that all problem values are correct. Γ = prob1.f.sys.Γ - test_vals(prob1, + test_vals( + prob1, Dict(X1 => 1.0, X2 => 2.0, Y1 => 3.0, Y2 => 4.0, V => 3.0, W => 6.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 3.0, Γ[2] => 7.0)) - test_vals(prob2, + Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 3.0, Γ[2] => 7.0) + ) + test_vals( + prob2, Dict(X1 => 10.0, X2 => 2.0, Y1 => 3.0, Y2 => 4.0, V => 3.0, W => 6.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 12.0, Γ[2] => 7.0)) - test_vals(prob3, + Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 12.0, Γ[2] => 7.0) + ) + test_vals( + prob3, Dict(X1 => 10.0, X2 => 20.0, Y1 => 3.0, Y2 => 4.0, V => 3.0, W => 6.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 30.0, Γ[2] => 7.0)) - test_vals(prob4, + Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 30.0, Γ[2] => 7.0) + ) + test_vals( + prob4, Dict(X1 => 1.0, X2 => 20.0, Y1 => 30.0, Y2 => 4.0, V => 3.0, W => 6.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 21.0, Γ[2] => 34.0)) - test_vals(prob5, + Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 21.0, Γ[2] => 34.0) + ) + test_vals( + prob5, Dict(X1 => 10.0, X2 => 20.0, Y1 => 3.0, Y2 => 4.0, V => 3.0, W => 6.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 30.0, Γ[2] => 7.0)) - test_vals(prob6, + Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 30.0, Γ[2] => 7.0) + ) + test_vals( + prob6, Dict(X1 => 1.0, X2 => 2.0, Y1 => 3.0, Y2 => 40.0, V => 3.0, W => 6.0), - Dict(k1 => 0.4, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 3.0, Γ[2] => 43.0)) - test_vals(prob7, + Dict(k1 => 0.4, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 3.0, Γ[2] => 43.0) + ) + test_vals( + prob7, Dict(X1 => 10.0, X2 => 20.0, Y1 => 3.0, Y2 => 4.0, V => 50.0, W => 6.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 50.0, v => 50.0, w => 6.0, Γ[1] => 30.0, Γ[2] => 7.0)) - test_vals(prob8, + Dict(k1 => 0.1, k2 => 0.2, V0 => 50.0, v => 50.0, w => 6.0, Γ[1] => 30.0, Γ[2] => 7.0) + ) + test_vals( + prob8, Dict(X1 => 1.0, X2 => 2.0, Y1 => 3.0, Y2 => 4.0, V => 3.0, W => 60.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 60.0, Γ[1] => 3.0, Γ[2] => 7.0)) - test_vals(prob9, + Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 60.0, Γ[1] => 3.0, Γ[2] => 7.0) + ) + test_vals( + prob9, Dict(X1 => 10.0, X2 => 0.0, Y1 => 3.0, Y2 => 17.0, V => 3.0, W => 6.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 10.0, Γ[2] => 20.0)) - test_vals(prob10, + Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 10.0, Γ[2] => 20.0) + ) + test_vals( + prob10, Dict(X1 => 1.0, X2 => 19.0, Y1 => 20.0, Y2 => 10.0, V => 3.0, W => 6.0), - Dict(k1 => 0.4, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 20.0, Γ[2] => 30.0)) - test_vals(prob11, + Dict(k1 => 0.4, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 20.0, Γ[2] => 30.0) + ) + test_vals( + prob11, Dict(X1 => 10.0, X2 => 10.0, Y1 => 20.0, Y2 => 10.0, V => 3.0, W => 6.0), - Dict(k1 => 0.4, k2 => 0.5, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 20.0, Γ[2] => 30.0)) + Dict(k1 => 0.4, k2 => 0.5, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 20.0, Γ[2] => 30.0) + ) end end @@ -422,7 +448,7 @@ end let # Creates the model. rn = @reaction_network begin - p1*p2, A + B --> C + p1 * p2, A + B --> C end @unpack p1, p2 = rn From 2c03cb7f187748d674defc1f5f3faa8b725596c0 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Wed, 3 Jun 2026 14:37:28 -0400 Subject: [PATCH 2/2] Drop Runic reformatting and check from this PR Catalyst did not previously run Runic, so adding the centralized Runic check reformatted the entire repo (~80 files), which is too large to bundle with the CI migration. Remove the Runic.yml caller and revert the formatting; Runic adoption can be done as a separate, focused PR. This PR keeps the rest of the centralization: SpellCheck and Downgrade callers, dependabot cleanup. Tests.yml/Documentation were already centralized and are unchanged. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/Runic.yml | 15 - .typos.toml | 2 - docs/make.jl | 24 +- docs/pages.jl | 170 +++--- .../global_sensitivity_analysis.md | 2 +- .../optimization_ode_param_fitting.md | 4 +- .../model_creation/functional_parameters.md | 2 +- .../bifurcation_kit_extension.jl | 18 +- ext/CatalystCairoMakieExtension.jl | 2 +- ...cairo_makie_extension_spatial_modelling.jl | 66 +-- ...graph_makie_extension_spatial_modelling.jl | 22 +- .../rn_graph_plot.jl | 60 +-- .../homotopy_continuation_extension.jl | 67 +-- .../structural_identifiability_extension.jl | 72 +-- src/Catalyst.jl | 34 +- src/chemistry_functionality.jl | 102 ++-- src/dsl.jl | 304 +++++------ src/expression_utils.jl | 4 +- src/latex_utils.jl | 2 +- src/latexify_recipes.jl | 58 +-- src/mtk_nullspace_function.jl | 56 +- src/network_analysis.jl | 208 ++++---- src/plotting.jl | 11 +- src/reaction.jl | 94 ++-- src/reactionsystem.jl | 294 ++++------- src/reactionsystem_conversions.jl | 490 +++++++----------- .../serialisation_support.jl | 65 +-- .../serialise_fields.jl | 30 +- .../serialise_reactionsystem.jl | 52 +- src/registered_functions.jl | 20 +- .../discrete_space_jump_systems.jl | 60 +-- .../discrete_space_reaction_systems.jl | 107 ++-- .../discrete_space_sim_struct_interfacing.jl | 66 +-- .../spatial_ODE_systems.jl | 100 ++-- .../spatial_reactions.jl | 40 +- src/spatial_reaction_systems/utility.jl | 74 +-- src/steady_state_stability.jl | 18 +- src/unit_helpers.jl | 158 ++---- test/dsl/dsl_basic_model_construction.jl | 192 +++---- test/extensions/bifurcation_kit.jl | 52 +- test/extensions/dspace_simulation_plotting.jl | 12 +- test/extensions/graphmakie.jl | 34 +- test/extensions/homotopy_continuation.jl | 50 +- test/extensions/stability_computation.jl | 32 +- test/extensions/structural_identifiability.jl | 150 +++--- test/miscellaneous_tests/compound_macro.jl | 72 +-- test/miscellaneous_tests/explicit_imports.jl | 12 +- .../miscellaneous_tests/reaction_balancing.jl | 90 ++-- .../reactionsystem_serialisation.jl | 120 ++--- test/network_analysis/conservation_laws.jl | 96 ++-- test/network_analysis/crn_theory.jl | 26 +- test/network_analysis/network_properties.jl | 211 ++++---- .../coupled_equation_crn_systems.jl | 226 ++++---- .../custom_crn_functions.jl | 194 +++---- test/reactionsystem_core/events.jl | 122 ++--- .../functional_parameters.jl | 65 ++- .../higher_order_reactions.jl | 26 +- .../parameter_type_designation.jl | 14 +- test/reactionsystem_core/reaction.jl | 84 ++- test/reactionsystem_core/reactionsystem.jl | 378 ++++++-------- .../symbolic_stoichiometry.jl | 90 ++-- test/runtests.jl | 154 ++---- test/runtests_extensions.jl | 24 +- test/simulation_and_solving/hybrid_models.jl | 154 +++--- .../jacobian_construction.jl | 72 ++- test/simulation_and_solving/simulate_ODEs.jl | 54 +- test/simulation_and_solving/simulate_SDEs.jl | 81 ++- test/simulation_and_solving/simulate_jumps.jl | 62 +-- .../simulation_and_solving/solve_nonlinear.jl | 48 +- .../dspace_reaction_systems.jl | 88 ++-- .../dspace_reaction_systems_ODEs.jl | 200 ++++--- .../dspace_reaction_systems_jumps.jl | 44 +- .../dspace_reaction_systems_space_types.jl | 112 ++-- .../dspace_simulation_struct_interfacing.jl | 98 ++-- test/spatial_modelling/simulate_PDEs.jl | 46 +- test/spatial_modelling/spatial_reactions.jl | 18 +- test/spatial_test_networks.jl | 42 +- test/test_functions.jl | 16 +- test/test_networks.jl | 20 +- test/upstream/mtk_structure_indexing.jl | 196 +++---- 80 files changed, 2836 insertions(+), 4014 deletions(-) delete mode 100644 .github/workflows/Runic.yml diff --git a/.github/workflows/Runic.yml b/.github/workflows/Runic.yml deleted file mode 100644 index e1cf7d9ad9..0000000000 --- a/.github/workflows/Runic.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: "Runic Formatting" - -on: - push: - branches: - - 'master' - - 'release-' - tags: '*' - pull_request: - -jobs: - runic: - name: "Runic Format Check" - uses: "SciML/.github/.github/workflows/runic.yml@v1" - secrets: "inherit" diff --git a/.typos.toml b/.typos.toml index 929b80d11f..5209b91ea0 100644 --- a/.typos.toml +++ b/.typos.toml @@ -2,8 +2,6 @@ # Catalyst specific systemes = "systemes" Hass = "Hass" -oint = "oint" # ODEIntegrator variable name used in tests -Lamba = "Lamba" # LambaEM/LambaEulerHeun SDE solvers (StochasticDiffEq) # Julia-specific functions indexin = "indexin" diff --git a/docs/make.jl b/docs/make.jl index 706dbfccbe..e9d15219fa 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -32,28 +32,20 @@ include("pages.jl") # clean = true, # pages = pages) -makedocs( - sitename = "Catalyst.jl", +makedocs(sitename = "Catalyst.jl", authors = "Samuel Isaacson", - format = Documenter.HTML(; - analytics = "UA-90474609-3", + format = Documenter.HTML(; analytics = "UA-90474609-3", prettyurls = (get(ENV, "CI", nothing) == "true"), collapselevel = 1, assets = ["assets/favicon.ico"], - canonical = "https://docs.sciml.ai/Catalyst/stable/" - ), - modules = [ - Catalyst, ModelingToolkitBase, SymbolicIndexingInterface, - Base.get_extension(Catalyst, :CatalystGraphMakieExtension), - ], + canonical = "https://docs.sciml.ai/Catalyst/stable/"), + modules = [Catalyst, ModelingToolkitBase, SymbolicIndexingInterface, + Base.get_extension(Catalyst, :CatalystGraphMakieExtension)], doctest = false, clean = true, pages = pages, pagesonly = true, - warnonly = [:missing_docs, :cross_references] -) # `:cross_references` here temporarily while getting docs to work on v16. + warnonly = [:missing_docs, :cross_references]) # `:cross_references` here temporarily while getting docs to work on v16. -deploydocs( - repo = "github.com/SciML/Catalyst.jl.git"; - push_preview = true -) +deploydocs(repo = "github.com/SciML/Catalyst.jl.git"; + push_preview = true) diff --git a/docs/pages.jl b/docs/pages.jl index 55db8e474d..9e80f95acf 100644 --- a/docs/pages.jl +++ b/docs/pages.jl @@ -1,87 +1,87 @@ 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/math_models_intro.md", - ], - "Model creation and properties" => Any[ - "model_creation/dsl_basics.md", - "model_creation/dsl_advanced.md", - "model_creation/programmatic_CRN_construction.md", - "model_creation/compositional_modeling.md", - "model_creation/coupled_non_crn_models.md", - "model_creation/events.md", - "model_creation/conservation_laws.md", - "model_creation/parametric_stoichiometry.md", - "model_creation/functional_parameters.md", - "model_creation/model_file_loading_and_export.md", - "model_creation/model_visualisation.md", - "model_creation/reactionsystem_content_accessing.md", - "model_creation/chemistry_related_functionality.md", - "Examples" => Any[ - "model_creation/examples/basic_CRN_library.md", - "model_creation/examples/programmatic_generative_linear_pathway.md", - "model_creation/examples/hodgkin_huxley_equation.md", - "model_creation/examples/smoluchowski_coagulation_equation.md", - "model_creation/examples/noise_modelling_approaches.md", - ], - ], - "Model simulation and visualization" => Any[ - "model_simulation/simulation_introduction.md", - "model_simulation/simulation_plotting.md", - "model_simulation/simulation_structure_interfacing.md", - "model_simulation/ensemble_simulations.md", - "model_simulation/hybrid_simulations.md", - "model_simulation/ode_simulation_performance.md", - "model_simulation/sde_simulation_performance.md", - # "model_simulation/finite_state_projection_simulation.md", # Wait for update with new version. - "Examples" => Any[ - "model_simulation/examples/periodic_events_simulation.md", - "model_simulation/examples/activation_time_distribution_measurement.md", - "model_simulation/examples/interactive_brusselator_simulation.md", - ], - ], - "Network Analysis" => Any[ - "network_analysis/odes.md", - "network_analysis/crn_theory.md", - "network_analysis/network_properties.md", - ], - "Steady state analysis" => Any[ - "steady_state_functionality/homotopy_continuation.md", - "steady_state_functionality/nonlinear_solve.md", - "steady_state_functionality/steady_state_stability_computation.md", - "steady_state_functionality/bifurcation_diagrams.md", - "steady_state_functionality/dynamical_systems.md", - "Examples" => Any[ - "steady_state_functionality/examples/nullcline_plotting.md", - "steady_state_functionality/examples/bifurcationkit_periodic_orbits.md", - "steady_state_functionality/examples/bifurcationkit_codim2.md", - ], - ], - "Inverse problems" => Any[ - "inverse_problems/petab_ode_param_fitting.md", - "inverse_problems/optimization_ode_param_fitting.md", - "inverse_problems/behaviour_optimisation.md", - "inverse_problems/structural_identifiability.md", - "inverse_problems/likelihood_profiler.md", - "inverse_problems/global_sensitivity_analysis.md", - "inverse_problems/turing_ode_param_fitting.md", - "Examples" => Any[ - "inverse_problems/examples/ode_fitting_oscillation.md", - ], - ], - "Spatial modelling" => Any[ - "spatial_modelling/discrete_space_reaction_systems.md", - "spatial_modelling/discrete_space_simulation_structure_interaction.md", - "spatial_modelling/discrete_space_simulation_plotting.md", - "spatial_modelling/spatial_ode_simulations.md", - "spatial_modelling/spatial_jump_simulations.md", - ], - "FAQs" => "faqs.md", - "API" => Any[ - "api/core_api.md", - "api/network_analysis_api.md", - ], - "Developer Documentation" => "devdocs/dev_guide.md", +"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/math_models_intro.md" +], +"Model creation and properties" => Any[ + "model_creation/dsl_basics.md", + "model_creation/dsl_advanced.md", + "model_creation/programmatic_CRN_construction.md", + "model_creation/compositional_modeling.md", + "model_creation/coupled_non_crn_models.md", + "model_creation/events.md", + "model_creation/conservation_laws.md", + "model_creation/parametric_stoichiometry.md", + "model_creation/functional_parameters.md", + "model_creation/model_file_loading_and_export.md", + "model_creation/model_visualisation.md", + "model_creation/reactionsystem_content_accessing.md", + "model_creation/chemistry_related_functionality.md", + "Examples" => Any[ + "model_creation/examples/basic_CRN_library.md", + "model_creation/examples/programmatic_generative_linear_pathway.md", + "model_creation/examples/hodgkin_huxley_equation.md", + "model_creation/examples/smoluchowski_coagulation_equation.md", + "model_creation/examples/noise_modelling_approaches.md" + ] +], +"Model simulation and visualization" => Any[ + "model_simulation/simulation_introduction.md", + "model_simulation/simulation_plotting.md", + "model_simulation/simulation_structure_interfacing.md", + "model_simulation/ensemble_simulations.md", + "model_simulation/hybrid_simulations.md", + "model_simulation/ode_simulation_performance.md", + "model_simulation/sde_simulation_performance.md", + # "model_simulation/finite_state_projection_simulation.md", # Wait for update with new version. + "Examples" => Any[ + "model_simulation/examples/periodic_events_simulation.md", + "model_simulation/examples/activation_time_distribution_measurement.md", + "model_simulation/examples/interactive_brusselator_simulation.md" + ] +], +"Network Analysis" => Any[ + "network_analysis/odes.md", + "network_analysis/crn_theory.md", + "network_analysis/network_properties.md" +], +"Steady state analysis" => Any[ + "steady_state_functionality/homotopy_continuation.md", + "steady_state_functionality/nonlinear_solve.md", + "steady_state_functionality/steady_state_stability_computation.md", + "steady_state_functionality/bifurcation_diagrams.md", + "steady_state_functionality/dynamical_systems.md", + "Examples" => Any[ + "steady_state_functionality/examples/nullcline_plotting.md", + "steady_state_functionality/examples/bifurcationkit_periodic_orbits.md", + "steady_state_functionality/examples/bifurcationkit_codim2.md" + ] +], +"Inverse problems" => Any[ + "inverse_problems/petab_ode_param_fitting.md", + "inverse_problems/optimization_ode_param_fitting.md", + "inverse_problems/behaviour_optimisation.md", + "inverse_problems/structural_identifiability.md", + "inverse_problems/likelihood_profiler.md", + "inverse_problems/global_sensitivity_analysis.md", + "inverse_problems/turing_ode_param_fitting.md", + "Examples" => Any[ + "inverse_problems/examples/ode_fitting_oscillation.md" + ] +], +"Spatial modelling" => Any[ + "spatial_modelling/discrete_space_reaction_systems.md", + "spatial_modelling/discrete_space_simulation_structure_interaction.md", + "spatial_modelling/discrete_space_simulation_plotting.md", + "spatial_modelling/spatial_ode_simulations.md", + "spatial_modelling/spatial_jump_simulations.md" +], +"FAQs" => "faqs.md", +"API" => Any[ + "api/core_api.md", + "api/network_analysis_api.md" +], +"Developer Documentation" => "devdocs/dev_guide.md" ] diff --git a/docs/src/inverse_problems/global_sensitivity_analysis.md b/docs/src/inverse_problems/global_sensitivity_analysis.md index 870e381118..ea98f82263 100644 --- a/docs/src/inverse_problems/global_sensitivity_analysis.md +++ b/docs/src/inverse_problems/global_sensitivity_analysis.md @@ -35,7 +35,7 @@ p_setter = setp_oop(oprob_base, [:β, :γ]) # Creates a function that simulates the model an returns the peak number of cases. function peak_cases(p) - # Updates the ODEProblem with the proposed parameter set. + # Updates the ODEProblem with teh proposed parameter set. p = p_setter(oprob_base, p) oprob = remake(oprob_base; p) sol = solve(oprob; maxiters = 100000, verbose = false) diff --git a/docs/src/inverse_problems/optimization_ode_param_fitting.md b/docs/src/inverse_problems/optimization_ode_param_fitting.md index 94ba0be71f..e3b2d87cbf 100644 --- a/docs/src/inverse_problems/optimization_ode_param_fitting.md +++ b/docs/src/inverse_problems/optimization_ode_param_fitting.md @@ -38,11 +38,11 @@ p_setter = setp_oop(oprob_base, [:p, :d]) # A loss function measuring the sum-of-square distance between a simulation and the data. function loss(p, (oprob_base, p_setter, t_samples, X_samples)) - # Updates the ODEProblem with the proposed parameter set. + # Updates the ODEProblem with teh proposed parameter set. p = p_setter(oprob_base, p) oprob = remake(oprob_base; p) - # Simulate the model. If successful, return sum-of-squares distance as loss. + # Simulate the model. If sucesfull, return sum-of-squares distance as loss. sol = solve(oprob; saveat = t_samples, verbose = false, maxiters = 10000) SciMLBase.successful_retcode(sol) || return Inf return sum(abs2, sol[:X] .- X_samples) diff --git a/docs/src/model_creation/functional_parameters.md b/docs/src/model_creation/functional_parameters.md index 2c5946b767..61cb8e913a 100644 --- a/docs/src/model_creation/functional_parameters.md +++ b/docs/src/model_creation/functional_parameters.md @@ -34,7 +34,7 @@ bd_model = @reaction_network begin d, X --> 0 end -# Finally we simulate the model normally, but using `spline` as the `pIn` parameter's value. +# Finally we simualte the model normally, but using `spline` as the `pIn` parameter's value. u0 = [:X => 0.5] ps = [:d => 2.0, :pIn => spline] oprob = ODEProblem(bd_model, u0, tend, ps) diff --git a/ext/CatalystBifurcationKitExtension/bifurcation_kit_extension.jl b/ext/CatalystBifurcationKitExtension/bifurcation_kit_extension.jl index 56ab85fcc9..09e520fc84 100644 --- a/ext/CatalystBifurcationKitExtension/bifurcation_kit_extension.jl +++ b/ext/CatalystBifurcationKitExtension/bifurcation_kit_extension.jl @@ -1,10 +1,8 @@ ### Dispatch for BifurcationKit BifurcationProblems ### # Creates a BifurcationProblem, using a ReactionSystem as an input. -function BK.BifurcationProblem( - rs::ReactionSystem, u0_bif, ps, bif_par, args...; - plot_var = nothing, record_from_solution = BK.record_sol_default, jac = true, u0 = [], kwargs... - ) +function BK.BifurcationProblem(rs::ReactionSystem, u0_bif, ps, bif_par, args...; + plot_var = nothing, record_from_solution = BK.record_sol_default, jac = true, u0 = [], kwargs...) if !isautonomous(rs) error("Attempting to create a `BifurcationProblem` for a non-autonomous system (e.g. where some rate depend on $(get_iv(rs))). This is not possible.") end @@ -22,21 +20,19 @@ function BK.BifurcationProblem( u0 = Catalyst.symmap_to_varmap(rs, u0) end - # Creates nonlinear System. If there are conservation laws, these are manually added as + # Creates nonlinear System. If there are conservation laws, these are manually added as # equations, and the conservationlaw parameter values are manually computed and added to `ps`. Catalyst.conservationlaw_errorcheck(rs, merge(Dict(ps), Dict(u0))) nsys = if Catalyst.num_cons_laws(rs) == 0 complete(ss_ode_model(rs)) else - Γ_vals = Catalyst.get_networkproperties(rs).conservedconst => + Γ_vals = Catalyst.get_networkproperties(rs).conservedconst => [Symbolics.substitute(ceq.rhs, u0) for ceq in conservationlaw_constants(rs)] ps = merge(Dict(ps), Dict(Γ_vals)) complete(ss_ode_model(rs; remove_conserved = true, include_cl_as_eqs = true)) end # Makes BifurcationProblem (this call goes through the ModelingToolkit-based BifurcationKit extension). - return BK.BifurcationProblem( - nsys, u0_bif, ps, bif_par, args...; plot_var, - record_from_solution, jac, kwargs... - ) -end + return BK.BifurcationProblem(nsys, u0_bif, ps, bif_par, args...; plot_var, + record_from_solution, jac, kwargs...) +end \ No newline at end of file diff --git a/ext/CatalystCairoMakieExtension.jl b/ext/CatalystCairoMakieExtension.jl index e50df3bef9..aadbc6e1b6 100644 --- a/ext/CatalystCairoMakieExtension.jl +++ b/ext/CatalystCairoMakieExtension.jl @@ -3,7 +3,7 @@ module CatalystCairoMakieExtension # Fetch packages. using Catalyst, CairoMakie, SparseArrays import Catalyst: dspace_plot, dspace_animation, dspace_kymograph, - demask_vals, extract_vals, extract_grid_axes + demask_vals, extract_vals, extract_grid_axes # Creates and exports utilities for plotting discrete space simulations. include("CatalystCairoMakieExtension/cairo_makie_extension_spatial_modelling.jl") diff --git a/ext/CatalystCairoMakieExtension/cairo_makie_extension_spatial_modelling.jl b/ext/CatalystCairoMakieExtension/cairo_makie_extension_spatial_modelling.jl index b5593a6f13..dc87acdc55 100644 --- a/ext/CatalystCairoMakieExtension/cairo_makie_extension_spatial_modelling.jl +++ b/ext/CatalystCairoMakieExtension/cairo_makie_extension_spatial_modelling.jl @@ -3,16 +3,13 @@ # Internal dispatch for the plotting of a discrete space simulation on a 1d space (Cartesian or masked). function dspace_plot( sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:Catalyst.GridLattice{1, S}, T}; - t = sol.t[end], markersize = 20, kwargs... - ) where {Q, R, S, T} + t = sol.t[end], markersize = 20, kwargs...) where {Q, R, S, T} # Prepares and creates the plot. vals = spat_getu(sol, sp, dsrs; t = [t]) vals = Catalyst.demask_vals(vals, dsrs)[1] - return scatterlines( - vals; axis = (xlabel = "Compartment", ylabel = "$(sp)"), - markersize = markersize, kwargs... - ) + return scatterlines(vals; axis = (xlabel = "Compartment", ylabel = "$(sp)"), + markersize = markersize, kwargs...) end # Internal dispatch for the animation of a discrete space simulation on a 1d space (Cartesian or masked). @@ -20,8 +17,7 @@ function dspace_animation( sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:Catalyst.GridLattice{1, S}, T}, filename::String; markersize = 20, plot_min = nothing, plot_max = nothing, nframes = 200, framerate = 20, - ttitle = true, kwargs... - ) where {Q, R, S, T} + ttitle = true, kwargs...) where {Q, R, S, T} # Prepares the inputs to the figure. t = LinRange(sol.prob.tspan[1], sol.prob.tspan[2], nframes) @@ -39,11 +35,9 @@ function dspace_animation( (; title = Makie.@lift(string("Time: ", round(t[$frame]; sigdigits = 3)))), ) end - fig, ax, plt = scatterlines( - Makie.@lift(vals[$frame]); + fig, ax, plt = scatterlines(Makie.@lift(vals[$frame]); axis = axis_kwargs, - markersize = markersize, kwargs... - ) + markersize = markersize, kwargs...) # Creates the animation. record(fig, filename, 1:1:nframes; framerate) do i @@ -56,8 +50,7 @@ end function dspace_kymograph( sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:Catalyst.GridLattice{1, S}, T}; colormap = :BuGn_7, - plot_min = nothing, plot_max = nothing, nframes = 200, kwargs... - ) where {Q, R, S, T} + plot_min = nothing, plot_max = nothing, nframes = 200, kwargs...) where {Q, R, S, T} # Prepares the inputs to the figure. t = LinRange(sol.prob.tspan[1], sol.prob.tspan[2], nframes) @@ -66,28 +59,22 @@ function dspace_kymograph( # Creates the figure. y_vals = LinRange(1, grid_size(dsrs)[1], grid_size(dsrs)[1]) - return heatmap( - t, + return heatmap(t, y_vals, vals; - axis = ( - xlabel = "Time", ylabel = "Compartment", - xgridvisible = false, ygridvisible = false, - ), + axis = (xlabel = "Time", ylabel = "Compartment", + xgridvisible = false, ygridvisible = false), colormap, - colorrange = (plot_min, plot_max) - ) + colorrange = (plot_min, plot_max)) end ### 2d Lattice Simulation Plots/Animations ### # Internal dispatch for the plotting of a discrete space simulation on a 2d space (Cartesian or masked). -function dspace_plot( - sol, sp, +function dspace_plot(sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:Catalyst.GridLattice{2, S}, T}; t = sol.t[end], colormap = :BuGn_7, plot_min = nothing, plot_max = nothing, - kwargs... - ) where {Q, R, S, T} + kwargs...) where {Q, R, S, T} # Prepares the inputs to the figure (the `extract_vals` call only finds limits). _, plot_min, plot_max = Catalyst.extract_vals(sol, sp, dsrs, plot_min, plot_max, nothing) @@ -96,18 +83,14 @@ function dspace_plot( x_vals, y_vals = Catalyst.extract_grid_axes(dsrs) # Creates the figure. - return heatmap( - x_vals, + return heatmap(x_vals, y_vals, vals; - axis = ( - xlabel = "Compartment", ylabel = "Compartment", - xgridvisible = false, ygridvisible = false, - ), + axis = (xlabel = "Compartment", ylabel = "Compartment", + xgridvisible = false, ygridvisible = false), colormap, colorrange = (plot_min, plot_max), - kwargs... - ) + kwargs...) end # Internal dispatch for the animation of a discrete space simulation on a 2d space (Cartesian or masked). @@ -115,8 +98,7 @@ function dspace_animation( sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:Catalyst.GridLattice{2, S}, T}, filename::String; colormap = :BuGn_7, nframes = 200, framerate = 20, plot_min = nothing, - plot_max = nothing, ttitle::Bool = true, kwargs... - ) where {Q, R, S, T} + plot_max = nothing, ttitle::Bool = true, kwargs...) where {Q, R, S, T} # Prepares the inputs to the figure. t = LinRange(sol.prob.tspan[1], sol.prob.tspan[2], nframes) @@ -135,12 +117,10 @@ function dspace_animation( (; title = Makie.@lift(string("Time: ", round(t[$frame]; sigdigits = 3)))), ) end - fig, ax, hm = heatmap( - x_vals, y_vals, Makie.@lift(vals[$frame]); + fig, ax, hm = heatmap(x_vals, y_vals, Makie.@lift(vals[$frame]); axis = axis_kwargs, colormap, colorrange = (plot_min, plot_max), - kwargs... - ) + kwargs...) # Creates the animation. record(fig, filename, 1:1:nframes; framerate) do i @@ -154,15 +134,13 @@ end # Internal dispatch for the plotting of a discrete space simulation on a 3d space (Cartesian or masked). function dspace_plot( sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:Catalyst.GridLattice{3, S}, T}; - kwargs... - ) where {Q, R, S, T} + kwargs...) where {Q, R, S, T} throw(ArgumentError("The `dspace_plot` function does not support 3d Cartesian/masked spaces.")) end # Internal dispatch for the animation of a space simulation on a 3d space (Cartesian or masked). function dspace_animation( sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:Catalyst.GridLattice{3, S}, T}, - filename::String; kwargs... - ) where {Q, R, S, T} + filename::String; kwargs...) where {Q, R, S, T} throw(ArgumentError("The `dspace_animation` function does not support 3d Cartesian/masked spaces.")) end diff --git a/ext/CatalystGraphMakieExtension/graph_makie_extension_spatial_modelling.jl b/ext/CatalystGraphMakieExtension/graph_makie_extension_spatial_modelling.jl index d5d1c7f36f..e52e81dc6e 100644 --- a/ext/CatalystGraphMakieExtension/graph_makie_extension_spatial_modelling.jl +++ b/ext/CatalystGraphMakieExtension/graph_makie_extension_spatial_modelling.jl @@ -1,11 +1,9 @@ ### Graph Lattice Simulation Plots/Animations ### -# Internal dispatch for the plotting of a discrete space simulation on a unstructured (graph) space. -function dspace_plot( - sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:AbstractGraph, T}; +# Internal dispatch for the plotting of a discrete space simulation on a unstructured (graph) space. +function dspace_plot(sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:AbstractGraph, T}; t = sol.t[end], plot_min = nothing, plot_max = nothing, colormap = :BuGn_7, - node_size = 50, kwargs... - ) where {Q, R, T} + node_size = 50, kwargs...) where {Q, R, T} # Prepares the inputs to the figure. plot_graph = SimpleGraph(Catalyst.dspace(dsrs)) _, plot_min, plot_max = Catalyst.extract_vals(sol, sp, dsrs, plot_min, plot_max, nothing) @@ -13,18 +11,16 @@ function dspace_plot( vals = Catalyst.demask_vals(vals, dsrs)[1] println(kwargs) # Creates the figure. - return graphplot( - plot_graph; node_color = vals, + return graphplot(plot_graph; node_color = vals, node_attr = (colorrange = (plot_min, plot_max), colormap), node_size, kwargs... ) end -# Internal dispatch for the animation of a discrete space simulation on a unstructured (graph) space. +# Internal dispatch for the animation of a discrete space simulation on a unstructured (graph) space. function dspace_animation( sol, sp, dsrs::DiscreteSpaceReactionSystem{Q, R, <:AbstractGraph, T}, filename::String; t = sol.t[end], nframes = 200, framerate = 20, plot_min = nothing, plot_max = nothing, - colormap = :BuGn_7, node_size = 50, ttitle::Bool = true, kwargs... - ) where {Q, R, T} + colormap = :BuGn_7, node_size = 50, ttitle::Bool = true, kwargs...) where {Q, R, T} # Prepares the inputs to the figure. plot_graph = SimpleGraph(Catalyst.dspace(dsrs)) t = LinRange(sol.prob.tspan[1], sol.prob.tspan[2], nframes) @@ -37,10 +33,8 @@ function dspace_animation( else (;) end - fig, ax, plt = graphplot( - plot_graph; node_color = Makie.@lift(vals[$frame]), - node_attr = (colorrange = (plot_min, plot_max), colormap), node_size, axis_kwarg..., kwargs... - ) + fig, ax, plt = graphplot(plot_graph; node_color = Makie.@lift(vals[$frame]), + node_attr = (colorrange = (plot_min, plot_max), colormap), node_size, axis_kwarg..., kwargs...) # Creates the animation. GraphMakie.record(fig, filename, 1:1:nframes; framerate) do i diff --git a/ext/CatalystGraphMakieExtension/rn_graph_plot.jl b/ext/CatalystGraphMakieExtension/rn_graph_plot.jl index 8696d9898d..46a64381d4 100644 --- a/ext/CatalystGraphMakieExtension/rn_graph_plot.jl +++ b/ext/CatalystGraphMakieExtension/rn_graph_plot.jl @@ -33,21 +33,21 @@ function SRGraphWrap(rn::ReactionSystem) for spec in deps specidx = sm[spec] has_edge(srg, specidx, i + length(specs)) ? - push!(multiedges, Graphs.SimpleEdge(specidx, i + length(specs))) : - add_edge!(srg, Graphs.SimpleEdge(specidx, i + length(specs))) + push!(multiedges, Graphs.SimpleEdge(specidx, i + length(specs))) : + add_edge!(srg, Graphs.SimpleEdge(specidx, i + length(specs))) end end end edgelist = vcat(collect(Graphs.edges(srg)), multiedges) edgeorder = sortperm(edgelist) - return MultiGraphWrap(srg, multiedges, edgeorder) + MultiGraphWrap(srg, multiedges, edgeorder) end # Automatically set edge drawing order if not supplied function MultiGraphWrap(g::SimpleDiGraph{T}, multiedges::Vector{Graphs.SimpleEdge{T}}) where {T} edgelist = vcat(collect(Graphs.edges(g)), multiedges) edgeorder = sortperm(edgelist) - return MultiGraphWrap(g, multiedges, edgeorder) + MultiGraphWrap(g, multiedges, edgeorder) end # Return the multigraph and reaction order corresponding to the complex graph. The reaction order is the order of reactions(rn) that would match the edge order given by g.edgeorder. @@ -80,7 +80,7 @@ function ComplexGraphWrap(rn::ReactionSystem) for i in 2:length(edgelist) isequal(edgelist[i], edgelist[i - 1]) && push!(multiedges, edgelist[i]) end - return MultiGraphWrap(img, multiedges), rxorder + MultiGraphWrap(img, multiedges), rxorder end Base.eltype(g::MultiGraphWrap) = eltype(g.g) @@ -101,11 +101,11 @@ function Graphs.adjacency_matrix(g::MultiGraphWrap) for e in g.multiedges adj[src(e), dst(e)] = 1 end - return adj + adj end function Graphs.edges(g::MultiGraphWrap) - return edgelist = vcat(collect(Graphs.edges(g.g)), g.multiedges)[g.edgeorder] + edgelist = vcat(collect(Graphs.edges(g.g)), g.multiedges)[g.edgeorder] end function gen_distances(g::MultiGraphWrap; inc = 0.2) @@ -114,9 +114,9 @@ function gen_distances(g::MultiGraphWrap; inc = 0.2) edgedict = Dict(edgelist[1] => [1]) for (i, e) in enumerate(@view edgelist[2:end]) if edgelist[i] != edgelist[i + 1] - edgedict[e] = [i + 1] + edgedict[e] = [i+1] else - push!(edgedict[e], i + 1) + push!(edgedict[e], i+1) end end @@ -126,18 +126,18 @@ function gen_distances(g::MultiGraphWrap; inc = 0.2) inds_ = edgedict[Edge(dst(edge), src(edge))] len = length(inds) + length(inds_) - sp = -inc / 2 * (len - 1) - ep = sp + inc * (len - 1) + sp = -inc/2*(len-1) + ep = sp + inc*(len-1) dists = collect(sp:inc:ep) distances[inds] = dists[1:length(inds)] distances[inds_] = -dists[(length(inds) + 1):end] else - sp = -inc / 2 * (length(inds) - 1) - ep = sp + inc * (length(inds) - 1) + sp = -inc/2*(length(inds)-1) + ep = sp + inc*(length(inds)-1) distances[inds] = collect(sp:inc:ep) end end - return distances + distances end # Convert a species to a string label, handling array species properly. @@ -175,24 +175,18 @@ For a list of accepted keyword arguments to the graph plot, please see the [Grap function Catalyst.plot_network(rn::ReactionSystem; kwargs...) srg = SRGraphWrap(rn) ns = length(species(rn)) - nodecolors = vcat( - [:skyblue3 for i in 1:ns], - [:green for i in (ns + 1):nv(srg)] - ) - ilabels = vcat( - map(species_label, species(rn)), - ["R$i" for i in 1:(nv(srg) - ns)] - ) + nodecolors = vcat([:skyblue3 for i in 1:ns], + [:green for i in (ns + 1):nv(srg)]) + ilabels = vcat(map(species_label, species(rn)), + ["R$i" for i in 1:(nv(srg) - ns)]) ssm = substoichmat(rn) psm = prodstoichmat(rn) # Get stoichiometry of reaction edgelabels = map(Graphs.edges(srg.g)) do e - string( - src(e) > ns ? - psm[dst(e), src(e) - ns] : - ssm[src(e), dst(e) - ns] - ) + string(src(e) > ns ? + psm[dst(e), src(e) - ns] : + ssm[src(e), dst(e) - ns]) end edgecolors = [:black for i in 1:ne(srg)] @@ -212,8 +206,7 @@ function Catalyst.plot_network(rn::ReactionSystem; kwargs...) layout = if !haskey(kwargs, :layout) Stress() end - f = graphplot( - srg; + f = graphplot(srg; layout, edge_color = edgecolors, elabels = edgelabels, @@ -233,7 +226,7 @@ function Catalyst.plot_network(rn::ReactionSystem; kwargs...) hidespines!(f.axis) f.axis.aspect = DataAspect() - return f + f end """ @@ -270,8 +263,7 @@ function Catalyst.plot_complexes(rn::ReactionSystem; show_rate_labels::Bool = fa Stress() end - f = graphplot( - cg; + f = graphplot(cg; layout, edge_color = edgecolors, elabels = edgelabels, @@ -289,7 +281,7 @@ function Catalyst.plot_complexes(rn::ReactionSystem; show_rate_labels::Bool = fa hidespines!(f.axis) f.axis.aspect = DataAspect() - return f + f end function complexelem_tostr(e::Catalyst.ReactionComplexElement, specstrs) @@ -318,5 +310,5 @@ function complexlabels(rn::ReactionSystem) push!(labels, str) end end - return labels + labels end diff --git a/ext/CatalystHomotopyContinuationExtension/homotopy_continuation_extension.jl b/ext/CatalystHomotopyContinuationExtension/homotopy_continuation_extension.jl index 004229ad0f..9e15686fc9 100644 --- a/ext/CatalystHomotopyContinuationExtension/homotopy_continuation_extension.jl +++ b/ext/CatalystHomotopyContinuationExtension/homotopy_continuation_extension.jl @@ -35,10 +35,8 @@ Notes: - Homotopy-based steady state finding only works when all rates are rational polynomials (e.g. constant, linear, mm, or hill functions). ``` """ -function Catalyst.hc_steady_states( - rs::ReactionSystem, ps; filter_negative = true, - neg_thres = -1.0e-15, u0 = [], kwargs... - ) +function Catalyst.hc_steady_states(rs::ReactionSystem, ps; filter_negative = true, + neg_thres = -1e-15, u0 = [], kwargs...) if !isautonomous(rs) error("Attempting to compute steady state for a non-autonomous system (e.g. where some rate depend on $(get_iv(rs))). This is not possible.") end @@ -57,20 +55,20 @@ function steady_state_polynomial(rs::ReactionSystem, ps, u0) pre_varmap = merge(Dict(Catalyst.symmap_to_varmap(rs, u0)), Dict(Catalyst.symmap_to_varmap(rs, ps))) Catalyst.conservationlaw_errorcheck(rs, pre_varmap) for u in setdiff(unknowns(rs), Catalyst.conslaw_species(rs)) - haskey(pre_varmap, u) || (pre_varmap[u] = -1.0) + haskey(pre_varmap, u) || (pre_varmap[u] = -1.0) end nprob_dummy = NonlinearProblem(ns, pre_varmap) p_dict_pre = Dict(p => nprob_dummy.ps[p] for p in parameters(ns)) # Expands vector variable stored in the dictionary so they can be substituted into the polynomial (e.g. from `Γ` to `Γ[1]` and `Γ[2]`) p_dict = typeof(p_dict_pre)() - for (k, v) in p_dict_pre + for (k,v) in p_dict_pre if v isa AbstractArray - for i in 1:length(v) + for i = 1:length(v) p_dict[k[i]] = v[i] end else - p_dict[k] = v + p_dict[k] = v end end @@ -108,11 +106,11 @@ end # Parses and expression and return a version where any exponents that are Float64 (but an int, like 2.0) are turned into Int64s. function make_int_exps(expr) - return wrap(Rewriters.Postwalk(Rewriters.PassThrough(___make_int_exps))(unwrap(expr))).val + wrap(Rewriters.Postwalk(Rewriters.PassThrough(___make_int_exps))(unwrap(expr))).val end function ___make_int_exps(expr) !iscall(expr) && return expr - return if (operation(expr) == ^) + if (operation(expr) == ^) if _isinteger(sorted_arguments(expr)[2]) return sorted_arguments(expr)[1]^Int64(Symbolics.value(sorted_arguments(expr)[2])) else @@ -181,11 +179,11 @@ function reorder_sols!(sols, ss_poly, rs::ReactionSystem) var_names_extended = String.(Symbol.(HC.variables(ss_poly))) var_names = [Symbol(s[1:prevind(s, findlast('_', s))]) for s in var_names_extended] sort_pattern = indexin(MT.getname.(unknowns(rs)), var_names) - return foreach(sol -> permute!(sol, sort_pattern), sols) + foreach(sol -> permute!(sol, sort_pattern), sols) end # Filters away solutions with negative species concentrations (and for neg_thres < val < 0.0, sets val=0.0). -function filter_negative_f(sols; neg_thres = -1.0e-15) +function filter_negative_f(sols; neg_thres = -1e-15) for sol in sols, idx in 1:length(sol) (neg_thres < sol[idx] < 0) && (sol[idx] = 0) @@ -195,36 +193,29 @@ end # Sometimes (when polynomials are created from coupled CRN/DAEs), the steady state polynomial have the wrong type. # This converts it to the correct type, which homotopy continuation can handle. -const WRONG_POLY_TYPE = Vector{ - DynamicPolynomials.Polynomial{ - DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, - DynamicPolynomials.Graded{DynamicPolynomials.LexOrder}, - }, -} -const CORRECT_POLY_TYPE = Vector{ - DynamicPolynomials.Polynomial{ - DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, - DynamicPolynomials.Graded{DynamicPolynomials.LexOrder}, Float64, - }, -} +const WRONG_POLY_TYPE = Vector{DynamicPolynomials.Polynomial{ + DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, + DynamicPolynomials.Graded{DynamicPolynomials.LexOrder}}} +const CORRECT_POLY_TYPE = Vector{DynamicPolynomials.Polynomial{ + DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, + DynamicPolynomials.Graded{DynamicPolynomials.LexOrder}, Float64}} function poly_type_convert(ss_poly) (typeof(ss_poly) == WRONG_POLY_TYPE) && return convert(CORRECT_POLY_TYPE, ss_poly) return ss_poly end + ### SAVED ARCHIVED MTK FUNCTION - REMOVE SOME TIME ### # pre-v10 version of function -function varmap_to_vars_mtkv9( - varmap, varlist; defaults = Dict(), check = true, +function varmap_to_vars_mtkv9(varmap, varlist; defaults = Dict(), check = true, toterm = MT.default_toterm, promotetoconcrete = nothing, - tofloat = true, use_union = true - ) + tofloat = true, use_union = true) varlist = collect(map(unwrap, varlist)) # Edge cases where one of the arguments is effectively empty. is_incomplete_initialization = varmap isa DiffEqBase.NullParameters || - varmap === nothing + varmap === nothing if is_incomplete_initialization || isempty(varmap) if isempty(defaults) if !is_incomplete_initialization && check @@ -247,10 +238,8 @@ function varmap_to_vars_mtkv9( vals = if eltype(varmap) <: Pair # `varmap` is a dict or an array of pairs varmap = MT.todict(varmap) - _varmap_to_vars_mtkv9( - varmap, varlist; defaults = defaults, check = check, - toterm = toterm - ) + _varmap_to_vars_mtkv9(varmap, varlist; defaults = defaults, check = check, + toterm = toterm) else # plain array-like initialization varmap end @@ -265,17 +254,13 @@ function varmap_to_vars_mtkv9( elseif container_type <: Tuple (vals...,) else - SymbolicUtils.Code.create_array( - container_type, eltype(vals), Val{1}(), - Val(length(vals)), vals... - ) + SymbolicUtils.Code.create_array(container_type, eltype(vals), Val{1}(), + Val(length(vals)), vals...) end end -function _varmap_to_vars_mtkv9( - varmap::Dict, varlist; defaults = Dict(), check = false, - toterm = Symbolics.diff2term, initialization_phase = false - ) +function _varmap_to_vars_mtkv9(varmap::Dict, varlist; defaults = Dict(), check = false, + toterm = Symbolics.diff2term, initialization_phase = false) varmap = canonicalize_varmap_mtkv9(varmap; toterm) defaults = canonicalize_varmap_mtkv9(defaults; toterm) varmap = merge(defaults, varmap) diff --git a/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl b/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl index 530bc262f4..24bdb7efd1 100644 --- a/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl +++ b/ext/CatalystStructuralIdentifiabilityExtension/structural_identifiability_extension.jl @@ -26,17 +26,13 @@ Notes: - This function is part of the StructuralIdentifiability.jl extension. StructuralIdentifiability.jl must be imported to access it. - `measured_quantities` and `known_p` input may also be symbolic (e.g. measured_quantities = [rs.X]) """ -function Catalyst.make_si_ode( - rs::ReactionSystem; measured_quantities = [], known_p = [], - ignore_no_measured_warn = false, remove_conserved = true - ) +function Catalyst.make_si_ode(rs::ReactionSystem; measured_quantities = [], known_p = [], + ignore_no_measured_warn = false, remove_conserved = true) # Creates a MTK ODE System, and a list of measured quantities (there are equations). # Gives these to SI to create an SI ode model of its preferred form. osys, conseqs, _, _ = make_osys(rs; remove_conserved) - measured_quantities = make_measured_quantities( - rs, measured_quantities, known_p, - conseqs; ignore_no_measured_warn - ) + measured_quantities = make_measured_quantities(rs, measured_quantities, known_p, + conseqs; ignore_no_measured_warn) return SI.mtk_to_si(osys, measured_quantities)[1] end @@ -67,24 +63,18 @@ Notes: - This function is part of the StructuralIdentifiability.jl extension. StructuralIdentifiability.jl must be imported to access it. - `measured_quantities` and `known_p` input may also be symbolic (e.g. measured_quantities = [rs.X]) """ -function SI.assess_local_identifiability( - rs::ReactionSystem, args...; +function SI.assess_local_identifiability(rs::ReactionSystem, args...; measured_quantities = [], known_p = [], funcs_to_check = Vector(), - remove_conserved = true, ignore_no_measured_warn = false, kwargs... - ) + remove_conserved = true, ignore_no_measured_warn = false, kwargs...) # Creates an ODE System, list of measured quantities, and functions to check, of SI's preferred form. osys, conseqs, consconsts, vars = make_osys(rs; remove_conserved) - measured_quantities = make_measured_quantities( - rs, measured_quantities, known_p, - conseqs; ignore_no_measured_warn - ) + measured_quantities = make_measured_quantities(rs, measured_quantities, known_p, + conseqs; ignore_no_measured_warn) funcs_to_check = make_ftc(funcs_to_check, conseqs, vars) # Computes identifiability and converts it to a easy to read form. - out = SI.assess_local_identifiability( - osys, args...; measured_quantities, - funcs_to_check, kwargs... - ) + out = SI.assess_local_identifiability(osys, args...; measured_quantities, + funcs_to_check, kwargs...) return make_output(out, funcs_to_check, consconsts) end @@ -113,26 +103,20 @@ Notes: - This function is part of the StructuralIdentifiability.jl extension. StructuralIdentifiability.jl must be imported to access it. - `measured_quantities` and `known_p` input may also be symbolic (e.g. measured_quantities = [rs.X]) """ -function SI.assess_identifiability( - rs::ReactionSystem, args...; +function SI.assess_identifiability(rs::ReactionSystem, args...; measured_quantities = [], known_p = [], funcs_to_check = Vector(), - remove_conserved = true, ignore_no_measured_warn = false, kwargs... - ) + remove_conserved = true, ignore_no_measured_warn = false, kwargs...) # Creates an ODE System, list of measured quantities, and functions to check, of SI's preferred form. osys, conseqs, consconsts, vars = make_osys(rs; remove_conserved) - measured_quantities = make_measured_quantities( - rs, measured_quantities, known_p, - conseqs; ignore_no_measured_warn - ) + measured_quantities = make_measured_quantities(rs, measured_quantities, known_p, + conseqs; ignore_no_measured_warn) funcs_to_check = make_ftc(funcs_to_check, conseqs, vars) # Computes identifiability and converts it to a easy to read form. # The `::System` designation fixes: https://github.com/SciML/StructuralIdentifiability.jl/issues/360, # however, the exact mechanisms of this is still not fully clear. - out = SI.assess_identifiability( - osys::System, args...; measured_quantities, - funcs_to_check, kwargs... - ) + out = SI.assess_identifiability(osys::System, args...; measured_quantities, + funcs_to_check, kwargs...) return make_output(out, funcs_to_check, consconsts) end @@ -161,17 +145,13 @@ Notes: - This function is part of the StructuralIdentifiability.jl extension. StructuralIdentifiability.jl must be imported to access it. - `measured_quantities` and `known_p` input may also be symbolic (e.g. measured_quantities = [rs.X]) """ -function SI.find_identifiable_functions( - rs::ReactionSystem, args...; +function SI.find_identifiable_functions(rs::ReactionSystem, args...; measured_quantities = [], known_p = [], remove_conserved = true, - ignore_no_measured_warn = false, kwargs... - ) + ignore_no_measured_warn = false, kwargs...) # Creates an ODE System, and list of measured quantities, of SI's preferred form. osys, conseqs, consconsts, _ = make_osys(rs; remove_conserved) - measured_quantities = make_measured_quantities( - rs, measured_quantities, known_p, - conseqs; ignore_no_measured_warn - ) + measured_quantities = make_measured_quantities(rs, measured_quantities, known_p, + conseqs; ignore_no_measured_warn) # Computes identifiable functions and converts it to a easy to read form. out = SI.find_identifiable_functions(osys, args...; measured_quantities, kwargs...) @@ -210,10 +190,8 @@ end # Creates a list of measured quantities of a form that SI can read. # Each measured quantity must have a form like: # `obs_var ~ X` # (Here, `obs_var` is a variable, and X is whatever we can measure). -function make_measured_quantities( - rs::ReactionSystem, measured_quantities::Vector{T}, - known_p::Vector{S}, conseqs; ignore_no_measured_warn = false - ) where {T, S} +function make_measured_quantities(rs::ReactionSystem, measured_quantities::Vector{T}, + known_p::Vector{S}, conseqs; ignore_no_measured_warn = false) where {T, S} # Warning if the user didn't give any measured quantities. if !ignore_no_measured_warn && isempty(measured_quantities) @warn "No measured quantity provided to the `measured_quantities` argument, any further identifiability analysis will likely fail. You can disable this warning by setting `ignore_no_measured_warn = true`." @@ -227,10 +205,8 @@ function make_measured_quantities( # Creates one internal observation variable for each measured quantity (`___internal_observables`). # Creates a vector of equations, setting each measured quantity equal to one observation variable. @variables (___internal_observables(Catalyst.get_iv(rs)))[1:length(mqs)] - return Equation[ - (q isa Equation) ? q : (___internal_observables[i] ~ q) - for (i, q) in enumerate(mqs) - ] + return Equation[(q isa Equation) ? q : (___internal_observables[i] ~ q) + for (i, q) in enumerate(mqs)] end # Creates the functions that we wish to check for identifiability. diff --git a/src/Catalyst.jl b/src/Catalyst.jl index e91211d7bb..2339490f58 100644 --- a/src/Catalyst.jl +++ b/src/Catalyst.jl @@ -9,8 +9,8 @@ import SciMLBase using LaTeXStrings, Latexify using LinearAlgebra, Combinatorics using JumpProcesses: JumpProcesses, JumpProblem, - MassActionJump, ConstantRateJump, VariableRateJump, - SpatialMassActionJump, CartesianGrid, CartesianGridRej + MassActionJump, ConstantRateJump, VariableRateJump, + SpatialMassActionJump, CartesianGrid, CartesianGridRej # ModelingToolkit imports and convenience functions we use using ModelingToolkitBase @@ -26,14 +26,14 @@ RuntimeGeneratedFunctions.init(@__MODULE__) import Symbolics: SymbolicT using Symbolics: iscall, sorted_arguments, value using ModelingToolkitBase: get_unknowns, get_ps, get_iv, get_systems, - get_eqs, toparam, get_var_to_name, get_observed, - getvar, has_iv, JumpType + get_eqs, toparam, get_var_to_name, get_observed, + getvar, has_iv, JumpType import ModelingToolkitBase: get_variables, namespace_expr, namespace_equation, - modified_unknowns!, namespace_variables, - namespace_parameters, renamespace, flatten, - is_alg_equation, is_diff_equation, collect_vars!, - eqtype_supports_collect_vars + modified_unknowns!, namespace_variables, + namespace_parameters, renamespace, flatten, + is_alg_equation, is_diff_equation, collect_vars!, + eqtype_supports_collect_vars # Import from owner modules (not re-exporters) per ExplicitImports.jl audit import Symbolics: get_variables!, rename @@ -43,7 +43,7 @@ import ModelingToolkitBase: SymmapT # internal but needed ModelingToolkit functions import ModelingToolkitBase: check_variables, check_parameters, - check_equations, iscomplete + check_equations, iscomplete # Import from owner module (SymbolicUtils) per ExplicitImports.jl audit import SymbolicUtils: _iszero, unwrap @@ -81,10 +81,8 @@ const CONSERVED_CONSTANT_SYMBOL = :Γ # Declares symbols which may neither be used as parameters nor unknowns. const forbidden_symbols_skip = Set([:ℯ, :pi, :π, :t, :∅, :Ø]) -const forbidden_symbols_error = union( - Set([:im, :nothing, CONSERVED_CONSTANT_SYMBOL]), - forbidden_symbols_skip -) +const forbidden_symbols_error = union(Set([:im, :nothing, CONSERVED_CONSTANT_SYMBOL]), + forbidden_symbols_skip) ### Unit Helpers ### @@ -131,7 +129,7 @@ include("reactionsystem_metadata.jl") # Conversions of the `ReactionSystem` structure. include("reactionsystem_conversions.jl") export ODEProblem, SDEProblem, JumpProblem, NonlinearProblem, - SteadyStateProblem, HybridProblem + SteadyStateProblem, HybridProblem export ismassaction, oderatelaw, jumpratelaw # reaction_network macro @@ -143,15 +141,15 @@ export @reaction_network, @network_component, @reaction, @species include("network_analysis.jl") export reactioncomplexmap, reactioncomplexes, incidencemat export complexstoichmat, laplacianmat, fluxmat, massactionvector, complexoutgoingmat, - adjacencymat + adjacencymat export incidencematgraph, linkageclasses, stronglinkageclasses, - terminallinkageclasses, deficiency, subnetworks + terminallinkageclasses, deficiency, subnetworks export linkagedeficiencies, isreversible, isweaklyreversible export conservationlaws, conservedquantities, conservedequations, conservationlaw_constants export satisfiesdeficiencyone, satisfiesdeficiencyzero export iscomplexbalanced, isdetailedbalanced, robustspecies -# Contains the `nullspace` function required for conservation law elimination. +# Containes the `nullspace` function required for conservation law elimination. include("mtk_nullspace_function.jl") # registers CRN specific functions using Symbolics.jl @@ -204,7 +202,7 @@ export DiscreteSpaceReactionSystem export spatial_species, vertex_parameters, edge_parameters export CartesianGrid, CartesianGridReJ # (Implemented in JumpProcesses) export has_cartesian_dspace, has_masked_dspace, has_grid_dspace, has_graph_dspace, - grid_dims, grid_size + grid_dims, grid_size export make_edge_p_values, make_directed_edge_values # Specific spatial problem types. diff --git a/src/chemistry_functionality.jl b/src/chemistry_functionality.jl index 1ba1c640fd..6ec8c76450 100644 --- a/src/chemistry_functionality.jl +++ b/src/chemistry_functionality.jl @@ -16,7 +16,7 @@ Returns `true` if the input is a compound species (else false). """ iscompound(s::Num) = iscompound(value(s)) function iscompound(s) - return MT.getmetadata(s, CompoundSpecies, false) + MT.getmetadata(s, CompoundSpecies, false) end """ @@ -26,7 +26,7 @@ Returns a vector with a list of all the components of a compound species (create """ components(s::Num) = components(value(s)) function components(s) - return MT.getmetadata(s, CompoundComponents) + MT.getmetadata(s, CompoundComponents) end """ @@ -36,7 +36,7 @@ Returns a vector with a list of all the stoichiometric coefficients of the compo """ coefficients(s::Num) = coefficients(value(s)) function coefficients(s) - return MT.getmetadata(s, CompoundCoefficients) + MT.getmetadata(s, CompoundCoefficients) end """ @@ -67,7 +67,7 @@ Notes: - The component species must be defined before using the `@compound` macro. """ macro compound(expr) - return make_compound(striplines(expr)) + make_compound(striplines(expr)) end # Declares compound error messages: @@ -85,10 +85,8 @@ function make_compound(expr) # Loops through all components, add the component and the coefficients to the corresponding vectors # Cannot extract directly using e.g. "getfield.(composition, :reactant)" because then # we get something like :([:C, :O]), rather than :([C, O]). - composition = Catalyst.recursive_find_reactants!( - expr.args[3], 1, - Vector{DSLReactant}(undef, 0) - ) + composition = Catalyst.recursive_find_reactants!(expr.args[3], 1, + Vector{DSLReactant}(undef, 0)) components = :([]) # Becomes something like :([C, O]). coefficients = :([]) # Becomes something like :([1, 2]). for comp in composition @@ -105,16 +103,9 @@ function make_compound(expr) # If no ivs were given, inserts an expression which evaluates to the union of the ivs # for the species the compound depends on. - ivs_get_expr = :( - unique( - reduce( - vcat, ( - Symbolics.sorted_arguments(Symbolics.unwrap(comp)) - for comp in $components - ) - ) - ) - ) + ivs_get_expr = :(unique(reduce( + vcat, (Symbolics.sorted_arguments(Symbolics.unwrap(comp)) + for comp in $components)))) if isempty(ivs) species_expr = Catalyst.insert_independent_variable(species_expr, :($ivs_get_expr...)) end @@ -131,48 +122,23 @@ function make_compound(expr) # `CO2 = ModelingToolkitBase.setmetadata(CO2, Catalyst.CompoundSpecies, [1, 2])` # `CO2 = ModelingToolkitBase.wrap(CO2)` species_declaration_expr = Expr(:escape, :(@species $species_expr)) - multiple_ivs_error_check_expr = Expr( - :escape, - :( - $(isempty(ivs)) && (length($ivs_get_expr) > 1) && - error($COMPOUND_CREATION_ERROR_DEPENDENT_VAR_REQUIRED) - ) - ) - iv_check_expr = Expr( - :escape, - :( - issetequal(Symbolics.arguments(Symbolics.unwrap($species_name)), $ivs_get_expr) || - error("The independent variable(S) provided to the compound ($(Symbolics.arguments(Symbolics.unwrap($species_name)))), and those of its components ($($ivs_get_expr)))), are not identical.") - ) - ) - compound_designation_expr = Expr( - :escape, - :( - $species_name = ModelingToolkitBase.setmetadata( - $species_name, Catalyst.CompoundSpecies, true - ) - ) - ) - components_designation_expr = Expr( - :escape, - :( - $species_name = ModelingToolkitBase.setmetadata( - $species_name, Catalyst.CompoundComponents, $components - ) - ) - ) - coefficients_designation_expr = Expr( - :escape, - :( - $species_name = ModelingToolkitBase.setmetadata( - $species_name, Catalyst.CompoundCoefficients, $coefficients - ) - ) - ) - compound_wrap_expr = Expr( - :escape, - :($species_name = ModelingToolkitBase.wrap($species_name)) - ) + multiple_ivs_error_check_expr = Expr(:escape, + :($(isempty(ivs)) && (length($ivs_get_expr) > 1) && + error($COMPOUND_CREATION_ERROR_DEPENDENT_VAR_REQUIRED))) + iv_check_expr = Expr(:escape, + :(issetequal(Symbolics.arguments(Symbolics.unwrap($species_name)), $ivs_get_expr) || + error("The independent variable(S) provided to the compound ($(Symbolics.arguments(Symbolics.unwrap($species_name)))), and those of its components ($($ivs_get_expr)))), are not identical."))) + compound_designation_expr = Expr(:escape, + :($species_name = ModelingToolkitBase.setmetadata( + $species_name, Catalyst.CompoundSpecies, true))) + components_designation_expr = Expr(:escape, + :($species_name = ModelingToolkitBase.setmetadata( + $species_name, Catalyst.CompoundComponents, $components))) + coefficients_designation_expr = Expr(:escape, + :($species_name = ModelingToolkitBase.setmetadata( + $species_name, Catalyst.CompoundCoefficients, $coefficients))) + compound_wrap_expr = Expr(:escape, + :($species_name = ModelingToolkitBase.wrap($species_name))) # Returns the rephrased expression. return quote @@ -207,7 +173,7 @@ Notes: - The component species must be defined before using the `@compound` macro. """ macro compounds(expr) - return make_compounds(striplines(expr)) + make_compounds(striplines(expr)) end # Function managing the @compound macro. @@ -233,12 +199,8 @@ function make_compounds(expr) push!(compound_declarations.args, :($(Expr(:escape, :($(compound_syms)))))) # The output needs to be converted to Vector{Num} (from Vector{SymbolicUtils.SymbolicT}) to be consistent with e.g. @variables. - compound_declarations.args[end] = :( - [ - ModelingToolkitBase.wrap(cmp) - for cmp in $(compound_declarations.args[end]) - ] - ) + compound_declarations.args[end] = :([ModelingToolkitBase.wrap(cmp) + for cmp in $(compound_declarations.args[end])]) # Returns output that. return compound_declarations @@ -305,10 +267,8 @@ function balance_reaction(reaction::Reaction) prodstoich = stoich[(length(reaction.substrates) + 1):end] # Create a new reaction with the balanced stoichiometries - balancedrx = Reaction( - reaction.rate, reaction.substrates, reaction.products, - substoich, prodstoich - ) + balancedrx = Reaction(reaction.rate, reaction.substrates, reaction.products, + substoich, prodstoich) # Add the reaction to the vector of all reactions balancedrxs[i] = balancedrx diff --git a/src/dsl.jl b/src/dsl.jl index 1c4b2708f4..88a2c8b6c3 100644 --- a/src/dsl.jl +++ b/src/dsl.jl @@ -8,11 +8,9 @@ const double_arrows = Set{Symbol}([:↔, :⟷, :⇄, :⇆, :⇌, :⇋, :⇔, : const pure_rate_arrows = Set{Symbol}([:(=>), :(<=), :⇐, :⟽, :⇒, :⟾, :⇔, :⟺]) # Declares the keys used for various options. -const option_keys = ( - :species, :parameters, :variables, :discretes, :ivs, :compounds, :observables, +const option_keys = (:species, :parameters, :variables, :discretes, :ivs, :compounds, :observables, :default_noise_scaling, :differentials, :equations, :continuous_events, :discrete_events, - :tstops, :brownians, :poissonians, :combinatoric_ratelaws, :require_declaration, :unit_checks, -) + :tstops, :brownians, :poissonians, :combinatoric_ratelaws, :require_declaration, :unit_checks) ### `@species` Macro ### @@ -65,17 +63,17 @@ of observables). Each option is designated by a tag starting with a `@` followed A list of options can be found [here](https://docs.sciml.ai/Catalyst/stable/api/#api_dsl_options). """ macro reaction_network(name::Symbol, network_expr::Expr) - return make_rs_expr(QuoteNode(name), network_expr) + make_rs_expr(QuoteNode(name), network_expr) end # The case where the name contains an interpolation. macro reaction_network(name::Expr, network_expr::Expr) - return make_rs_expr(esc(name.args[1]), network_expr) + make_rs_expr(esc(name.args[1]), network_expr) end # The case where nothing, or only a name, is provided. macro reaction_network(name::Symbol = gensym(:ReactionSystem)) - return make_rs_expr(QuoteNode(name)) + make_rs_expr(QuoteNode(name)) end # Handles two disjoint cases. @@ -93,17 +91,17 @@ Equivalent to `@reaction_network` except the generated `ReactionSystem` is not m complete. """ macro network_component(name::Symbol, network_expr::Expr) - return make_rs_expr(QuoteNode(name), network_expr; complete = false) + make_rs_expr(QuoteNode(name), network_expr; complete = false) end # The case where the name contains an interpolation. macro network_component(name::Expr, network_expr::Expr) - return make_rs_expr(esc(name.args[1]), network_expr; complete = false) + make_rs_expr(esc(name.args[1]), network_expr; complete = false) end # The case where nothing, or only a name, is provided. macro network_component(name::Symbol = gensym(:ReactionSystem)) - return make_rs_expr(QuoteNode(name); complete = false) + make_rs_expr(QuoteNode(name); complete = false) end # Handles two disjoint cases. @@ -148,14 +146,12 @@ struct DSLReaction metadata::Expr rxexpr::Expr - function DSLReaction( - sub_line::ExprValues, prod_line::ExprValues, - rate::ExprValues, metadata_line::ExprValues, rx_line::Expr - ) + function DSLReaction(sub_line::ExprValues, prod_line::ExprValues, + rate::ExprValues, metadata_line::ExprValues, rx_line::Expr) subs = recursive_find_reactants!(sub_line, 1, Vector{DSLReactant}(undef, 0)) prods = recursive_find_reactants!(prod_line, 1, Vector{DSLReactant}(undef, 0)) metadata = extract_metadata(metadata_line) - return new(subs, prods, rate, metadata, rx_line) + new(subs, prods, rate, metadata, rx_line) end end @@ -163,10 +159,8 @@ end # stoichiometry. Recursion makes it able to handle weird cases like 2(X + Y + 3(Z + XY)). The # reactants are stored in the `reactants` vector. As the expression tree is parsed, the # stoichiometry is updated and new reactants are added. -function recursive_find_reactants!( - ex::ExprValues, mult::ExprValues, - reactants::Vector{DSLReactant} - ) +function recursive_find_reactants!(ex::ExprValues, mult::ExprValues, + reactants::Vector{DSLReactant}) # We have reached the end of the expression tree and can finalise and return the reactants. if (typeof(ex) != Expr) || (ex.head == :escape) || (ex.head == :ref) # The final bit of the expression is not a relevant reactant, no additions are required. @@ -202,13 +196,13 @@ function recursive_find_reactants!( else throw("Malformed reaction, bad operator: $(ex.args[1]) found in stoichiometry expression $ex.") end - return reactants + reactants end # Helper function for updating the multiplicity throughout recursion (handles e.g. parametric # stoichiometries). The `op` argument is an operation (e.g. `*`, but could also e.g. be `+`). function processmult(op, mult, stoich) - return if (mult isa Number) && (stoich isa Number) + if (mult isa Number) && (stoich isa Number) op(mult, stoich) else :($op($mult, $stoich)) @@ -236,7 +230,7 @@ end function Base.showerror(io::IO, err::UndeclaredSymbolicError) print(io, "UndeclaredSymbolicError: ") - return print(io, err.msg) + print(io, err.msg) end ### DSL Internal Master Function ### @@ -256,7 +250,7 @@ function make_reaction_system(ex::Expr, name) error("@reaction_network input contain $(length(ex.args) - numlines) malformed lines.") options = Dict(Symbol(String(arg.args[1])[2:end]) => arg for arg in option_lines) any(!in(option_keys), keys(options)) && - error("The following unsupported options were used: $(filter(opt_in -> !in(opt_in, option_keys), keys(options)))") + error("The following unsupported options were used: $(filter(opt_in->!in(opt_in,option_keys), keys(options)))") # Read options that explicitly declare some symbol (e.g. `@species`). Compiles a list of # all declared symbols and checks that there have been no double-declarations. @@ -269,14 +263,8 @@ function make_reaction_system(ex::Expr, name) diffsexpr, diffs_declared = read_differentials_option(options) brownsexpr_init, browns_declared = read_brownians_option(options) poissexpr_init, poiss_declared = read_poissonians_option(options) - syms_declared = collect( - Iterators.flatten( - ( - cmps_declared, sps_declared, ps_declared, - vs_declared, discs_declared, ivs, diffs_declared, browns_declared, poiss_declared, - ) - ) - ) + syms_declared = collect(Iterators.flatten((cmps_declared, sps_declared, ps_declared, + vs_declared, discs_declared, ivs, diffs_declared, browns_declared, poiss_declared))) if !allunique(syms_declared) nonunique_syms = [s for s in syms_declared if count(x -> x == s, syms_declared) > 1] error("The following symbols $(unique(nonunique_syms)) have explicitly been declared as multiple types of components (e.g. occur in at least two of the `@species`, `@parameters`, `@variables`, `@ivs`, `@compounds`, `@differentials`). This is not allowed.") @@ -286,18 +274,14 @@ function make_reaction_system(ex::Expr, name) requiredec = haskey(options, :require_declaration) reactions = get_reactions(reaction_lines) sps_inferred, ps_pre_inferred, stoich_ps = extract_sps_and_ps(reactions, syms_declared; requiredec) - vs_inferred, diffs_inferred, equations = read_equations_option!( - diffsexpr, options, - union(syms_declared, sps_inferred), tiv; requiredec - ) + vs_inferred, diffs_inferred, equations = read_equations_option!(diffsexpr, options, + union(syms_declared, sps_inferred), tiv; requiredec) ps_inferred = setdiff(ps_pre_inferred, vs_inferred, diffs_inferred) syms_inferred = union(sps_inferred, ps_inferred, vs_inferred, diffs_inferred) all_syms = union(syms_declared, syms_inferred) validate_poissonian_rate_syms(options, all_syms) - obsexpr, obs_eqs, obs_syms = read_observables_option( - options, ivs, - union(sps_declared, vs_declared), all_syms; requiredec - ) + obsexpr, obs_eqs, obs_syms = read_observables_option(options, ivs, + union(sps_declared, vs_declared), all_syms; requiredec) # Read options not related to the declaration or inference of symbols. discs_inferred = Vector{Symbol}() @@ -324,48 +308,41 @@ function make_reaction_system(ex::Expr, name) # Assemblies the full expression that declares all required symbolic variables, and # then the output `ReactionSystem`. - return MacroTools.flatten( - striplines( - quote - # Inserts the expressions which generate the `ReactionSystem` input. - $ivsexpr - $psexpr - $vsexpr - $spsexpr - $discsexpr - $obsexpr - $cmpsexpr - $diffsexpr - $brownsexpr - $poissexpr - - # Stores each kwarg in a variable. Not necessary, but useful when debugging generated code. - name = $name - spatial_ivs = $sivs - _unit_checks = $unit_checks - rx_eq_vec = $rxsexprs - us = setdiff(union($spsvar, $vsvar, $cmpsvar), $obs_syms) - ps = union($psvar, $discsvar) - _observed = $obs_eqs - _continuous_events = $continuous_events_expr - _discrete_events = $discrete_events_expr - _tstops = $tstops_expr - _combinatoric_ratelaws = $combinatoric_ratelaws - _default_reaction_metadata = $default_reaction_metadata - - remake_ReactionSystem_internal( - make_ReactionSystem_internal( - rx_eq_vec, $tiv, us, ps, $brownsvar; poissonians = $poissvar, - name, spatial_ivs, observed = _observed, continuous_events = _continuous_events, - discrete_events = _discrete_events, tstops = _tstops, - combinatoric_ratelaws = _combinatoric_ratelaws, - unit_checks = _unit_checks - ); - default_reaction_metadata = _default_reaction_metadata - ) - end - ) - ) + MacroTools.flatten(striplines(quote + # Inserts the expressions which generate the `ReactionSystem` input. + $ivsexpr + $psexpr + $vsexpr + $spsexpr + $discsexpr + $obsexpr + $cmpsexpr + $diffsexpr + $brownsexpr + $poissexpr + + # Stores each kwarg in a variable. Not necessary, but useful when debugging generated code. + name = $name + spatial_ivs = $sivs + _unit_checks = $unit_checks + rx_eq_vec = $rxsexprs + us = setdiff(union($spsvar, $vsvar, $cmpsvar), $obs_syms) + ps = union($psvar, $discsvar) + _observed = $obs_eqs + _continuous_events = $continuous_events_expr + _discrete_events = $discrete_events_expr + _tstops = $tstops_expr + _combinatoric_ratelaws = $combinatoric_ratelaws + _default_reaction_metadata = $default_reaction_metadata + + remake_ReactionSystem_internal( + make_ReactionSystem_internal(rx_eq_vec, $tiv, us, ps, $brownsvar; poissonians = $poissvar, + name, spatial_ivs, observed = _observed, continuous_events = _continuous_events, + discrete_events = _discrete_events, tstops = _tstops, + combinatoric_ratelaws = _combinatoric_ratelaws, + unit_checks = _unit_checks); + default_reaction_metadata = _default_reaction_metadata) + end)) end ### DSL Reaction Reading Functions ### @@ -383,7 +360,7 @@ function get_reactions(exprs::Vector{Expr}) # Currently, reaction bundling where rates (but neither substrates nor products) are # bundled, is disabled. See discussion in https://github.com/SciML/Catalyst.jl/issues/1219. if !in(arrow, double_arrows) && Meta.isexpr(rate, :tuple) && - !Meta.isexpr(reaction.args[2], :tuple) && !Meta.isexpr(reaction.args[3], :tuple) + !Meta.isexpr(reaction.args[2], :tuple) && !Meta.isexpr(reaction.args[3], :tuple) error("Bundling of reactions with multiple rates but singular substrates and product sets is disallowed. This error is potentially due to a bidirectional (`<-->`) reaction being incorrectly typed as `-->`.") end @@ -394,24 +371,16 @@ function get_reactions(exprs::Vector{Expr}) (typeof(metadata) != Expr || metadata.head != :tuple) && error("Error: Must provide a tuple of reaction metadata when declaring a bi-directional reaction.") - push_reactions!( - reactions, reaction.args[2], reaction.args[3], - rate.args[1], metadata.args[1], arrow, line - ) - push_reactions!( - reactions, reaction.args[3], reaction.args[2], - rate.args[2], metadata.args[2], arrow, line - ) + push_reactions!(reactions, reaction.args[2], reaction.args[3], + rate.args[1], metadata.args[1], arrow, line) + push_reactions!(reactions, reaction.args[3], reaction.args[2], + rate.args[2], metadata.args[2], arrow, line) elseif in(arrow, fwd_arrows) - push_reactions!( - reactions, reaction.args[2], reaction.args[3], - rate, metadata, arrow, line - ) + push_reactions!(reactions, reaction.args[2], reaction.args[3], + rate, metadata, arrow, line) elseif in(arrow, bwd_arrows) - push_reactions!( - reactions, reaction.args[3], reaction.args[2], - rate, metadata, arrow, line - ) + push_reactions!(reactions, reaction.args[3], reaction.args[2], + rate, metadata, arrow, line) else throw("Malformed reaction, invalid arrow type used in: $(striplines(line))") end @@ -444,10 +413,8 @@ end # Takes a reaction line and creates reaction(s) from it and pushes those to the reaction vector. # Used to create multiple reactions from bundled reactions (like `k, (X,Y) --> 0`). -function push_reactions!( - reactions::Vector{DSLReaction}, subs::ExprValues, - prods::ExprValues, rate::ExprValues, metadata::ExprValues, arrow::Symbol, line::Expr - ) +function push_reactions!(reactions::Vector{DSLReaction}, subs::ExprValues, + prods::ExprValues, rate::ExprValues, metadata::ExprValues, arrow::Symbol, line::Expr) # The rates, substrates, products, and metadata may be in a tuple form (e.g. `k, (X,Y) --> 0`). # This finds these tuples' lengths (or 1 for non-tuple forms). Inconsistent lengths yield error. lengs = (tup_leng(subs), tup_leng(prods), tup_leng(rate), tup_leng(metadata)) @@ -473,7 +440,6 @@ function push_reactions!( subs_i, prods_i, rate_i = get_tup_arg.((subs, prods, rate), i) push!(reactions, DSLReaction(subs_i, prods_i, rate_i, metadata_i, line)) end - return end ### DSL Species and Parameters Extraction ### @@ -495,7 +461,7 @@ end # Function looping through all reactions, to find undeclared symbols (species or # parameters) and assign them to the right category. # `stoich_ps` records parameters used in stoichiometries (if these are not declare separately, -# these are inferred to be integers).. +# these are infered to be integers).. function extract_sps_and_ps(reactions, excluded_syms; requiredec = false) # Loops through all reactants and extract undeclared ones as species. species = OrderedSet{Union{Symbol, Expr}}() @@ -522,7 +488,7 @@ function extract_sps_and_ps(reactions, excluded_syms; requiredec = false) end end - return collect(species), collect(parameters), collect(stoich_ps) + collect(species), collect(parameters), collect(stoich_ps) end # Function called by `extract_sps_and_ps`, recursively loops through an expression and find @@ -540,7 +506,7 @@ function add_syms_from_expr!(push_symbols::AbstractSet, expr::ExprValues, exclud add_syms_from_expr!(push_symbols, expr.args[i], excluded_syms, push_symbols2) end end - return nothing + nothing end ### DSL Output Expression Builders ### @@ -559,7 +525,7 @@ function get_psexpr(parameters_extracted, stoich_ps, options) (pexprs.args[3].args) : (pexprs.args) foreach(p -> push!(arg_vec, p), setdiff(parameters_extracted, stoich_ps)) foreach(p -> push!(arg_vec, :($p::Int64)), stoich_ps) - return pexprs + pexprs end # Given the extracted species (or variables) and the option dictionary, create the @@ -578,7 +544,7 @@ function get_usexpr(us_extracted, options, key = :species; ivs = (DEFAULT_IV_SYM for u in us_extracted u isa Symbol && push!(arg_vec, Expr(:call, u, ivs...)) end - return usexpr + usexpr end # From the system reactions (as `DSLReaction`s) and equations (as expressions), @@ -599,12 +565,8 @@ function get_rxexpr(rx::DSLReaction) subs_stoich_init = deepcopy(subs_init) prod_init = isempty(rx.products) ? nothing : :([]) prod_stoich_init = deepcopy(prod_init) - rx_constructor = :( - Reaction( - $rate, $subs_init, $prod_init, $subs_stoich_init, - $prod_stoich_init; metadata = $(rx.metadata), unit_checks = _unit_checks - ) - ) + rx_constructor = :(Reaction($rate, $subs_init, $prod_init, $subs_stoich_init, + $prod_stoich_init; metadata = $(rx.metadata), unit_checks = _unit_checks)) # Loops through all products and substrates, and adds them (and their stoichiometries) # to the `Reaction` expression. @@ -655,7 +617,7 @@ end function escape_equation!(eqexpr::Expr, all_syms) eqexpr.args[2] = recursive_escape_functions!(eqexpr.args[2], all_syms) eqexpr.args[3] = recursive_escape_functions!(eqexpr.args[3], all_syms) - return eqexpr + eqexpr end ### DSL Option Handling ### @@ -688,10 +650,8 @@ function read_compounds_option(options) if haskey(options, :compounds) cmpexpr_init = options[:compounds] cmpexpr_init.args[3] = option_block_form(get_block_option(cmpexpr_init)) - cmps_declared = [ - find_varinfo_in_declaration(arg.args[2])[1] - for arg in cmpexpr_init.args[3].args - ] + cmps_declared = [find_varinfo_in_declaration(arg.args[2])[1] + for arg in cmpexpr_init.args[3].args] (length(cmps_declared) == 1) && (cmpexpr_init.args[1] = Symbol("@compound")) else # If option is not used, return empty vectors and expressions. cmpexpr_init = :() @@ -706,10 +666,8 @@ function read_differentials_option(options) # Creates the differential expression. # If differentials were provided as options, this is used as the initial expression. # If the default differential (D(...)) was used in equations, this is added to the expression. - diffsexpr = ( - haskey(options, :differentials) ? - get_block_option(options[:differentials]) : striplines(:(begin end)) - ) + diffsexpr = (haskey(options, :differentials) ? + get_block_option(options[:differentials]) : striplines(:(begin end))) diffsexpr = option_block_form(diffsexpr) # Goes through all differentials, checking that they are correctly formatted. Adds their @@ -728,10 +686,10 @@ function read_differentials_option(options) return diffsexpr, diffs_declared end -# Creates the initial expression for declaring brownians. Also extracts any symbols +# Creates the initial expression for declaring brownians. Also extracts any symbols # declared as brownians by the `@brownian` option. function read_brownians_option(options) - browns_declared = extract_syms(options, :brownians) + browns_declared = extract_syms(options, :brownians) brownsexpr_init = haskey(options, :brownians) ? options[:brownians] : :() return brownsexpr_init, browns_declared end @@ -792,7 +750,7 @@ end # Recursively collect bare Symbol names from an expression, skipping escaped nodes. function _collect_symbols!(syms::Vector{Symbol}, ex) - return if ex isa Symbol + if ex isa Symbol push!(syms, ex) elseif ex isa Expr is_escaped_expr(ex) && return @@ -810,14 +768,11 @@ end function validate_poissonian_rate_syms(options, all_syms) poiss_rate_syms = extract_poissonian_rate_syms(options) undeclared = setdiff(poiss_rate_syms, all_syms) - return if !isempty(undeclared) - throw( - UndeclaredSymbolicError( - "Unrecognized symbol(s) $(join(undeclared, ", ")) in a `@poissonians` rate " * - "expression. Symbols in poissonian rates must be pre-declared (e.g. via " * - "`@parameters`, `@species`, or `@variables`) or interpolated." - ) - ) + if !isempty(undeclared) + throw(UndeclaredSymbolicError( + "Unrecognized symbol(s) $(join(undeclared, ", ")) in a `@poissonians` rate " * + "expression. Symbols in poissonian rates must be pre-declared (e.g. via " * + "`@parameters`, `@species`, or `@variables`) or interpolated.")) end end @@ -825,12 +780,11 @@ end # as well as the equation vector. If the default differential was used, update the `diffsexpr` # expression so that this declares this as well. function read_equations_option!( - diffsexpr, options, syms_unavailable, tiv; requiredec = false - ) + diffsexpr, options, syms_unavailable, tiv; requiredec = false) # Prepares the equations. First, extract equations from the provided option (converting to block form if required). # Next, uses `parse_equations!` function to split input into a vector with the equations. eqs_input = haskey(options, :equations) ? get_block_option(options[:equations]) : - MacroTools.striplines(:(begin end)) + MacroTools.striplines(:(begin end)) eqs_input = option_block_form(eqs_input) equations = eqs_input.args @@ -846,22 +800,16 @@ function read_equations_option!( # If the default differential (`D`) is used, record that it should be declared later on. if (:D ∉ syms_unavailable) && find_D_call(eq) - requiredec && throw( - UndeclaredSymbolicError( - "Unrecognized symbol D was used as a differential in an equation: \"$eq\". Since the @require_declaration flag is set, all differentials in equations must be explicitly declared using the @differentials option." - ) - ) + requiredec && throw(UndeclaredSymbolicError( + "Unrecognized symbol D was used as a differential in an equation: \"$eq\". Since the @require_declaration flag is set, all differentials in equations must be explicitly declared using the @differentials option.")) add_default_diff = true push!(syms_unavailable, :D) end # Any undeclared symbolic variables encountered should be extracted as variables. add_syms_from_expr!(vs_inferred, eq, syms_unavailable) - (!isempty(vs_inferred) && requiredec) && throw( - UndeclaredSymbolicError( - "Unrecognized symbol $(join(vs_inferred, ", ")) detected in equation expression: \"$(string(eq))\". Since the flag @require_declaration is declared, all symbolic variables must be explicitly declared with the @species, @variables, and @parameters options." - ) - ) + (!isempty(vs_inferred) && requiredec) && throw(UndeclaredSymbolicError( + "Unrecognized symbol $(join(vs_inferred, ", ")) detected in equation expression: \"$(string(eq))\". Since the flag @require_declaration is declared, all symbolic variables must be explicitly declared with the @species, @variables, and @parameters options.")) end # If `D` differential is used, add it to the differential expression and inferred differentials list. @@ -890,8 +838,7 @@ end # a vector containing the observable equations, and a list of all observable symbols (this # list contains both those declared separately or inferred from the `@observables` option` input`). function read_observables_option( - options, all_ivs, us_declared, all_syms; requiredec = false - ) + options, all_ivs, us_declared, all_syms; requiredec = false) syms_unavailable = setdiff(all_syms, us_declared) if haskey(options, :observables) # Gets list of observable equations and prepares variable declaration expression. @@ -903,7 +850,7 @@ function read_observables_option( for (idx, obs_eq) in enumerate(obs_eqs.args) # Extract the observable, checks for errors. obs_name, ivs, _, defaults, - metadata = find_varinfo_in_declaration(obs_eq.args[2]) + metadata = find_varinfo_in_declaration(obs_eq.args[2]) # Error checks. (requiredec && !in(obs_name, us_declared)) && @@ -928,28 +875,13 @@ function read_observables_option( if !((obs_name in us_declared) || is_escaped_expr(obs_eq.args[2])) # Creates an expression which extracts the ivs of the species & variables the # observable depends on, and splats them out in the correct order. - dep_var_expr = :( - filter( - !MT.isparameter, - Symbolics.get_variables($(obs_eq.args[3])) - ) - ) - ivs_get_expr = :( - unique( - reduce( - vcat, [ - sorted_arguments(unwrap(dep)) - for dep in $dep_var_expr - ] - ) - ) - ) - ivs_get_expr_sorted = :( - sort( - $(ivs_get_expr); - by = iv -> findfirst(MT.getname(iv) == ivs for ivs in $all_ivs) - ) - ) + dep_var_expr = :(filter(!MT.isparameter, + Symbolics.get_variables($(obs_eq.args[3])))) + ivs_get_expr = :(unique(reduce( + vcat, [sorted_arguments(unwrap(dep)) + for dep in $dep_var_expr]))) + ivs_get_expr_sorted = :(sort($(ivs_get_expr); + by = iv -> findfirst(MT.getname(iv) == ivs for ivs in $all_ivs))) obs_expr = insert_independent_variable(obs_eq.args[2], :($ivs_get_expr_sorted...)) push!(obsexpr.args[1].args, obs_expr) @@ -1001,7 +933,7 @@ function expr_contains_pre(expr) end # Read the events (continuous or discrete) provided as options to the DSL. Returns an expression which evaluates to these. -# Inferred parameters that are updated byu the event should be declared using e.g. `@discretes p(t)`. +# Infered parameters that are updated byu the event should be declared using e.g. `@discretes p(t)`. # `read_events_option!` moves these from `ps_inferred` to `discs_inferred` function read_events_option!(options, discs_inferred::Vector, ps_inferred::Vector, discs_declared::Vector, event_type::Symbol) # Prepares the events, if required to, converts them to block form. @@ -1009,7 +941,7 @@ function read_events_option!(options, discs_inferred::Vector, ps_inferred::Vecto error("Trying to read an unsupported event type.") end events_input = haskey(options, event_type) ? get_block_option(options[event_type]) : - striplines(:(begin end)) + striplines(:(begin end)) events_input = option_block_form(events_input) # Goes through the events, checks for errors, and adds them to the output vector. @@ -1018,11 +950,11 @@ function read_events_option!(options, discs_inferred::Vector, ps_inferred::Vecto # Formatting error checks. # NOTE: Maybe we should move these deeper into the system (rather than the DSL), throwing errors more generally? if (arg isa Expr) && (arg.head != :call) || (arg.args[1] != :(=>)) || - (length(arg.args) != 3) + (length(arg.args) != 3) error("Events should be on form `condition => affect`, separated by a `=>`. This appears not to be the case for: $(arg).") end if (arg isa Expr) && (arg.args[2] isa Expr) && (arg.args[2].head != :vect) && - (event_type == :continuous_events) + (event_type == :continuous_events) error("The condition part of continuous events (the left-hand side) must be a vector. This is not the case for: $(arg).") end if (arg isa Expr) && (arg.args[3] isa Expr) && (arg.args[3].head != :vect) @@ -1059,10 +991,8 @@ function read_events_option!(options, discs_inferred::Vector, ps_inferred::Vecto end # Adds the correctly formatted event to the event creation expression. - event_func = ( - event_type == :continuous_events ? :(MT.SymbolicContinuousCallback) : - :(MT.SymbolicDiscreteCallback) - ) + event_func = (event_type == :continuous_events ? :(MT.SymbolicContinuousCallback) : + :(MT.SymbolicDiscreteCallback)) event = :($event_func($(arg.args[2]) => $affects; discrete_parameters = $disc_ps)) push!(events_expr.args, event) end @@ -1121,7 +1051,7 @@ end # be used or not. If not provided, use the default (true). function read_combinatoric_ratelaws_option(options) return haskey(options, :combinatoric_ratelaws) ? - get_block_option(options[:combinatoric_ratelaws]) : true + get_block_option(options[:combinatoric_ratelaws]) : true end # Reads unit_checks option, which determines if unit validation should be run or not. @@ -1191,7 +1121,7 @@ Notes: macro. See [The Reaction DSL](@ref dsl_description) tutorial for more details. """ macro reaction(ex) - return make_reaction(ex) + make_reaction(ex) end # Function for creating a Reaction structure (used by the @reaction macro). @@ -1215,7 +1145,7 @@ function make_reaction(ex::Expr) # Returns a rephrased expression which generates the `Reaction`. # _unit_checks is defined here so that get_rxexpr's generated code can reference it. - return quote + quote $pexprs $iv $spexprs @@ -1238,15 +1168,13 @@ end # Special function calls like "hill(...)" are not expanded. function recursive_escape_functions!(expr::ExprValues, syms_skip = []) (typeof(expr) != Expr) && (return expr) - foreach( - i -> expr.args[i] = recursive_escape_functions!(expr.args[i], syms_skip), - 1:length(expr.args) - ) + foreach(i -> expr.args[i] = recursive_escape_functions!(expr.args[i], syms_skip), + 1:length(expr.args)) if (expr.head == :call) && (expr.args[1] isa Symbol) &&!isdefined(Catalyst, expr.args[1]) && expr.args[1] ∉ syms_skip expr.args[1] = esc(expr.args[1]) end - return expr + expr end # Returns the length of an expression tuple, or 1 if it is not an expression tuple (probably a Symbol/Numerical). diff --git a/src/expression_utils.jl b/src/expression_utils.jl index 809684ca87..fd997b18f3 100644 --- a/src/expression_utils.jl +++ b/src/expression_utils.jl @@ -13,7 +13,7 @@ function esc_dollars!(ex) ex.args[i] = esc_dollars!(ex.args[i]) end end - return ex + ex end # Checks if an expression is an escaped expression (e.g. on the form `$(Expr(:escape, :Y))`) @@ -26,7 +26,7 @@ end # Throws an error when a forbidden symbol is used. function forbidden_symbol_check(syms) used_forbidden_syms = intersect(forbidden_symbols_error, syms) - return isempty(used_forbidden_syms) || + isempty(used_forbidden_syms) || error("The following symbol(s) are used as species or parameters: $used_forbidden_syms, this is not permitted.") end diff --git a/src/latex_utils.jl b/src/latex_utils.jl index bfb98d5b25..7f8dfc6728 100644 --- a/src/latex_utils.jl +++ b/src/latex_utils.jl @@ -32,5 +32,5 @@ as plain math italic instead of `\\mathtt{}` in LaTeX. No-op if already set. """ function _add_latex_wrapper(s) _has_latex_wrapper(s) && return s - return _set_latex_wrapper(s, string) + _set_latex_wrapper(s, string) end diff --git a/src/latexify_recipes.jl b/src/latexify_recipes.jl index 9b64215c12..5a16819598 100644 --- a/src/latexify_recipes.jl +++ b/src/latexify_recipes.jl @@ -47,16 +47,14 @@ function processsym(s) var = value(Symbolics.variable(MT.getname(s), idxs...)) end _has_latex_wrapper(var) || (var = _set_latex_wrapper(var, string)) - return var + var end -function chemical_arrows( - rn::ReactionSystem; +function chemical_arrows(rn::ReactionSystem; double_linebreak = LATEX_DEFS.double_linebreak, starred = LATEX_DEFS.starred, mathrm = true, mathjax = LATEX_DEFS.mathjax, math_delimiters = false, - show_time_arg = false, kwargs... - ) + show_time_arg = false, kwargs...) any_nonrx_subsys(rn) && (@warn "Latexify currently ignores non-ReactionSystem subsystems. Please call `flatsys = flatten(sys)` to obtain a flattened version of your system before trying to Latexify it.") @@ -76,10 +74,8 @@ function chemical_arrows( # test if in IJulia since their mathjax is outdated... # VSCODE uses Katex and doesn't have this issue. - if mathjax || ( - isdefined(Main, :IJulia) && Main.IJulia.inited && - !any(s -> occursin("VSCODE", s), collect(keys(ENV))) - ) + if mathjax || (isdefined(Main, :IJulia) && Main.IJulia.inited && + !any(s -> occursin("VSCODE", s), collect(keys(ENV)))) str *= "\\require{mhchem} \n" end @@ -101,23 +97,19 @@ function chemical_arrows( rate = (r.rate isa SymbolicT && !show_time_arg) ? subber(r.rate) : r.rate ### Generate formatted string of substrates - substrates = [ - make_stoich_str( - substrate[1], substrate[2], subber; mathrm, - kwargs... - ) - for substrate in zip(r.substrates, r.substoich) - ] + substrates = [make_stoich_str(substrate[1], substrate[2], subber; mathrm, + kwargs...) + for substrate in zip(r.substrates, r.substoich)] isempty(substrates) && (substrates = ["\\varnothing"]) str *= join(substrates, " + ") ### Generate reaction arrows if i + 1 <= length(rxs) && issetequal(r.products, rxs[i + 1].substrates) && - issetequal(r.substrates, rxs[i + 1].products) + issetequal(r.substrates, rxs[i + 1].products) ### Bi-directional arrows rate_backwards = (rxs[i + 1].rate isa SymbolicT && !show_time_arg) ? subber(rxs[i + 1].rate) : - rxs[i + 1].rate + rxs[i + 1].rate str *= " &" * rev_arrow str *= "[" * latexraw(rate_backwards; kwargs...) * "]" str *= "{" * latexraw(rate; kwargs...) * "} " @@ -128,20 +120,14 @@ function chemical_arrows( end ### Generate formatted string of products - products = [ - make_stoich_str( - product[1], product[2], subber; mathrm = true, - kwargs... - ) - for product in zip(r.products, r.prodstoich) - ] + products = [make_stoich_str(product[1], product[2], subber; mathrm = true, + kwargs...) + for product in zip(r.products, r.prodstoich)] isempty(products) && (products = ["\\varnothing"]) str *= join(products, " + ") - if ( - (i == lastidx) || - (((i + 1) == lastidx) && (backwards_reaction == true)) - ) && - isempty(nonrxs) + if ((i == lastidx) || + (((i + 1) == lastidx) && (backwards_reaction == true))) && + isempty(nonrxs) str *= " \n " else str *= " $eol" @@ -183,7 +169,7 @@ function any_nonrx_subsys(rn::MT.AbstractSystem) for subsys in get_systems(rn) any_nonrx_subsys(subsys) && (return true) end - return false + false end function make_stoich_str(spec, stoich, subber; mathrm = true, kwargs...) @@ -195,17 +181,17 @@ function make_stoich_str(spec, stoich, subber; mathrm = true, kwargs...) poststr = "" end - return if isequal(stoich, one(stoich)) + if isequal(stoich, one(stoich)) prestr * latexraw(subber(spec); kwargs...) * poststr else if (stoich isa SymbolicT) && iscall(stoich) LaTeXString("(") * - latexraw(subber(stoich); kwargs...) * - LaTeXString(")") * - prestr * latexraw(subber(spec); kwargs...) * poststr + latexraw(subber(stoich); kwargs...) * + LaTeXString(")") * + prestr * latexraw(subber(spec); kwargs...) * poststr else latexraw(subber(stoich); kwargs...) * LaTeXString(" ") * - prestr * latexraw(subber(spec); kwargs...) * poststr + prestr * latexraw(subber(spec); kwargs...) * poststr end end end diff --git a/src/mtk_nullspace_function.jl b/src/mtk_nullspace_function.jl index f89a31b772..6a5cb7dfc8 100644 --- a/src/mtk_nullspace_function.jl +++ b/src/mtk_nullspace_function.jl @@ -12,15 +12,13 @@ using SparseArrays using SparseArrays: AbstractSparseMatrixCSC, getcolptr macro swap(a, b) - return esc(:(($a, $b) = ($b, $a))) + esc(:(($a, $b) = ($b, $a))) end import Base: swaprows! -function bareiss_update!( - zero!, M::StridedMatrix, k, swapto, pivot, - prev_pivot::Base.BitInteger - ) +function bareiss_update!(zero!, M::StridedMatrix, k, swapto, pivot, + prev_pivot::Base.BitInteger) flag = zero(prev_pivot) prev_pivot = Base.MultiplicativeInverses.SignedMultiplicativeInverse(prev_pivot) @inbounds for i in (k + 1):size(M, 2) @@ -31,14 +29,14 @@ function bareiss_update!( end end iszero(flag) || error("Overflow occurred") - return zero!(M, (k + 1):size(M, 1), k) + zero!(M, (k + 1):size(M, 1), k) end function bareiss_update!(zero!, M::StridedMatrix, k, swapto, pivot, prev_pivot) @inbounds for i in (k + 1):size(M, 2), j in (k + 1):size(M, 1) M[j, i] = exactdiv(M[j, i] * pivot - M[j, k] * M[k, i], prev_pivot) end - return zero!(M, (k + 1):size(M, 1), k) + zero!(M, (k + 1):size(M, 1), k) end @views function bareiss_update!(zero!, M::AbstractMatrix, k, swapto, pivot, prev_pivot) @@ -53,16 +51,14 @@ end end end -function bareiss_update_virtual_colswap!( - zero!, M::AbstractMatrix, k, swapto, pivot, - prev_pivot - ) +function bareiss_update_virtual_colswap!(zero!, M::AbstractMatrix, k, swapto, pivot, + prev_pivot) if prev_pivot isa Base.BitInteger prev_pivot = Base.MultiplicativeInverses.SignedMultiplicativeInverse(prev_pivot) end V = @view M[(k + 1):end, :] V .= @views exactdiv.(V .* pivot .- M[(k + 1):end, swapto[2]] * M[k, :]', prev_pivot) - return zero!(M, (k + 1):size(M, 1), swapto[2]) + zero!(M, (k + 1):size(M, 1), swapto[2]) end bareiss_zero!(M, i, j) = M[i, j] .= zero(eltype(M)) @@ -71,21 +67,19 @@ function find_pivot_col(M, i) p = findfirst(!iszero, @view M[i, i:end]) p === nothing && return nothing idx = CartesianIndex(i, p + i - 1) - return (idx, M[idx]) + (idx, M[idx]) end function find_pivot_any(M, i) p = findfirst(!iszero, @view M[i:end, i:end]) p === nothing && return nothing idx = p + CartesianIndex(i - 1, i - 1) - return (idx, M[idx]) + (idx, M[idx]) end const bareiss_colswap = (Base.swapcols!, swaprows!, bareiss_update!, bareiss_zero!) -const bareiss_virtcolswap = ( - (M, i, j) -> nothing, swaprows!, - bareiss_update_virtual_colswap!, bareiss_zero!, -) +const bareiss_virtcolswap = ((M, i, j) -> nothing, swaprows!, + bareiss_update_virtual_colswap!, bareiss_zero!) """ bareiss!(M, [swap_strategy]) @@ -97,10 +91,8 @@ swap_strategy is an optional argument that determines how the swapping of rows a bareiss_colswap (the default) swaps the columns and rows normally. bareiss_virtcolswap pretends to swap the columns which can be faster for sparse matrices. """ -function bareiss!( - M::AbstractMatrix{T}, swap_strategy = bareiss_colswap; - find_pivot = find_pivot_any, column_pivots = nothing - ) where {T} +function bareiss!(M::AbstractMatrix{T}, swap_strategy = bareiss_colswap; + find_pivot = find_pivot_any, column_pivots = nothing) where {T} swapcols!, swaprows!, update!, zero! = swap_strategy prev = one(eltype(M)) n = size(M, 1) @@ -147,27 +139,25 @@ function nullspace(A; col_order = nothing) end fill!(pivots_cache, 0) - + # Modified: Was `N = ModelingToolkit.reduced_echelon_nullspace(rank, B, pivots_cache)`. - N = reduced_echelon_nullspace(rank, B, pivots_cache) - return apply_inv_pivot_rows!(N, column_pivots) + N = reduced_echelon_nullspace(rank, B, pivots_cache) + apply_inv_pivot_rows!(N, column_pivots) end function apply_inv_pivot_rows!(M, ipiv) for i in size(M, 1):-1:1 swaprows!(M, i, ipiv[i]) end - return M + M end ### ### Modified from AbstractAlgebra.jl ### ### https://github.com/Nemocas/AbstractAlgebra.jl/blob/4803548c7a945f3f7bd8c63f8bb7c79fac92b11a/LICENSE.md -function reduce_echelon!( - A::AbstractMatrix{T}, rank, d, - pivots_cache = zeros(Int, size(A, 2)) - ) where {T} +function reduce_echelon!(A::AbstractMatrix{T}, rank, d, + pivots_cache = zeros(Int, size(A, 2))) where {T} m, n = size(A) isreduced = true @inbounds for i in 1:rank @@ -232,10 +222,8 @@ function reduce_echelon!( return A end -function reduced_echelon_nullspace( - rank, A::AbstractMatrix{T}, - pivots_cache = zeros(Int, size(A, 2)) - ) where {T} +function reduced_echelon_nullspace(rank, A::AbstractMatrix{T}, + pivots_cache = zeros(Int, size(A, 2))) where {T} n = size(A, 2) nullity = n - rank U = zeros(T, n, nullity) diff --git a/src/network_analysis.jl b/src/network_analysis.jl index 6a16cd38c2..277319fe9d 100644 --- a/src/network_analysis.jl +++ b/src/network_analysis.jl @@ -1,9 +1,9 @@ ### Reaction Complex Handling ### -# Checks if a reaction has integer stoichiometry. Used to detect and error on +# Checks if a reaction has integer stoichiometry. Used to detect and error on # non-integer stoichiometry in network analysis functions. function has_integer_stoichiometry(rx::Reaction) - return (eltype(rx.substoich) <: Integer) && (eltype(rx.prodstoich) <: Integer) + (eltype(rx.substoich) <: Integer) && (eltype(rx.prodstoich) <: Integer) end # Helper function to check all reactions for integer stoichiometry and throw an informative error. @@ -12,15 +12,13 @@ function check_integer_stoichiometry(rn::ReactionSystem) nonint_rxs = filter(!has_integer_stoichiometry, rxs) if !isempty(nonint_rxs) rx_strs = [" $rx" for rx in nonint_rxs] - error( - "Network analysis functions require integer stoichiometric coefficients, " * - "but the following reaction(s) have non-integer stoichiometry:\n" * - join(rx_strs, "\n") * - "\n\nNon-integer stoichiometry is not currently supported for " * - "network analysis functions." - ) + error("Network analysis functions require integer stoichiometric coefficients, " * + "but the following reaction(s) have non-integer stoichiometry:\n" * + join(rx_strs, "\n") * + "\n\nNon-integer stoichiometry is not currently supported for " * + "network analysis functions.") end - return nothing + nothing end # get the species indices and stoichiometry while filtering out constant species. @@ -40,7 +38,7 @@ function filter_constspecs(specs, stoich::AbstractVector{V}, smap) where {V <: I ids = map(Base.Fix1(getindex, smap), specs) filtered_stoich = copy(stoich) end - return ids, filtered_stoich + ids, filtered_stoich end """ @@ -91,7 +89,7 @@ function reactioncomplexmap(rn::ReactionSystem) complextorxsmap[prodrc] = [i => 1] end end - return complextorxsmap + complextorxsmap end @doc raw""" @@ -123,19 +121,17 @@ function reactioncomplexes(rn::ReactionSystem; sparse = false) if isempty(nps.complexes) || (sparse != issparse(nps.complexes)) complextorxsmap = reactioncomplexmap(rn) nps.complexes, - nps.incidencemat = if sparse + nps.incidencemat = if sparse reactioncomplexes(SparseMatrixCSC{Int, Int}, rn, complextorxsmap) else reactioncomplexes(Matrix{Int}, rn, complextorxsmap) end end - return nps.complexes, nps.incidencemat + nps.complexes, nps.incidencemat end -function reactioncomplexes( - ::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSystem, - complextorxsmap - ) +function reactioncomplexes(::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSystem, + complextorxsmap) complexes = collect(keys(complextorxsmap)) Is = Int[] Js = Int[] @@ -148,7 +144,7 @@ function reactioncomplexes( end end B = sparse(Is, Js, Vs, length(complexes), numreactions(rn)) - return complexes, B + complexes, B end function reactioncomplexes(::Type{Matrix{Int}}, rn::ReactionSystem, complextorxsmap) @@ -159,7 +155,7 @@ function reactioncomplexes(::Type{Matrix{Int}}, rn::ReactionSystem, complextorxs B[i, j] = σ end end - return complexes, B + complexes, B end """ @@ -194,7 +190,7 @@ function complexstoichmat(rn::ReactionSystem; sparse = false) complexstoichmat(Matrix{Int}, rn, keys(reactioncomplexmap(rn))) end end - return nps.complexstoichmat + nps.complexstoichmat end function complexstoichmat(::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSystem, rcs) @@ -208,7 +204,7 @@ function complexstoichmat(::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSystem, push!(Vs, rcel.speciesstoich) end end - return Z = sparse(Is, Js, Vs, numspecies(rn), length(rcs)) + Z = sparse(Is, Js, Vs, numspecies(rn), length(rcs)) end function complexstoichmat(::Type{Matrix{Int}}, rn::ReactionSystem, rcs) @@ -218,7 +214,7 @@ function complexstoichmat(::Type{Matrix{Int}}, rn::ReactionSystem, rcs) Z[rcel.speciesid, i] = rcel.speciesstoich end end - return Z + Z end @doc raw""" @@ -233,7 +229,7 @@ Returns a symbolic matrix by default, but will return a numerical matrix if para function laplacianmat(rn::ReactionSystem, pmap::Dict = Dict(); sparse = false) D = incidencemat(rn; sparse) K = fluxmat(rn, pmap; sparse) - return D * K + D * K end @doc raw""" @@ -281,7 +277,7 @@ function fluxmat(::Type{SparseMatrixCSC{T, Int}}, rcmap, rates) where {T} end end end - return Z = sparse(Is, Js, Vs, length(rates), length(rcmap)) + Z = sparse(Is, Js, Vs, length(rates), length(rcmap)) end function fluxmat(::Type{Matrix{T}}, rcmap, rates) where {T} @@ -293,17 +289,17 @@ function fluxmat(::Type{Matrix{T}}, rcmap, rates) where {T} dir == -1 && (K[rx, i] = rates[rx]) end end - return K + K end function fluxmat(rn::ReactionSystem, pmap::Vector) pdict = Dict(pmap) - return fluxmat(rn, pdict) + fluxmat(rn, pdict) end function fluxmat(rn::ReactionSystem, pmap::Tuple) pdict = Dict(pmap) - return fluxmat(rn, pdict) + fluxmat(rn, pdict) end # Helper to substitute values into a (vector of) symbolic expressions. The syms are the symbols to substitute and the symexprs are the expressions to substitute into. @@ -312,7 +308,7 @@ function substitutevals(rn::ReactionSystem, map::Dict, syms, symexprs) error("Incorrect number of parameter-value pairs were specified.") map = symmap_to_varmap(rn, map) map = Dict(value(k) => v for (k, v) in map) - return vals = [value(substitute(expr, map)) for expr in symexprs] + vals = [value(substitute(expr, map)) for expr in symexprs] end """ @@ -326,10 +322,8 @@ If the `combinatoric_ratelaws` option is set, will include prefactors for that ( **Warning**: Unlike other Catalyst functions, the `massactionvector` function will return a `Vector{Num}` in the symbolic case. This is to allow easier computation of the matrix decomposition of the ODEs. """ -function massactionvector( - rn::ReactionSystem, scmap::Dict = Dict(); - combinatoric_ratelaws = Catalyst.get_combinatoric_ratelaws(rn) - ) +function massactionvector(rn::ReactionSystem, scmap::Dict = Dict(); + combinatoric_ratelaws = Catalyst.get_combinatoric_ratelaws(rn)) r = numreactions(rn) rxs = reactions(rn) sm = speciesmap(rn) @@ -360,23 +354,19 @@ function massactionvector( push!(Φ, maprod) end - return Φ + Φ end -function massactionvector( - rn::ReactionSystem, scmap::Tuple; - combinatoric_ratelaws = Catalyst.get_combinatoric_ratelaws(rn) - ) +function massactionvector(rn::ReactionSystem, scmap::Tuple; + combinatoric_ratelaws = Catalyst.get_combinatoric_ratelaws(rn)) sdict = Dict(scmap) - return massactionvector(rn, sdict; combinatoric_ratelaws) + massactionvector(rn, sdict; combinatoric_ratelaws) end -function massactionvector( - rn::ReactionSystem, scmap::Vector; - combinatoric_ratelaws = Catalyst.get_combinatoric_ratelaws(rn) - ) +function massactionvector(rn::ReactionSystem, scmap::Vector; + combinatoric_ratelaws = Catalyst.get_combinatoric_ratelaws(rn)) sdict = Dict(scmap) - return massactionvector(rn, sdict; combinatoric_ratelaws) + massactionvector(rn, sdict; combinatoric_ratelaws) end @doc raw""" @@ -408,7 +398,7 @@ function complexoutgoingmat(rn::ReactionSystem; sparse = false) complexoutgoingmat(Matrix{Int}, rn, B) end end - return nps.complexoutgoingmat + nps.complexoutgoingmat end function complexoutgoingmat(::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSystem, B) @@ -430,7 +420,7 @@ function complexoutgoingmat(::Type{SparseMatrixCSC{Int, Int}}, rn::ReactionSyste end end end - return sparse(Is, Js, Vs, size(B, 1), size(B, 2)) + sparse(Is, Js, Vs, size(B, 1), size(B, 2)) end function complexoutgoingmat(::Type{Matrix{Int}}, rn::ReactionSystem, B) @@ -438,7 +428,7 @@ function complexoutgoingmat(::Type{Matrix{Int}}, rn::ReactionSystem, B) for (I, b) in pairs(Δ) (b == 1) && (Δ[I] = 0) end - return Δ + Δ end """ @@ -462,7 +452,7 @@ function incidencematgraph(rn::ReactionSystem) isempty(nps.incidencemat) && reactioncomplexes(rn) nps.incidencegraph = incidencematgraph(nps.incidencemat) end - return nps.incidencegraph + nps.incidencegraph end function incidencematgraph(incidencemat::Matrix{Int}) @@ -529,13 +519,13 @@ function species_reaction_graph(rn::ReactionSystem) edgelist = Graphs.Edge[] for (i, rx) in enumerate(rxs) for spec in rx.substrates - push!(edgelist, Graphs.Edge(sm[spec], s + i)) + push!(edgelist, Graphs.Edge(sm[spec], s+i)) end for spec in rx.products - push!(edgelist, Graphs.Edge(s + i, sm[spec])) + push!(edgelist, Graphs.Edge(s+i, sm[spec])) end end - return srg = Graphs.SimpleDiGraphFromIterator(edgelist) + srg = Graphs.SimpleDiGraphFromIterator(edgelist) end ### Linkage, Deficiency, Reversibility ### @@ -567,7 +557,7 @@ function linkageclasses(rn::ReactionSystem) if isempty(nps.linkageclasses) nps.linkageclasses = linkageclasses(incidencematgraph(rn)) end - return nps.linkageclasses + nps.linkageclasses end linkageclasses(incidencegraph) = Graphs.connected_components(incidencegraph) @@ -583,7 +573,7 @@ function stronglinkageclasses(rn::ReactionSystem) if isempty(nps.stronglinkageclasses) nps.stronglinkageclasses = stronglinkageclasses(incidencematgraph(rn)) end - return nps.stronglinkageclasses + nps.stronglinkageclasses end stronglinkageclasses(incidencegraph) = Graphs.strongly_connected_components(incidencegraph) @@ -601,7 +591,7 @@ function terminallinkageclasses(rn::ReactionSystem) tslcs = filter(lc -> isterminal(lc, rn), slcs) nps.terminallinkageclasses = tslcs end - return nps.terminallinkageclasses + nps.terminallinkageclasses end # Helper function for terminallinkageclasses. Given a linkage class and a reaction network, say whether the linkage class is terminal, @@ -619,7 +609,7 @@ function isterminal(lc::Vector, rn::ReactionSystem) p in Set(lc) ? continue : return false end end - return true + true end function isforestlike(rn::ReactionSystem) @@ -632,7 +622,7 @@ function isforestlike(rn::ReactionSystem) nps = get_networkproperties(subnet) isempty(nps.incidencemat) && reactioncomplexes(subnet; sparse = sparseig) end - return all(Graphs.is_tree ∘ SimpleGraph ∘ incidencematgraph, subnets) + all(Graphs.is_tree ∘ SimpleGraph ∘ incidencematgraph, subnets) end @doc raw""" @@ -667,19 +657,13 @@ function deficiency(rn::ReactionSystem) r = nps.rank nps.deficiency = Graphs.nv(ig) - length(lc) - r end - return nps.deficiency + nps.deficiency end # Used in the subsequent function. function subnetworkmapping(linkageclass, allrxs, complextorxsmap, p) - rxinds = sort!( - collect( - Set( - rxidx for rcidx in linkageclass - for rxidx in complextorxsmap[rcidx] - ) - ) - ) + rxinds = sort!(collect(Set(rxidx for rcidx in linkageclass + for rxidx in complextorxsmap[rcidx]))) rxs = allrxs[rxinds] specset = Set(s for rx in rxs for s in rx.substrates if !isconstant(s)) for rx in rxs @@ -693,7 +677,7 @@ function subnetworkmapping(linkageclass, allrxs, complextorxsmap, p) Symbolics.get_variables!(newps, rx.rate, p) end newps_vec = collect(newps) - return rxs, specs, newps_vec # reactions and species involved in reactions of subnetwork + rxs, specs, newps_vec # reactions and species involved in reactions of subnetwork end """ @@ -722,12 +706,10 @@ function subnetworks(rs::ReactionSystem) for i in 1:length(lcs) reacs, specs, newps = subnetworkmapping(lcs[i], rxs, complextorxsmap, p) newname = Symbol(nameof(rs), "_", i) - push!( - subnetworks, - ReactionSystem(reacs, t, specs, newps; name = newname, spatial_ivs) - ) + push!(subnetworks, + ReactionSystem(reacs, t, specs, newps; name = newname, spatial_ivs)) end - return subnetworks + subnetworks end """ @@ -753,7 +735,7 @@ function linkagedeficiencies(rs::ReactionSystem) nps = get_networkproperties(subnet) δ[i] = length(lcs[i]) - 1 - nps.rank end - return δ + δ end """ @@ -772,7 +754,7 @@ isreversible(sir) """ function isreversible(rn::ReactionSystem) ig = incidencematgraph(rn) - return Graphs.reverse(ig) == ig + Graphs.reverse(ig) == ig end """ @@ -801,7 +783,7 @@ function isweaklyreversible(rn::ReactionSystem, subnets) end # A network is weakly reversible if all of its subnetworks are strongly connected - return all(Graphs.is_strongly_connected ∘ incidencematgraph, subnets) + all(Graphs.is_strongly_connected ∘ incidencematgraph, subnets) end ### Conservation Laws ### @@ -821,7 +803,7 @@ function isconserved(x, default = false) if iscall(x) && operation(x) === getindex x = first(arguments(x)) end - return Symbolics.getmetadata(x, ConservedParameter, default) + Symbolics.getmetadata(x, ConservedParameter, default) end """ @@ -851,7 +833,7 @@ gives function conservedequations(rn::ReactionSystem) conservationlaws(rn) nps = get_networkproperties(rn) - return nps.conservedeqs + nps.conservedeqs end """ @@ -881,7 +863,7 @@ gives function conservationlaw_constants(rn::ReactionSystem) conservationlaws(rn) nps = get_networkproperties(rn) - return nps.constantdefs + nps.constantdefs end """ @@ -892,7 +874,7 @@ conservation laws, each represented as a row in the output. """ function conservationlaws(nsm::Matrix; col_order = nothing) conslaws = positive_nullspace(nsm'; col_order = col_order) - return Matrix(conslaws) + Matrix(conslaws) end # Used in the subsequent function. @@ -909,13 +891,13 @@ function cache_conservationlaw_eqs!(rn::ReactionSystem, N::AbstractMatrix, col_o rhs_terms = Vector{SymbolicT}(undef, nullity) for (i, depidx) in enumerate(depidxs) scaleby = (N[i, depidx] != 1) ? N[i, depidx] : one(eltype(N)) - (scaleby != 0) || + (scaleby != 0) || error("Error, found a zero in the conservation law matrix where one was not expected.") coefs = @view N[i, indepidxs] rhs_terms[i] = sum(p -> p[1] / scaleby * p[2], zip(coefs, indepspecs)) end - # Declare the conservation constant parameters + # Declare the conservation constant parameters #`using guesses is for consistency and possibly faster initialisation guesses = [Initial(depspecs[i] + rhs_terms[i]) for i in 1:nullity] Γs = @parameters $(CONSERVED_CONSTANT_SYMBOL)[1:nullity] = missing [conserved = true, guess = guesses] @@ -935,7 +917,7 @@ function cache_conservationlaw_eqs!(rn::ReactionSystem, N::AbstractMatrix, col_o nps.constantdefs = constantdefs nps.conservedconst = constants - return nothing + nothing end """ @@ -954,7 +936,7 @@ function conservationlaws(rs::ReactionSystem) nsm = netstoichmat(rs) nps.conservationmat = conservationlaws(nsm; col_order = nps.col_order) cache_conservationlaw_eqs!(rs, nps.conservationmat, nps.col_order) - return nps.conservationmat + nps.conservationmat end """ @@ -983,10 +965,10 @@ function conservationlaw_errorcheck(rs, pre_varmap) vars_with_vals = Set(p[1] for p in pre_varmap) missing_cl_vals = filter(sp -> (sp ∉ vars_with_vals) && !MT.hasdefault(sp), conslaw_species(rs)) isempty(missing_cl_vals) && return - error("The system has conservation laws but initial conditions were not provided for these conservation law-involved species: $(missing_cl_vals).") + error("The system has conservation laws but initial conditions were not provided for these conservation law-invovled species: $(missing_cl_vals).") end -# Returns a vector with all species that are involved in conservation laws. +# Returns a vector with all species that are invovled in conservation laws. # Note: uses Symbolics.get_variables! instead of MT.collect_vars! because # the latter fails to extract variables on Julia 1.10 LTS (empty result # despite valid input expressions). @@ -1006,7 +988,7 @@ end Constructively compute whether a kinetic system (a reaction network with a set of rate constants) will admit detailed-balanced equilibrium solutions, using the Wegscheider conditions, [Feinberg, 1989](https://www.sciencedirect.com/science/article/pii/0009250989851243). A detailed-balanced solution is one for which the rate of every forward reaction exactly equals its reverse reaction. Accepts a dictionary, vector, or tuple of variable-to-value mappings, e.g. [k1 => 1.0, k2 => 2.0,...]. """ -function isdetailedbalanced(rs::ReactionSystem, parametermap::Dict; abstol = 0, reltol = 1.0e-9) +function isdetailedbalanced(rs::ReactionSystem, parametermap::Dict; abstol = 0, reltol = 1e-9) if length(parametermap) != numparams(rs) error("Incorrect number of parameters specified.") elseif !isreversible(rs) @@ -1050,18 +1032,10 @@ function isdetailedbalanced(rs::ReactionSystem, parametermap::Dict; abstol = 0, for i in 1:size(sols, 2) α = sols[:, i] - fwd = prod( - [ - K[Graphs.src(e), Graphs.dst(e)]^α[i] - for (e, i) in zip(spanning_forest, 1:length(α)) - ] - ) - rev = prod( - [ - K[Graphs.dst(e), Graphs.src(e)]^α[i] - for (e, i) in zip(spanning_forest, 1:length(α)) - ] - ) + fwd = prod([K[Graphs.src(e), Graphs.dst(e)]^α[i] + for (e, i) in zip(spanning_forest, 1:length(α))]) + rev = prod([K[Graphs.dst(e), Graphs.src(e)]^α[i] + for (e, i) in zip(spanning_forest, 1:length(α))]) isapprox(fwd, rev; atol = abstol, rtol = reltol) ? continue : return false end end @@ -1079,12 +1053,12 @@ end function isdetailedbalanced(rs::ReactionSystem, parametermap::Vector{<:Pair}) pdict = Dict(parametermap) - return isdetailedbalanced(rs, pdict) + isdetailedbalanced(rs, pdict) end function isdetailedbalanced(rs::ReactionSystem, parametermap::Tuple{<:Pair}) pdict = Dict(parametermap) - return isdetailedbalanced(rs, pdict) + isdetailedbalanced(rs, pdict) end function isdetailedbalanced(rs::ReactionSystem, parametermap) @@ -1128,12 +1102,12 @@ end function iscomplexbalanced(rs::ReactionSystem, parametermap::Vector{<:Pair}) pdict = Dict(parametermap) - return iscomplexbalanced(rs, pdict) + iscomplexbalanced(rs, pdict) end function iscomplexbalanced(rs::ReactionSystem, parametermap::Tuple) pdict = Dict(parametermap) - return iscomplexbalanced(rs, pdict) + iscomplexbalanced(rs, pdict) end function iscomplexbalanced(rs::ReactionSystem, parametermap) @@ -1191,7 +1165,7 @@ function adjacencymat(::Type{SparseMatrixCSC{T, Int}}, D, rates) where {T} push!(Js, p) push!(Vs, rates[r]) end - return A = sparse(Is, Js, Vs, nc, nc) + A = sparse(Is, Js, Vs, nc, nc) end function adjacencymat(::Type{Matrix{T}}, D, rates) where {T} @@ -1203,17 +1177,17 @@ function adjacencymat(::Type{Matrix{T}}, D, rates) where {T} p = findfirst(==(1), @view D[:, r]) A[s, p] = rates[r] end - return A + A end function adjacencymat(rn::ReactionSystem, pmap::Vector{<:Pair}; sparse = false) pdict = Dict(pmap) - return adjacencymat(rn, pdict; sparse) + adjacencymat(rn, pdict; sparse) end function adjacencymat(rn::ReactionSystem, pmap::Tuple; sparse = false) pdict = Dict(pmap) - return adjacencymat(rn, pdict; sparse) + adjacencymat(rn, pdict; sparse) end function adjacencymat(rn::ReactionSystem, pmap) @@ -1264,7 +1238,7 @@ function treeweight(t::SimpleDiGraph, g::SimpleDiGraph, distmx::Matrix) t = Graphs.dst(e) prod *= distmx[s, t] end - return prod + prod end """ @@ -1279,11 +1253,11 @@ function cycles(rs::ReactionSystem) nsm = netstoichmat(rs) !isempty(nps.cyclemat) && return nps.cyclemat nps.cyclemat = cycles(nsm; col_order = nps.col_order) - return nps.cyclemat + nps.cyclemat end function cycles(nsm::Matrix; col_order = nothing) - return positive_nullspace(nsm; col_order) + positive_nullspace(nsm; col_order) end function positive_nullspace(M::T; col_order = nothing) where {T <: AbstractMatrix} @@ -1296,13 +1270,11 @@ function positive_nullspace(M::T; col_order = nothing) where {T <: AbstractMatri end # check we haven't overflowed - iszero(M * N) || error( - "Calculation of the cycle matrix was inaccurate, " - * "likely due to numerical overflow. Please use a larger integer " - * "type like Int128 or BigInt for the net stoichiometry matrix." - ) + iszero(M * N) || error("Calculation of the cycle matrix was inaccurate, " + * "likely due to numerical overflow. Please use a larger integer " + * "type like Int128 or BigInt for the net stoichiometry matrix.") - return T(N) + T(N) end """ @@ -1311,7 +1283,7 @@ end See documentation for [`cycles`](@ref). """ function fluxvectors(rs::ReactionSystem) - return cycles(rs) + cycles(rs) end ### Deficiency one @@ -1335,7 +1307,7 @@ function satisfiesdeficiencyone(rn::ReactionSystem) # 1) the deficiency of each individual linkage class is at most 1; # 2) the sum of the linkage deficiencies is the total deficiency, and # 3) there is only one terminal linkage class per linkage class. - return all(<=(1), δ_l) && (sum(δ_l) == δ) && (length(lcs) == length(tslcs)) + all(<=(1), δ_l) && (sum(δ_l) == δ) && (length(lcs) == length(tslcs)) end """ @@ -1347,7 +1319,7 @@ function satisfiesdeficiencyzero(rn::ReactionSystem) all(r -> ismassaction(r, rn), reactions(rn)) || error("The deficiency zero theorem is only valid for reaction networks that are mass action.") δ = deficiency(rn) - return δ == 0 && isweaklyreversible(rn, subnetworks(rn)) + δ == 0 && isweaklyreversible(rn, subnetworks(rn)) end """ @@ -1393,5 +1365,5 @@ function robustspecies(rn::ReactionSystem) nps.robustspecies = robust_species end - return nps.robustspecies + nps.robustspecies end diff --git a/src/plotting.jl b/src/plotting.jl index f67b926d7a..b4efb42a96 100644 --- a/src/plotting.jl +++ b/src/plotting.jl @@ -58,16 +58,13 @@ end for (_, mime) in _showables MIMEType = typeof(MIME(mime)) - @eval Base.show( - io::IO, ::$MIMEType, s::Showable{>:$MIMEType}; - options... - ) = show( - io, $MIMEType(), s.content; s.options..., options... - ) + @eval Base.show(io::IO, ::$MIMEType, s::Showable{>:$MIMEType}; + options...) = show( + io, $MIMEType(), s.content; s.options..., options...) end macro mime_str(s) - return :(Showable{MIME{Symbol($s)}}) + :(Showable{MIME{Symbol($s)}}) end for (name, mime) in _showables diff --git a/src/reaction.jl b/src/reaction.jl index f1248d531a..8c1dd8c446 100644 --- a/src/reaction.jl +++ b/src/reaction.jl @@ -19,7 +19,7 @@ function isconstant(s) if iscall(s) && operation(s) === getindex s = first(arguments(s)) end - return MT.getmetadata(s, ParameterConstantSpecies, false) + MT.getmetadata(s, ParameterConstantSpecies, false) end """ @@ -29,7 +29,7 @@ Tests if the given symbolic variable corresponds to a boundary condition species """ isbc(s::Num) = isbc(value(s)) function isbc(s) - return MT.getmetadata(s, VariableBCSpecies, false) + MT.getmetadata(s, VariableBCSpecies, false) end """ @@ -56,9 +56,9 @@ Notes: function tospecies(s) MT.isparameter(s) && throw(ArgumentError("Parameters, including isconstantspecies parameters, can not be converted to species. Please pass a variable.")) - MT.getmetadata(unwrap(s), ParameterConstantSpecies, false) && - throw(ArgumentError("isconstantspecies metadata can only be used with parameters.")) - return MT.setmetadata(s, VariableSpecies, true) + MT.getmetadata(unwrap(s), ParameterConstantSpecies, false) && + throw(ArgumentError("isconstantspecies metadata can only be used with parameters.")) + MT.setmetadata(s, VariableSpecies, true) end # Other species functions. @@ -89,7 +89,7 @@ function get_netstoich(subs, prods, sstoich, pstoich) end # stoichiometry as a vector - return [el for el in nsdict if !_iszero(el[2])] + [el for el in nsdict if !_iszero(el[2])] end # Get the net stoichiometries' type. @@ -170,12 +170,10 @@ struct Reaction{S, T} end # Five-argument constructor accepting rate, substrates, and products, and their stoichiometries. -function Reaction( - rate, subs, prods, substoich, prodstoich; +function Reaction(rate, subs, prods, substoich, prodstoich; netstoich = nothing, metadata = Pair{Symbol, Any}[], only_use_rate = metadata_only_use_rate_check(metadata), - unit_checks::Bool = false, kwargs... - ) + unit_checks::Bool = false, kwargs...) # Handles empty/nothing vectors. isnothing(subs) || isempty(subs) && (subs = nothing) isnothing(prods) || isempty(prods) && (prods = nothing) @@ -232,7 +230,7 @@ function Reaction( get_netstoich(subs, prods, substoich′, prodstoich′) else (netstoich_stoichtype(netstoich) != stoich_type) ? - convert.(stoich_type, netstoich) : netstoich + convert.(stoich_type, netstoich) : netstoich end # Check that all metadata entries are unique. (cannot use `in` since some entries may be symbolics). @@ -250,14 +248,14 @@ function Reaction( rx = Reaction(value(rate), subs, prods, substoich′, prodstoich′, ns, only_use_rate, metadata) unit_checks && assert_valid_units(rx) - return rx + rx end # Three argument constructor assumes stoichiometric coefs are one and integers. function Reaction(rate, subs, prods; kwargs...) sstoich = isnothing(subs) ? nothing : ones(Int, length(subs)) pstoich = isnothing(prods) ? nothing : ones(Int, length(prods)) - return Reaction(rate, subs, prods, sstoich, pstoich; kwargs...) + Reaction(rate, subs, prods, sstoich, pstoich; kwargs...) end ### Base Function Dispatches ### @@ -270,7 +268,7 @@ function print_rxside(io::IO, specs, stoich) else for (i, spec) in enumerate(specs) prspec = (MT.isparameter(spec) || (MT.operation(spec) == getindex)) ? - spec : MT.operation(spec) + spec : MT.operation(spec) if isequal(stoich[i], one(stoich[i])) print(io, prspec) elseif iscall(stoich[i]) @@ -282,7 +280,7 @@ function print_rxside(io::IO, specs, stoich) (i < length(specs)) && print(io, " + ") end end - return nothing + nothing end # Show function for `Reaction`s. @@ -291,7 +289,7 @@ function Base.show(io::IO, rx::Reaction) print_rxside(io, rx.substrates, rx.substoich) arrow = rx.only_use_rate ? "⇒" : "-->" print(io, " ", arrow, " ") - return print_rxside(io, rx.products, rx.prodstoich) + print_rxside(io, rx.products, rx.prodstoich) end """ @@ -311,7 +309,7 @@ function (==)(rx1::Reaction, rx2::Reaction) issetequal(zip(rx1.products, rx1.prodstoich), zip(rx2.products, rx2.prodstoich)) || return false issetequal(rx1.netstoich, rx2.netstoich) || return false - return rx1.only_use_rate == rx2.only_use_rate + rx1.only_use_rate == rx2.only_use_rate end # Hash function. @@ -326,7 +324,7 @@ function hash(rx::Reaction, h::UInt) for s in rx.netstoich h ⊻= hash(s) end - return Base.hash(rx.only_use_rate, h) + Base.hash(rx.only_use_rate, h) end ### ModelingToolkit Function Dispatches ### @@ -336,7 +334,7 @@ function apply_if_nonempty(f, v) isempty(v) && return v s = similar(v) map!(f, s, v) - return s + s end # Returns a name-spaced version of a reaction. @@ -353,10 +351,8 @@ function MT.namespace_equation(rx::Reaction, name; kw...) ns = similar(rx.netstoich) map!(n -> f(n[1]) => f(n[2]), ns, rx.netstoich) end - return Reaction( - rate, subs, prods, substoich, prodstoich, netstoich, - rx.only_use_rate, rx.metadata - ) + Reaction(rate, subs, prods, substoich, prodstoich, netstoich, + rx.only_use_rate, rx.metadata) end # Overwrites equation-type functions to give the correct input for `Reaction`s. @@ -365,11 +361,9 @@ MT.is_alg_equation(rx::Reaction) = false # MTK functions for extracting variables within equation type object MT.eqtype_supports_collect_vars(rx::Reaction) = true -function MT.collect_vars!( - unknowns::OrderedSet{SymbolicT}, parameters::OrderedSet{SymbolicT}, +function MT.collect_vars!(unknowns::OrderedSet{SymbolicT}, parameters::OrderedSet{SymbolicT}, rx::Reaction, iv::Union{SymbolicT, Nothing}, ::Type{op} = Symbolics.Operator; - depth = 0 - ) where {op} + depth = 0) where {op} MT.collect_vars!(unknowns, parameters, rx.rate, iv, op; depth) for items in (rx.substrates, rx.products, rx.substoich, rx.prodstoich) @@ -433,7 +427,7 @@ function MT.get_variables!(deps::Set, rx::Reaction, variables) # parametric stoichiometry means may have a parameter as a substrate any(isequal(s), variables) && push!(deps, s) end - return deps + deps end # determine which species a reaction modifies @@ -441,14 +435,14 @@ function MT.modified_unknowns!(munknowns, rx::Reaction, sts::Set) for (species, stoich) in rx.netstoich (species in sts) && push!(munknowns, species) end - return munknowns + munknowns end function MT.modified_unknowns!(munknowns, rx::Reaction, sts::AbstractVector) for (species, stoich) in rx.netstoich any(isequal(species), sts) && push!(munknowns, species) end - return munknowns + munknowns end ### `Reaction`-specific Functions ### @@ -475,7 +469,7 @@ function isbcbalanced(rx::Reaction) end end - return true + true end ### Reaction Metadata Implementation ### @@ -562,7 +556,7 @@ function SymbolicUtils.setmetadata(rx::Reaction, key::Symbol, val) else mdvec[idx] = key => val end - return nothing + nothing end ### Catalyst Defined Reaction Metadata ### @@ -769,13 +763,9 @@ function unit_validation_report(rx::Reaction; info::String = "") # Symbolic exponents on unitful bases have indeterminate units. if _has_symbolic_unitful_pow(rx.rate) - push!( - issues, UnitValidationIssue( - :symbolic_exponent, - string(rx), nothing, nothing, - "Symbolic exponent on unitful base is not supported for unit validation" - ) - ) + push!(issues, UnitValidationIssue(:symbolic_exponent, + string(rx), nothing, nothing, + "Symbolic exponent on unitful base is not supported for unit validation")) return UnitValidationReport(false, issues) end @@ -785,12 +775,8 @@ function unit_validation_report(rx::Reaction; info::String = "") for i in 2:length(rx.substrates) if !_units_match(catalyst_get_unit(rx.substrates[i]), subunits) valid = false - push!( - issues, UnitValidationIssue( - :reaction_species_unit_mismatch, string(rx), subunits, - catalyst_get_unit(rx.substrates[i]), "substrates have differing units" - ) - ) + push!(issues, UnitValidationIssue(:reaction_species_unit_mismatch, string(rx), subunits, + catalyst_get_unit(rx.substrates[i]), "substrates have differing units")) end end @@ -798,26 +784,18 @@ function unit_validation_report(rx::Reaction; info::String = "") for i in 2:length(rx.products) if !_units_match(catalyst_get_unit(rx.products[i]), produnits) valid = false - push!( - issues, UnitValidationIssue( - :reaction_species_unit_mismatch, string(rx), produnits, - catalyst_get_unit(rx.products[i]), "products have differing units" - ) - ) + push!(issues, UnitValidationIssue(:reaction_species_unit_mismatch, string(rx), produnits, + catalyst_get_unit(rx.products[i]), "products have differing units")) end end if (subunits !== nothing) && (produnits !== nothing) && !_units_match(subunits, produnits) valid = false - push!( - issues, UnitValidationIssue( - :reaction_side_unit_mismatch, string(rx), subunits, - produnits, "Substrate units are not consistent with product units." - ) - ) + push!(issues, UnitValidationIssue(:reaction_side_unit_mismatch, string(rx), subunits, + produnits, "Substrate units are not consistent with product units.")) end - return UnitValidationReport(valid, issues) + UnitValidationReport(valid, issues) end """ diff --git a/src/reactionsystem.jl b/src/reactionsystem.jl index 7d885e30db..8db6feb94c 100644 --- a/src/reactionsystem.jl +++ b/src/reactionsystem.jl @@ -27,49 +27,45 @@ struct ReactionComplex{V <: Integer} <: AbstractVector{ReactionComplexElement{V} """The stoichiometric coefficients of all species participating in this complex.""" speciesstoichs::Vector{V} - function ReactionComplex{V}( - speciesids::Vector{Int}, - speciesstoichs::Vector{V} - ) where {V <: Integer} - return new{V}(speciesids, speciesstoichs) + function ReactionComplex{V}(speciesids::Vector{Int}, + speciesstoichs::Vector{V}) where {V <: Integer} + new{V}(speciesids, speciesstoichs) end end # Special constructor. -function ReactionComplex( - speciesids::Vector{Int}, - speciesstoichs::Vector{V} - ) where {V <: Integer} +function ReactionComplex(speciesids::Vector{Int}, + speciesstoichs::Vector{V}) where {V <: Integer} (length(speciesids) == length(speciesstoichs)) || error("Creating a complex with different number of species ids and associated stoichiometries.") - return ReactionComplex{V}(speciesids, speciesstoichs) + ReactionComplex{V}(speciesids, speciesstoichs) end # Defines base function overloads for `ReactionComplex`. function (==)(a::ReactionComplex{V}, b::ReactionComplex{V}) where {V <: Integer} - return (a.speciesids == b.speciesids) && + (a.speciesids == b.speciesids) && (a.speciesstoichs == b.speciesstoichs) end function hash(rc::ReactionComplex, h::UInt) - return Base.hash(rc.speciesids, Base.hash(rc.speciesstoichs, h)) + Base.hash(rc.speciesids, Base.hash(rc.speciesstoichs, h)) end Base.size(rc::ReactionComplex) = size(rc.speciesids) Base.length(rc::ReactionComplex) = length(rc.speciesids) function Base.getindex(rc::ReactionComplex, i...) - return ReactionComplexElement(getindex(rc.speciesids, i...), getindex(rc.speciesstoichs, i...)) + ReactionComplexElement(getindex(rc.speciesids, i...), getindex(rc.speciesstoichs, i...)) end function Base.setindex!(rc::ReactionComplex, t::ReactionComplexElement, i...) setindex!(rc.speciesids, t.speciesid, i...) setindex!(rc.speciesstoichs, t.speciesstoich, i...) - return rc + rc end function Base.isless(a::ReactionComplexElement, b::ReactionComplexElement) - return isless(a.speciesid, b.speciesid) + isless(a.speciesid, b.speciesid) end Base.Sort.defalg(::ReactionComplex) = Base.DEFAULT_UNSTABLE @@ -125,7 +121,7 @@ end # Defines base function overloads for `NetworkProperties`. function Base.show(io::IO, nps::NetworkProperties) - return if (nps.conservationmat !== nothing) + if (nps.conservationmat !== nothing) println(io, "Conserved Equations: ") foreach(eq -> println(io, eq), nps.conservedeqs) println() @@ -134,7 +130,7 @@ end Base.isempty(nps::NetworkProperties) = getfield(nps, :isempty) function Base.setproperty!(nps::NetworkProperties, sym::Symbol, x) (sym !== :isempty) && setfield!(nps, :isempty, false) - return setfield!(nps, sym, x) + setfield!(nps, sym, x) end # Resets computed properties. @@ -167,11 +163,11 @@ function reset!(nps::NetworkProperties{I, V}) where {I, V} # this needs to be last due to setproperty! setting it to false nps.isempty = true - return nothing + nothing end function initialized_conserved(nps::NetworkProperties) - return nps.conservedconst !== __UNINITIALIZED_CONSERVED_CONSTS + nps.conservedconst !== __UNINITIALIZED_CONSERVED_CONSTS end ### ReactionSystem Constructor Functions ### @@ -183,17 +179,17 @@ eqsortby(eq::CatalystEqType) = eq isa Reaction ? 1 : 2 # its condition and affect functions to their respective vectors (`ps` and `us`). # Uses MT.collect_vars! for consistency with other variable discovery in the codebase. function find_event_vars!(ps, us, events::Vector, t) - return foreach(event -> find_event_vars!(ps, us, event, t), events) + foreach(event -> find_event_vars!(ps, us, event, t), events) end # For a single event, adds quantities from its condition and affect expression(s) to `ps` and `us`. # Two dispatches required: events can be given as a MTK callback structure or a Pair of symbolic expressions. function find_event_vars!(ps, us, event::Pair, t) MT.collect_vars!(us, ps, event[1], t) # condition - return MT.collect_vars!(us, ps, event[2], t) # affect + MT.collect_vars!(us, ps, event[2], t) # affect end function find_event_vars!(ps, us, event::MT.AbstractCallback, t) MT.collect_vars!(us, ps, event.conditions, t) - return MT.collect_vars!(us, ps, event.affect.affect, t) + MT.collect_vars!(us, ps, event.affect.affect, t) end # Loops through all jumps, adding all unknowns and parameters found to their respective vectors. @@ -202,7 +198,6 @@ function find_jump_vars!(ps, us, jumps::Vector, t) for jump in jumps MT.collect_vars!(us, ps, jump, t) end - return end # Loops through all tstop expressions, adding encountered unknowns and parameters @@ -212,7 +207,6 @@ function find_tstop_vars!(ps, us, tstops::Vector, t) for ts in tstops MT.collect_vars!(us, ps, unwrap(ts), t) end - return end # Validates that tstop expressions only contain parameters and constants (no unknowns or @@ -242,7 +236,7 @@ function check_tstops(tstops, unknowns, iv) end end end - return nothing + nothing end ### ReactionSystem Structure ### @@ -261,8 +255,7 @@ const reactionsystem_fields = ( :eqs, :rxs, :iv, :sivs, :unknowns, :species, :ps, :var_to_name, :observed, :name, :systems, :bindings, :initial_conditions, :networkproperties, :combinatoric_ratelaws, :continuous_events, - :discrete_events, :tstops, :brownians, :poissonians, :jumps, :metadata, :complete, :parent, -) + :discrete_events, :tstops, :brownians, :poissonians, :jumps, :metadata, :complete, :parent) """ $(TYPEDEF) @@ -386,12 +379,10 @@ struct ReactionSystem{V <: NetworkProperties} <: MT.AbstractSystem parent::Union{Nothing, ReactionSystem} # 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, + function ReactionSystem(eqs, rxs, iv, sivs, unknowns, spcs, ps, var_to_name, observed, name, systems, bindings, initial_conditions, nps, cls, cevs, devs, tstops, brownians, poissonians, jumps, metadata, complete = false, parent = nothing; - checks::Bool = true, unit_checks::Bool = false - ) + checks::Bool = true, unit_checks::Bool = false) # Structural checks (fast, always on by default). if checks && isempty(sivs) @@ -412,10 +403,9 @@ struct ReactionSystem{V <: NetworkProperties} <: MT.AbstractSystem rs = new{typeof(nps)}( eqs, rxs, iv, sivs, unknowns, spcs, ps, var_to_name, observed, name, systems, bindings, initial_conditions, nps, cls, cevs, - devs, tstops, brownians, poissonians, jumps, metadata, complete, parent - ) + devs, tstops, brownians, poissonians, jumps, metadata, complete, parent) unit_checks && assert_valid_units(rs; info = string("ReactionSystem constructor for ", name)) - return rs + rs end end @@ -432,8 +422,7 @@ end # use the two-argument constructor for auto-discovery from equations. # `unit_checks = true` validates species unit consistency, reaction rate units, and # equation unit balance at construction time. Default is `false`. -function ReactionSystem( - eqs, iv, unknowns, ps, brownians = SymbolicT[]; +function ReactionSystem(eqs, iv, unknowns, ps, brownians = SymbolicT[]; poissonians = SymbolicT[], jumps = JumpType[], observed = Equation[], @@ -451,8 +440,7 @@ function ReactionSystem( discrete_events = nothing, tstops = [], metadata = MT.MetadataT(), - disable_forbidden_symbol_check = false - ) + disable_forbidden_symbol_check = false) # Error checks name === nothing && @@ -551,12 +539,11 @@ function ReactionSystem( # got promoted to Num by array promotion in `[param, 5.0]`). tstops′ = Any[Symbolics.value(ts) for ts in tstops] - return ReactionSystem( + ReactionSystem( eqs′, rxs, iv′, sivs′, unknowns′, spcs, ps′, var_to_name, observed, name, systems, bindings, initial_conditions, nps, combinatoric_ratelaws, continuous_events, discrete_events, tstops′, brownians′, poissonians′, jumps′, metadata; - checks, unit_checks - ) + checks, unit_checks) end # Handles that events can be a single event or a vector. @@ -582,18 +569,18 @@ function make_metadata(metadata) end metadata = meta end - return metadata = MT.refreshed_metadata(metadata) + metadata = MT.refreshed_metadata(metadata) end # Two-argument constructor (reactions/equations and time variable). # Calls the `make_ReactionSystem_internal`, which in turn calls the four-argument constructor. function ReactionSystem(rxs::Vector, iv = Catalyst.DEFAULT_IV; kwargs...) - return make_ReactionSystem_internal(rxs, iv, [], []; kwargs...) + make_ReactionSystem_internal(rxs, iv, [], []; kwargs...) end # One-argument constructor. Creates an empty `ReactionSystem` from a time independent variable only. function ReactionSystem(iv; kwargs...) - return ReactionSystem(Reaction[], iv, [], []; kwargs...) + ReactionSystem(Reaction[], iv, [], []; kwargs...) end # Called internally (whether DSL-based or programmatic model creation is used). @@ -602,11 +589,9 @@ end # the model creation) and creates the corresponding vectors. # While species are ordered before variables in the unknowns vector, this ordering is not imposed here, # but carried out at a later stage. -function make_ReactionSystem_internal( - rxs_and_eqs::Vector, iv, us_in, ps_in, brownians = SymbolicT[]; +function make_ReactionSystem_internal(rxs_and_eqs::Vector, iv, us_in, ps_in, brownians = SymbolicT[]; poissonians = SymbolicT[], spatial_ivs = nothing, continuous_events = [], - discrete_events = [], tstops = [], observed = [], jumps = JumpType[], kwargs... - ) + discrete_events = [], tstops = [], observed = [], jumps = JumpType[], kwargs...) # Error if any observables have been declared a species or variable obs_vars = Set(obs_eq.lhs for obs_eq in observed) @@ -680,7 +665,7 @@ function make_ReactionSystem_internal( if iscall(p) && operation(p) === getindex par = arguments(p)[begin] if MT.symbolic_has_known_size(par) && - all(par[i] in ps for i in eachindex(par)) + all(par[i] in ps for i in eachindex(par)) push!(new_ps, par) else push!(new_ps, p) @@ -693,10 +678,8 @@ function make_ReactionSystem_internal( # Passes the processed input into the next `ReactionSystem` call. # Note: brownians are passed as the 5th positional argument. - return ReactionSystem( - fulleqs, t, usv, psv, brownians; poissonians, spatial_ivs, - continuous_events, discrete_events, tstops, observed, jumps, kwargs... - ) + ReactionSystem(fulleqs, t, usv, psv, brownians; poissonians, spatial_ivs, + continuous_events, discrete_events, tstops, observed, jumps, kwargs...) end ### Base Function Dispatches ### @@ -720,19 +703,19 @@ end function symbolic_affect_matches(aff1, aff2) (isnothing(aff1) && isnothing(aff2)) && return true (isnothing(aff1) || isnothing(aff2)) && return false - return issetequal(aff1.affect, aff2.affect) && issetequal(aff1.discrete_parameters, aff2.discrete_parameters) + issetequal(aff1.affect, aff2.affect) && issetequal(aff1.discrete_parameters, aff2.discrete_parameters) end # Compare two SymbolicContinuousCallbacks by their conditions and affects. function continuous_event_matches(evt1, evt2) - return issetequal(evt1.conditions, evt2.conditions) && + issetequal(evt1.conditions, evt2.conditions) && symbolic_affect_matches(evt1.affect, evt2.affect) && symbolic_affect_matches(evt1.affect_neg, evt2.affect_neg) end # Compare two SymbolicDiscreteCallbacks by their conditions and affects. function discrete_event_matches(evt1, evt2) - return isequal(evt1.conditions, evt2.conditions) && symbolic_affect_matches(evt1.affect, evt2.affect) + isequal(evt1.conditions, evt2.conditions) && symbolic_affect_matches(evt1.affect, evt2.affect) end # Convenience wrappers for continuous and discrete event comparison. @@ -743,10 +726,8 @@ function debug_comparer(fun, prop1, prop2, propname; debug = false) if fun(prop1, prop2) return true else - debug && println( - "Comparison was false for property: ", propname, - "\n Found: ", prop1, " vs ", prop2 - ) + debug && println("Comparison was false for property: ", propname, + "\n Found: ", prop1, " vs ", prop2) return false end end @@ -769,19 +750,15 @@ Notes: - Pass `debug = true` to print out the field that caused the two systems to be considered different. """ -function isequivalent( - rn1::ReactionSystem, rn2::ReactionSystem; ignorenames = true, - debug = false - ) +function isequivalent(rn1::ReactionSystem, rn2::ReactionSystem; ignorenames = true, + debug = false) # metadata type fields if !ignorenames debug_comparer(==, nameof(rn1), nameof(rn2), "name"; debug) || return false end - debug_comparer( - ==, get_combinatoric_ratelaws(rn1), get_combinatoric_ratelaws(rn2), - "combinatoric_ratelaws"; debug - ) || return false + debug_comparer(==, get_combinatoric_ratelaws(rn1), get_combinatoric_ratelaws(rn2), + "combinatoric_ratelaws"; debug) || return false debug_comparer(==, MT.iscomplete(rn1), MT.iscomplete(rn2), "complete"; debug) || return false @@ -796,25 +773,19 @@ function isequivalent( debug_comparer(issetequal, MT.get_bindings(rn1), MT.get_bindings(rn2), "bindings"; debug) || return false debug_comparer( - issetequal, MT.get_initial_conditions(rn1), MT.get_initial_conditions(rn2), "defaults"; debug - ) || + issetequal, MT.get_initial_conditions(rn1), MT.get_initial_conditions(rn2), "defaults"; debug) || return false # equations and reactions debug_comparer( - issetequal, MT.get_observed(rn1), MT.get_observed(rn2), "observed"; debug - ) || + issetequal, MT.get_observed(rn1), MT.get_observed(rn2), "observed"; debug) || return false debug_comparer(issetequal, get_eqs(rn1), get_eqs(rn2), "eqs"; debug) || return false # Use custom event comparison functions to work around MTK issue #3907 - debug_comparer( - continuous_events_equal, MT.get_continuous_events(rn1), - MT.get_continuous_events(rn2), "cevents"; debug - ) || return false - debug_comparer( - discrete_events_equal, MT.get_discrete_events(rn1), - MT.get_discrete_events(rn2), "devents"; debug - ) || return false + debug_comparer(continuous_events_equal, MT.get_continuous_events(rn1), + MT.get_continuous_events(rn2), "cevents"; debug) || return false + debug_comparer(discrete_events_equal, MT.get_discrete_events(rn1), + MT.get_discrete_events(rn2), "devents"; debug) || return false debug_comparer(issetequal, MT.get_tstops(rn1), MT.get_tstops(rn2), "tstops"; debug) || return false @@ -834,14 +805,12 @@ function isequivalent( # Use isequivalent recursively for subsystems instead of issetequal (which uses ==). # This is needed because == on ReactionSystems uses object identity. if !systems_are_equivalent(get_systems(rn1), get_systems(rn2); ignorenames, debug) - debug && println( - "Comparison was false for property: systems", - "\n Found: ", get_systems(rn1), " vs ", get_systems(rn2) - ) + debug && println("Comparison was false for property: systems", + "\n Found: ", get_systems(rn1), " vs ", get_systems(rn2)) return false end - return true + true end # Helper for isequivalent: checks if two collections of ReactionSystems are equivalent. @@ -919,7 +888,7 @@ during calls to `convert` of problem constructors. function combinatoric_ratelaws(sys::ReactionSystem) crl = get_combinatoric_ratelaws(sys) subsys = Iterators.filter(s -> s isa ReactionSystem, get_systems(sys)) - return mapreduce(combinatoric_ratelaws, |, subsys; init = crl) + mapreduce(combinatoric_ratelaws, |, subsys; init = crl) end # Gets sub systems that are also reaction systems. @@ -930,14 +899,14 @@ function filter_nonrxsys(network) for sys in systems (sys isa ReactionSystem) && push!(rxsystems, sys) end - return rxsystems + rxsystems end # Special species but which take a set of states and names spaces them according to another # `ReactionSystem`. # Used by `species(network)`. function species(network::ReactionSystem, sts) - return [MT.renamespace(network, st) for st in sts] + [MT.renamespace(network, st) for st in sts] end """ @@ -955,7 +924,7 @@ function species(network) sts = get_species(network) systems = filter_nonrxsys(network) isempty(systems) && return sts - return unique([sts; reduce(vcat, map(sys -> species(sys, species(sys)), systems))]) + unique([sts; reduce(vcat, map(sys -> species(sys, species(sys)), systems))]) end """ @@ -969,7 +938,7 @@ function numspecies(network) for sys in get_systems(network) (sys isa ReactionSystem) && (numspcs += numspecies(sys)) end - return numspcs + numspcs end """ @@ -982,7 +951,7 @@ Notes: - Allocates a new array to store the non-species variables. """ function nonspecies(network) - return unknowns(network)[(numspecies(network) + 1):end] + unknowns(network)[(numspecies(network) + 1):end] end """ @@ -996,7 +965,7 @@ function speciesmap(network) if isempty(nps.speciesmap) nps.speciesmap = Dict(S => i for (i, S) in enumerate(species(network))) end - return nps.speciesmap + nps.speciesmap end # get the non-bc, independent unknown variables and independent species, preserving their @@ -1010,7 +979,7 @@ function get_indep_sts(rs::ReactionSystem, remove_conserved = false) else filter(s -> !isbc(s), sts) end - return indepsts, filter(isspecies, indepsts) + indepsts, filter(isspecies, indepsts) end """ @@ -1023,7 +992,7 @@ function numparams(network) for sys in get_systems(network) nps += numparams(sys) end - return nps + nps end """ @@ -1034,14 +1003,14 @@ parameters that appear within the system to their index within `parameters(network)`. """ function paramsmap(network) - return Dict(p => i for (i, p) in enumerate(parameters(network))) + Dict(p => i for (i, p) in enumerate(parameters(network))) end # used in the next function (`reactions(network)`). function namespace_reactions(network::ReactionSystem) rxs = reactions(network) isempty(rxs) && return Reaction[] - return map(rx -> namespace_equation(rx, network), rxs) + map(rx -> namespace_equation(rx, network), rxs) end """ @@ -1056,7 +1025,7 @@ function reactions(network) rxs = get_rxs(network) systems = filter_nonrxsys(network) isempty(systems) && (return rxs) - return [rxs; reduce(vcat, namespace_reactions.(systems); init = Reaction[])] + [rxs; reduce(vcat, namespace_reactions.(systems); init = Reaction[])] end """ @@ -1070,7 +1039,7 @@ function numreactions(network) for sys in get_systems(network) (sys isa ReactionSystem) && (nr += numreactions(sys)) end - return nr + nr end """ @@ -1080,7 +1049,7 @@ Check if the given `network` has any non-reaction equations such as ODEs or alge equations. """ function has_nonreactions(network) - return numreactions(network) != length(equations(network)) + numreactions(network) != length(equations(network)) end """ @@ -1092,7 +1061,7 @@ Notes: - Allocates a new array to store the non-species variables. """ function nonreactions(network) - return equations(network)[(numreactions(network) + 1):end] + equations(network)[(numreactions(network) + 1):end] end """ @@ -1102,7 +1071,7 @@ Given a [`ReactionSystem`](@ref), returns a vector of the symbolic reaction rates for each reaction. """ function reactionrates(rn) - return [r.rate for r in reactions(rn)] + [r.rate for r in reactions(rn)] end """ @@ -1120,10 +1089,8 @@ function MT.equations(sys::ReactionSystem) eqs = get_eqs(sys) systems = get_systems(sys) if !isempty(systems) - eqs = CatalystEqType[ - eqs; - reduce(vcat, MT.namespace_equations.(systems); init = CatalystEqType[]) - ] + eqs = CatalystEqType[eqs; + reduce(vcat, MT.namespace_equations.(systems); init = CatalystEqType[])] return sort!(eqs; by = eqsortby) end return eqs @@ -1155,7 +1122,7 @@ function MT.complete(sys::ReactionSystem; flatten = true, kwargs...) end sys = newsys end - return isdefined(sys, :complete) ? (@set! sys.complete = true) : sys + isdefined(sys, :complete) ? (@set! sys.complete = true) : sys end ### Network Matrix Representations ### @@ -1171,10 +1138,8 @@ Note: - Note that constant species are not considered substrates, but just components that modify the associated rate law. """ -function substoichmat( - ::Type{SparseMatrixCSC{T, Int}}, - rn::ReactionSystem - ) where {T <: Number} +function substoichmat(::Type{SparseMatrixCSC{T, Int}}, + rn::ReactionSystem) where {T <: Number} Is = Int[] Js = Int[] Vs = T[] @@ -1188,7 +1153,7 @@ function substoichmat( push!(Vs, stoich[i]) end end - return sparse(Is, Js, Vs, numspecies(rn), numreactions(rn)) + sparse(Is, Js, Vs, numspecies(rn), numreactions(rn)) end function substoichmat(::Type{Matrix{T}}, rn::ReactionSystem) where {T <: Number} @@ -1201,7 +1166,7 @@ function substoichmat(::Type{Matrix{T}}, rn::ReactionSystem) where {T <: Number} smat[smap[sub], k] = stoich[i] end end - return smat + smat end function substoichmat(rn::ReactionSystem; sparse::Bool = false) @@ -1209,7 +1174,7 @@ function substoichmat(rn::ReactionSystem; sparse::Bool = false) T = reduce(promote_type, eltype(rx.substoich) for rx in reactions(rn)) (T == Any) && error("Stoichiometry matrices with symbolic stoichiometry are not supported") - return sparse ? substoichmat(SparseMatrixCSC{T, Int}, rn) : substoichmat(Matrix{T}, rn) + sparse ? substoichmat(SparseMatrixCSC{T, Int}, rn) : substoichmat(Matrix{T}, rn) end """ @@ -1223,10 +1188,8 @@ Note: - Note that constant species are not treated as products, but just components that modify the associated rate law. """ -function prodstoichmat( - ::Type{SparseMatrixCSC{T, Int}}, - rn::ReactionSystem - ) where {T <: Number} +function prodstoichmat(::Type{SparseMatrixCSC{T, Int}}, + rn::ReactionSystem) where {T <: Number} Is = Int[] Js = Int[] Vs = T[] @@ -1240,7 +1203,7 @@ function prodstoichmat( push!(Vs, stoich[i]) end end - return sparse(Is, Js, Vs, numspecies(rn), numreactions(rn)) + sparse(Is, Js, Vs, numspecies(rn), numreactions(rn)) end function prodstoichmat(::Type{Matrix{T}}, rn::ReactionSystem) where {T <: Number} @@ -1253,7 +1216,7 @@ function prodstoichmat(::Type{Matrix{T}}, rn::ReactionSystem) where {T <: Number pmat[smap[prod], k] = stoich[i] end end - return pmat + pmat end function prodstoichmat(rn::ReactionSystem; sparse = false) @@ -1263,7 +1226,7 @@ function prodstoichmat(rn::ReactionSystem; sparse = false) T = reduce(promote_type, eltype(rx.prodstoich) for rx in reactions(rn)) (T == Any) && error("Stoichiometry matrices with symbolic stoichiometry are not supported") - return sparse ? prodstoichmat(SparseMatrixCSC{T, Int}, rn) : prodstoichmat(Matrix{T}, rn) + sparse ? prodstoichmat(SparseMatrixCSC{T, Int}, rn) : prodstoichmat(Matrix{T}, rn) end # Used in `netstoichmat` function. @@ -1281,10 +1244,8 @@ Notes: - Note that constant species are not treated as reactants, but just components that modify the associated rate law. As such they do not contribute to the net stoichiometry matrix. """ -function netstoichmat( - ::Type{SparseMatrixCSC{T, Int}}, - rn::ReactionSystem - ) where {T <: Number} +function netstoichmat(::Type{SparseMatrixCSC{T, Int}}, + rn::ReactionSystem) where {T <: Number} Is = Int[] Js = Int[] Vs = Vector{T}() @@ -1297,7 +1258,7 @@ function netstoichmat( push!(Vs, coef) end end - return sparse(Is, Js, Vs, numspecies(rn), numreactions(rn)) + sparse(Is, Js, Vs, numspecies(rn), numreactions(rn)) end function netstoichmat(::Type{Matrix{T}}, rn::ReactionSystem) where {T <: Number} @@ -1309,7 +1270,7 @@ function netstoichmat(::Type{Matrix{T}}, rn::ReactionSystem) where {T <: Number} nmat[smap[spec], k] = coef end end - return nmat + nmat end function netstoichmat(rn::ReactionSystem; sparse = false) @@ -1338,7 +1299,7 @@ function netstoichmat(rn::ReactionSystem; sparse = false) nps.netstoichmat = nsmat end - return nsmat + nsmat end ### General `ReactionSystem`-specific Functions ### @@ -1348,7 +1309,7 @@ end # certain functionalities which would break if the `ReactionSystem` structure is updated without # also updating these functionalities. function reactionsystem_uptodate_check() - return if fieldnames(ReactionSystem) != reactionsystem_fields + if fieldnames(ReactionSystem) != reactionsystem_fields @warn "The `ReactionSystem` structure have been modified without this being taken into account in the functionality you are attempting to use. Please report this at https://github.com/SciML/Catalyst.jl/issues. Proceed with caution, as there might be errors in whichever functionality you are attempting to use." end end @@ -1361,7 +1322,7 @@ properties need to be recalculated for some reason. """ function reset_networkproperties!(rn::ReactionSystem) reset!(get_networkproperties(rn)) - return nothing + nothing end """ @@ -1406,7 +1367,7 @@ end See documentation for [`dependents`](@ref). """ function dependants(rx, network) - return dependents(rx, network) + dependents(rx, network) end """ @@ -1465,7 +1426,7 @@ end function set_default_metadata(rs::ReactionSystem; default_reaction_metadata = []) # Updates reaction metadata for reactions in this specific system. function eqtransform(eq) - return eq isa Reaction ? set_default_metadata(eq, default_reaction_metadata) : eq + eq isa Reaction ? set_default_metadata(eq, default_reaction_metadata) : eq end updated_equations = map(eqtransform, get_eqs(rs)) @set! rs.eqs = updated_equations @@ -1482,8 +1443,7 @@ function set_default_metadata(rs::ReactionSystem; default_reaction_metadata = [] ns_sps = Iterators.filter(Catalyst.isspecies, ns_syms) ns_vs = Iterators.filter( sym -> !Catalyst.isspecies(sym) && - !MT.isparameter(sym), ns_syms - ) + !MT.isparameter(sym), ns_syms) # Adds parameters, species, and variables to the `ReactionSystem`. @set! rs.ps = union(get_ps(rs), ns_ps) sps_new = union(get_species(rs), ns_sps) @@ -1506,8 +1466,7 @@ end # For a `Reaction`, adds missing default metadata values. Equations are passed back unmodified. function set_default_metadata(rx::Reaction, default_metadata) missing_metadata = filter( - md -> !in(md[1], entry[1] for entry in rx.metadata), default_metadata - ) + md -> !in(md[1], entry[1] for entry in rx.metadata), default_metadata) updated_metadata = vcat(rx.metadata, missing_metadata) updated_metadata = convert(Vector{Pair{Symbol, Any}}, updated_metadata) return @set rx.metadata = updated_metadata @@ -1527,8 +1486,7 @@ Arguments: """ function set_default_noise_scaling(rs::ReactionSystem, noise_scaling) return remake_ReactionSystem_internal( - rs, default_reaction_metadata = [:noise_scaling => noise_scaling] - ) + rs, default_reaction_metadata = [:noise_scaling => noise_scaling]) end ### ReactionSystem Composing & Hierarchical Modelling ### @@ -1540,7 +1498,7 @@ Construct an empty [`ReactionSystem`](@ref). `iv` is the independent variable, usually time, and `name` is the name to give the `ReactionSystem`. """ function make_empty_network(; iv = DEFAULT_IV, name = gensym(:ReactionSystem)) - return ReactionSystem(Reaction[], iv, SymbolicT[], SymbolicT[]; name = name) + ReactionSystem(Reaction[], iv, SymbolicT[], SymbolicT[]; name = name) end # Checks if a system is an allowed subsystem. @@ -1573,8 +1531,7 @@ function MT.flatten(rs::ReactionSystem; name = nameof(rs)) # Note: brownians are 5th positional arg; poissonians and jumps are kwargs. # MT.brownians(rs), MT.poissonians(rs), and MT.jumps(rs) are recursive accessors # that collect from all subsystems. - return ReactionSystem( - equations(rs), get_iv(rs), unknowns(rs), parameters(rs), MT.brownians(rs); + ReactionSystem(equations(rs), get_iv(rs), unknowns(rs), parameters(rs), MT.brownians(rs); poissonians = MT.poissonians(rs), jumps = MT.jumps(rs), observed = MT.observed(rs), @@ -1587,15 +1544,14 @@ function MT.flatten(rs::ReactionSystem; name = nameof(rs)) continuous_events = MT.continuous_events(rs), discrete_events = MT.discrete_events(rs), tstops = MT.symbolic_tstops(rs), - metadata = MT.get_metadata(rs) - ) + metadata = MT.get_metadata(rs)) end function complete_check(sys, method) if MT.iscomplete(sys) error("$method with one or more `ReactionSystem`s requires systems to not be marked complete, but system: $(MT.get_name(sys)) is marked complete.") end - return nothing + nothing end """ @@ -1652,10 +1608,8 @@ Notes: - Returns a new `ReactionSystem` and does not modify `rs`. - By default, the new `ReactionSystem` will have the same name as `sys`. """ -function MT.extend( - sys::ReactionSystem, rs::ReactionSystem; - name::Symbol = nameof(sys) - ) +function MT.extend(sys::ReactionSystem, rs::ReactionSystem; + name::Symbol = nameof(sys)) complete_check(sys, "MT.extend") complete_check(rs, "MT.extend") @@ -1676,7 +1630,7 @@ function MT.extend( # ReactionSystem specific properties combinatoric_ratelaws = Catalyst.get_combinatoric_ratelaws(sys) | - Catalyst.get_combinatoric_ratelaws(rs) + Catalyst.get_combinatoric_ratelaws(rs) sivs = union(get_sivs(sys), get_sivs(rs)) # Union brownians, poissonians, and jumps from both systems @@ -1684,8 +1638,7 @@ function MT.extend( new_poissonians = union(MT.get_poissonians(rs), MT.get_poissonians(sys)) new_jumps = union(MT.get_jumps(rs), MT.get_jumps(sys)) - return ReactionSystem( - eqs, t, sts, ps, collect(new_brownians); + ReactionSystem(eqs, t, sts, ps, collect(new_brownians); poissonians = collect(new_poissonians), jumps = collect(new_jumps), observed = obs, @@ -1698,8 +1651,7 @@ function MT.extend( spatial_ivs = sivs, continuous_events, discrete_events, - tstops - ) + tstops) end ### Units Handling ### @@ -1760,12 +1712,8 @@ function unit_validation_report(rs::ReactionSystem; info::String = "") su = catalyst_get_unit(spec) if !_units_match(su, specunits) validated = false - push!( - issues, UnitValidationIssue( - :species_unit_mismatch, string(spec), - specunits, su, "Species unit mismatch" - ) - ) + push!(issues, UnitValidationIssue(:species_unit_mismatch, string(spec), + specunits, su, "Species unit mismatch")) end end timeunits = catalyst_get_unit(get_iv(rs)) @@ -1785,25 +1733,17 @@ function unit_validation_report(rs::ReactionSystem; info::String = "") # Symbolic stoichiometry cannot be used in dimensional analysis. if !rx.only_use_rate && any(s -> !(s isa Number), rx.substoich) validated = false - push!( - issues, UnitValidationIssue( - :symbolic_stoichiometry, - string(rx), nothing, nothing, - "Symbolic stoichiometry is not supported for unit validation" - ) - ) + push!(issues, UnitValidationIssue(:symbolic_stoichiometry, + string(rx), nothing, nothing, + "Symbolic stoichiometry is not supported for unit validation")) continue end # Symbolic exponents on unitful bases have indeterminate units. if _has_symbolic_unitful_pow(rx.rate) validated = false - push!( - issues, UnitValidationIssue( - :symbolic_exponent, - string(rx), nothing, nothing, - "Symbolic exponent on unitful base is not supported for unit validation" - ) - ) + push!(issues, UnitValidationIssue(:symbolic_exponent, + string(rx), nothing, nothing, + "Symbolic exponent on unitful base is not supported for unit validation")) continue end rxunits = catalyst_get_unit(rx.rate) @@ -1814,12 +1754,8 @@ function unit_validation_report(rs::ReactionSystem; info::String = "") end if !_units_match(rxunits, rateunits) validated = false - push!( - issues, UnitValidationIssue( - :reaction_rate_unit_mismatch, - string(rx), rateunits, rxunits, "Reaction rate unit mismatch" - ) - ) + push!(issues, UnitValidationIssue(:reaction_rate_unit_mismatch, + string(rx), rateunits, rxunits, "Reaction rate unit mismatch")) end end end @@ -1834,7 +1770,7 @@ function unit_validation_report(rs::ReactionSystem; info::String = "") validated &= _validate_equation(eq; noise_units, issues, warn = false) end - return UnitValidationReport(validated, issues) + UnitValidationReport(validated, issues) end """ diff --git a/src/reactionsystem_conversions.jl b/src/reactionsystem_conversions.jl index 0d97d7d8b7..cba5a24b5b 100644 --- a/src/reactionsystem_conversions.jl +++ b/src/reactionsystem_conversions.jl @@ -40,7 +40,7 @@ function oderatelaw(rx; combinatoric_ratelaw = true, expand_catalyst_funs = true end combinatoric_ratelaw && (!isequal(coef, one(coef))) && (rl /= coef) end - return rl + rl end # Function returning `true` for species which shouldn't change from the reactions, @@ -50,7 +50,7 @@ drop_dynamics(s) = isconstant(s) || isbc(s) || (!isspecies(s)) # Compute signed stoichiometry term: stoich * expr, optimized for common cases. # Used in both ODE RHS assembly and noise coefficient computation. function _signed_stoich_term(stoich, expr) - return if stoich isa SymbolicT + if stoich isa SymbolicT stoich * expr else signed_expr = (stoich > zero(stoich)) ? expr : -expr @@ -58,11 +58,9 @@ function _signed_stoich_term(stoich, expr) end end -function assemble_oderhs( - rs, ispcs; combinatoric_ratelaws = true, remove_conserved = false, +function assemble_oderhs(rs, ispcs; combinatoric_ratelaws = true, remove_conserved = false, physical_scales = nothing, expand_catalyst_funs = true, - use_jump_ratelaws = false - ) + use_jump_ratelaws = false) nps = get_networkproperties(rs) species_to_idx = Dict(x => i for (i, x) in enumerate(ispcs)) rhsvec = Any[0 for _ in ispcs] @@ -74,21 +72,15 @@ function assemble_oderhs( for (rxidx, rx) in enumerate(get_rxs(rs)) # check this reaction should contribute to drift (ODE and SDE reactions do) - !( - (physical_scales === nothing) || - (physical_scales[rxidx] in (PhysicalScale.ODE, PhysicalScale.SDE)) - ) && continue + !((physical_scales === nothing) || + (physical_scales[rxidx] in (PhysicalScale.ODE, PhysicalScale.SDE))) && continue rl = if use_jump_ratelaws - jumpratelaw( - rx; combinatoric_ratelaw = combinatoric_ratelaws, - expand_catalyst_funs - ) + jumpratelaw(rx; combinatoric_ratelaw = combinatoric_ratelaws, + expand_catalyst_funs) else - oderatelaw( - rx; combinatoric_ratelaw = combinatoric_ratelaws, - expand_catalyst_funs - ) + oderatelaw(rx; combinatoric_ratelaw = combinatoric_ratelaws, + expand_catalyst_funs) end remove_conserved && (rl = substitute(rl, depspec_submap)) for (spec, stoich) in rx.netstoich @@ -112,34 +104,28 @@ function assemble_oderhs( else Δspec = isone(abs(stoich)) ? rl : abs(stoich) * rl rhsvec[i] = (stoich > zero(stoich)) ? (rhsvec[i] + Δspec) : - (rhsvec[i] - Δspec) + (rhsvec[i] - Δspec) end end end end - return rhsvec + rhsvec end -function assemble_drift( - rs, ispcs; combinatoric_ratelaws = true, as_odes = true, +function assemble_drift(rs, ispcs; combinatoric_ratelaws = true, as_odes = true, include_zero_odes = true, remove_conserved = false, physical_scales = nothing, - expand_catalyst_funs = true, use_jump_ratelaws = false - ) - rhsvec = assemble_oderhs( - rs, ispcs; combinatoric_ratelaws, remove_conserved, - physical_scales, expand_catalyst_funs, use_jump_ratelaws - ) + expand_catalyst_funs = true, use_jump_ratelaws = false) + rhsvec = assemble_oderhs(rs, ispcs; combinatoric_ratelaws, remove_conserved, + physical_scales, expand_catalyst_funs, use_jump_ratelaws) if as_odes D = Differential(get_iv(rs)) - eqs = [ - Equation(D(x), rhs) - for (x, rhs) in zip(ispcs, rhsvec) if (include_zero_odes || (!_iszero(rhs))) - ] + eqs = [Equation(D(x), rhs) + for (x, rhs) in zip(ispcs, rhsvec) if (include_zero_odes || (!_iszero(rhs)))] else eqs = [Equation(0, rhs) for rhs in rhsvec if (include_zero_odes || (!_iszero(rhs)))] end - return eqs + eqs end """ @@ -151,11 +137,9 @@ The noise coefficient is `stoich * sqrt(|ratelaw|) * [noise_scaling]`. This is a shared helper used by both `assemble_diffusion` (legacy noise matrix path) and `add_noise_to_rhs!` (Brownian-based path) to avoid code duplication. """ -function foreach_noise_coeff( - f, rx, species_to_idx, nps, depspec_submap; +function foreach_noise_coeff(f, rx, species_to_idx, nps, depspec_submap; combinatoric_ratelaws = true, remove_conserved = false, - expand_catalyst_funs = true, use_jump_ratelaws = false - ) + expand_catalyst_funs = true, use_jump_ratelaws = false) rl = if use_jump_ratelaws jumpratelaw(rx; combinatoric_ratelaw = combinatoric_ratelaws, expand_catalyst_funs) else @@ -169,24 +153,19 @@ function foreach_noise_coeff( remove_conserved && (spec in nps.depspecs) && continue drop_dynamics(spec) && continue if !haskey(species_to_idx, spec) - error( - "Species $spec appears in reaction $rx but is not in the independent species list. " * - "This indicates a problem with the reaction system structure." - ) + error("Species $spec appears in reaction $rx but is not in the independent species list. " * + "This indicates a problem with the reaction system structure.") end i = species_to_idx[spec] coef = _signed_stoich_term(stoich, rlsqrt) f(i, coef) end - return end # this doesn't work with constraint equations currently -function assemble_diffusion( - rs, sts, ispcs; combinatoric_ratelaws = true, +function assemble_diffusion(rs, sts, ispcs; combinatoric_ratelaws = true, remove_conserved = false, expand_catalyst_funs = true, - use_jump_ratelaws = false - ) + use_jump_ratelaws = false) # as BC species should ultimately get an equation, we include them in the noise matrix num_bcsts = count(isbc, get_unknowns(rs)) @@ -202,15 +181,13 @@ function assemble_diffusion( end for (j, rx) in enumerate(get_rxs(rs)) - foreach_noise_coeff( - rx, species_to_idx, nps, depspec_submap; - combinatoric_ratelaws, remove_conserved, expand_catalyst_funs, - use_jump_ratelaws - ) do i, coef + foreach_noise_coeff(rx, species_to_idx, nps, depspec_submap; + combinatoric_ratelaws, remove_conserved, expand_catalyst_funs, + use_jump_ratelaws) do i, coef eqs[i, j] = coef end end - return eqs + eqs end ### Brownian Noise Helpers ### @@ -233,7 +210,7 @@ function create_sde_brownians(scales) push!(brownian_vars, B) push!(brownian_map, rx_idx => B) end - return brownian_vars, brownian_map + brownian_vars, brownian_map end """ @@ -243,11 +220,9 @@ Mutate the RHS vector `rhsvec` to add Brownian noise terms for each SDE-scale reaction. For each such reaction, adds `stoich * sqrt(|ratelaw|) * [noise_scaling] * B_j` to the corresponding species' RHS entry. """ -function add_noise_to_rhs!( - rhsvec, rs, ispcs, brownian_map; +function add_noise_to_rhs!(rhsvec, rs, ispcs, brownian_map; combinatoric_ratelaws = true, remove_conserved = false, - expand_catalyst_funs = true, use_jump_ratelaws = false - ) + expand_catalyst_funs = true, use_jump_ratelaws = false) nps = get_networkproperties(rs) species_to_idx = Dict(x => i for (i, x) in enumerate(ispcs)) depspec_submap = if remove_conserved @@ -258,15 +233,12 @@ function add_noise_to_rhs!( for (rx_idx, B_j) in brownian_map rx = get_rxs(rs)[rx_idx] - foreach_noise_coeff( - rx, species_to_idx, nps, depspec_submap; - combinatoric_ratelaws, remove_conserved, expand_catalyst_funs, - use_jump_ratelaws - ) do i, coef + foreach_noise_coeff(rx, species_to_idx, nps, depspec_submap; + combinatoric_ratelaws, remove_conserved, expand_catalyst_funs, + use_jump_ratelaws) do i, coef rhsvec[i] += coef * B_j end end - return end ### Jumps Assembly ### @@ -307,7 +279,7 @@ function jumpratelaw(rx; combinatoric_ratelaw = true, expand_catalyst_funs = tru s = substrates[i] if stoich isa SymbolicT rl *= combinatoric_ratelaw ? binomial(s, stoich) : - factorial(s) / factorial(s - stoich) + factorial(s) / factorial(s - stoich) else rl *= s isone(stoich) && continue @@ -319,7 +291,7 @@ function jumpratelaw(rx; combinatoric_ratelaw = true, expand_catalyst_funs = tru end combinatoric_ratelaw && !isequal(coef, one(coef)) && (rl /= coef) end - return rl + rl end # if haveivdep=false then time dependent rates will still be classified as mass action @@ -355,11 +327,9 @@ Notes: symbolic variables/terms or floating point numbers for stoichiometric coefficients. """ -function ismassaction( - rx, rs; rxvars = get_variables(rx.rate), +function ismassaction(rx, rs; rxvars = get_variables(rx.rate), haveivdep::Union{Nothing, Bool} = nothing, - unknownset = Set(get_unknowns(rs)), ivset = nothing - ) + unknownset = Set(get_unknowns(rs)), ivset = nothing) # we define non-integer (i.e. float or symbolic) stoich to be non-mass action ((eltype(rx.substoich) <: Integer) && (eltype(rx.prodstoich) <: Integer)) || @@ -419,10 +389,8 @@ end isempty(net_stoch) && error("$rx has no net stoichiometry change once accounting for constant and boundary condition species. This is not supported.") - return MassActionJump( - Num(rate), reactant_stoch, net_stoch, scale_rates = false, - useiszero = false - ) + MassActionJump(Num(rate), reactant_stoch, net_stoch, scale_rates = false, + useiszero = false) end # recursively visit each neighbor's rooted tree and mark everything in it as vrj @@ -435,7 +403,7 @@ function dfs_mark!(isvrjvec, visited, depgraph, i) dfs_mark!(isvrjvec, visited, depgraph, nhbr) end end - return nothing + nothing end # get_depgraph(rs)[i] is the list of reactions with rates depending on species changed by @@ -444,7 +412,7 @@ function get_depgraph(rs) eqs = reactions(rs) jdeps = asgraph(rs; eqs) vdeps = variable_dependencies(rs; eqs) - return eqeq_dependencies(jdeps, vdeps).fadjlist + eqeq_dependencies(jdeps, vdeps).fadjlist end # note that reactions that are not constant rate are treated as vrjs here @@ -484,13 +452,11 @@ function classify_vrjs(rs, physcales) end end - return isvrjvec + isvrjvec end -function assemble_jumps( - rs; combinatoric_ratelaws = true, physical_scales = nothing, - expand_catalyst_funs = true, save_positions = (true, true) - ) +function assemble_jumps(rs; combinatoric_ratelaws = true, physical_scales = nothing, + expand_catalyst_funs = true, save_positions = (true, true)) meqs = MassActionJump[] ceqs = ConstantRateJump[] veqs = VariableRateJump[] @@ -522,10 +488,8 @@ function assemble_jumps( if (!isvrj) && ismassaction(rx, rs; rxvars, haveivdep = false, unknownset) push!(meqs, makemajump(rx; combinatoric_ratelaw = combinatoric_ratelaws)) else - rl = jumpratelaw( - rx; combinatoric_ratelaw = combinatoric_ratelaws, - expand_catalyst_funs - ) + rl = jumpratelaw(rx; combinatoric_ratelaw = combinatoric_ratelaws, + expand_catalyst_funs) affect = Vector{Equation}() for (spec, stoich) in rx.netstoich # don't change species that are constant or BCs @@ -538,17 +502,15 @@ function assemble_jumps( end end end - return reduce(vcat, (meqs, ceqs, veqs); init = JumpType[]) + reduce(vcat, (meqs, ceqs, veqs); init = JumpType[]) end ### Equation Coupling ### # merge constraint components with the ReactionSystem components # also handles removing BC and constant species -function addconstraints!( - eqs, rs::ReactionSystem, ists, ispcs; remove_conserved = false, - compute_cl_initeqs = false, include_cl_as_eqs = false - ) +function addconstraints!(eqs, rs::ReactionSystem, ists, ispcs; remove_conserved = false, + compute_cl_initeqs = false, include_cl_as_eqs = false) # if there are BC species, put them after the independent species rssts = get_unknowns(rs) sts = any(isbc, rssts) ? vcat(ists, filter(isbc, rssts)) : ists @@ -586,24 +548,24 @@ function addconstraints!( if !isempty(ceqs) if remove_conserved @info """ - Be careful mixing ODEs or algebraic equations and elimination of - conservation laws. Catalyst does not check that the conserved equations - still hold for the final coupled system of equations. Consider using - `remove_conserved = false` and instead calling - ModelingToolkitBase.mtkcompile to simplify any generated system. - """ + Be careful mixing ODEs or algebraic equations and elimination of + conservation laws. Catalyst does not check that the conserved equations + still hold for the final coupled system of equations. Consider using + `remove_conserved = false` and instead calling + ModelingToolkitBase.mtkcompile to simplify any generated system. + """ end append!(eqs, ceqs) end - return eqs, sts, ps, obs, ics, initeqs + eqs, sts, ps, obs, ics, initeqs end ### Utility ### # Throws an error when attempting to convert a spatial system to an unsupported type. function spatial_convert_err(rs::ReactionSystem, systype) - return isspatial(rs) && error("Conversion to $systype is not supported for spatial networks.") + isspatial(rs) && error("Conversion to $systype is not supported for spatial networks.") end # Finds and differentials in an expression, and sets these to 0. @@ -656,8 +618,7 @@ reaction's assigned [`PhysicalScale`](@ref). 3. `default_scale` kwarg (fallback for `Auto`) 4. If still `Auto` after all three → error """ -function hybrid_model( - rs::ReactionSystem; +function hybrid_model(rs::ReactionSystem; name = nameof(rs), physical_scales = nothing, default_scale = PhysicalScale.Auto, @@ -670,8 +631,7 @@ function hybrid_model( checks = false, initial_conditions = Dict(), use_jump_ratelaws = false, - kwargs... - ) + kwargs...) # Error checks. iscomplete(rs) || error(COMPLETENESS_ERROR) @@ -722,19 +682,15 @@ function hybrid_model( brownian_vars = SymbolicT[] if has_continuous - rhsvec = assemble_oderhs( - flatrs, ispcs; combinatoric_ratelaws, remove_conserved, - physical_scales = scales, expand_catalyst_funs, use_jump_ratelaws - ) + rhsvec = assemble_oderhs(flatrs, ispcs; combinatoric_ratelaws, remove_conserved, + physical_scales = scales, expand_catalyst_funs, use_jump_ratelaws) # Add Brownian noise terms for SDE-scale reactions. if has_rxn_sde rxn_brownian_vars, brownian_map = create_sde_brownians(scales) - add_noise_to_rhs!( - rhsvec, flatrs, ispcs, brownian_map; + add_noise_to_rhs!(rhsvec, flatrs, ispcs, brownian_map; combinatoric_ratelaws, remove_conserved, expand_catalyst_funs, - use_jump_ratelaws - ) + use_jump_ratelaws) # Merge reaction-generated brownians with user-provided brownians. brownian_vars = unique(vcat(rxn_brownian_vars, user_brownians)) else @@ -744,20 +700,16 @@ function hybrid_model( # Convert RHS vector to D(x) ~ rhs equations. D = Differential(get_iv(flatrs)) - eqs = [ - Equation(D(x), rhs) - for (x, rhs) in zip(ispcs, rhsvec) - if (include_zero_odes || (!_iszero(rhs))) - ] + eqs = [Equation(D(x), rhs) + for (x, rhs) in zip(ispcs, rhsvec) + if (include_zero_odes || (!_iszero(rhs)))] end # --- Build jumps (Jump + VariableRateJump reactions only) --- rxn_jumps = JumpType[] if has_rxn_jump - rxn_jumps = assemble_jumps( - flatrs; combinatoric_ratelaws, expand_catalyst_funs, - physical_scales = scales, save_positions - ) + rxn_jumps = assemble_jumps(flatrs; combinatoric_ratelaws, expand_catalyst_funs, + physical_scales = scales, save_positions) end # Merge reaction-generated jumps with user-provided jumps. jumps = vcat(rxn_jumps, user_jumps) @@ -777,8 +729,7 @@ function hybrid_model( # --- Construct unified System --- # Note: brownians is a positional arg (5th) in the System constructor. - return MT.System( - eqs, get_iv(flatrs), us, ps, brownian_vars; + MT.System(eqs, get_iv(flatrs), us, ps, brownian_vars; poissonians = user_poissonians, jumps, observed = obs, @@ -790,8 +741,7 @@ function hybrid_model( discrete_events = MT.get_discrete_events(flatrs), tstops = MT.get_tstops(flatrs), metadata = MT.get_metadata(rs), - kwargs... - ) + kwargs...) end """ @@ -817,45 +767,35 @@ Keyword args and default values: (binomials) instead of the ODE rate law (powers). This gives the mathematically correct propensities from the CME when species populations are integers. """ -function ode_model( - rs::ReactionSystem; name = nameof(rs), +function ode_model(rs::ReactionSystem; name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), include_zero_odes = true, remove_conserved = false, checks = false, initial_conditions = Dict(), expand_catalyst_funs = true, use_jump_ratelaws = false, - kwargs... - ) + kwargs...) # Error if ReactionSystem has coupled brownians or jumps. flatrs = Catalyst.flatten(rs) if !isempty(MT.brownians(flatrs)) - error( - """Cannot convert ReactionSystem with coupled brownian noise to a pure ODE system. - Found brownians: $(MT.brownians(flatrs)) - Use `SDEProblem` or `HybridProblem` instead.""" - ) + error("""Cannot convert ReactionSystem with coupled brownian noise to a pure ODE system. + Found brownians: $(MT.brownians(flatrs)) + Use `SDEProblem` or `HybridProblem` instead.""") end if !isempty(MT.jumps(flatrs)) - error( - """Cannot convert ReactionSystem with coupled jumps to a pure ODE system. - Found $(length(MT.jumps(flatrs))) jump(s). - Use `JumpProblem` or `HybridProblem` instead.""" - ) + error("""Cannot convert ReactionSystem with coupled jumps to a pure ODE system. + Found $(length(MT.jumps(flatrs))) jump(s). + Use `JumpProblem` or `HybridProblem` instead.""") end if !isempty(MT.poissonians(flatrs)) - error( - """Cannot convert ReactionSystem with poissonians to a pure ODE system. - Found poissonians: $(MT.poissonians(flatrs)) - Use `JumpProblem` or `HybridProblem` instead.""" - ) + error("""Cannot convert ReactionSystem with poissonians to a pure ODE system. + Found poissonians: $(MT.poissonians(flatrs)) + Use `JumpProblem` or `HybridProblem` instead.""") end - return hybrid_model( - rs; + hybrid_model(rs; _override_all_scales = PhysicalScale.ODE, name, combinatoric_ratelaws, include_zero_odes, remove_conserved, checks, initial_conditions, - expand_catalyst_funs, use_jump_ratelaws, kwargs... - ) + expand_catalyst_funs, use_jump_ratelaws, kwargs...) end function is_autonomous_error(iv) @@ -869,7 +809,7 @@ end # Returns `nothing` for non-species or already-BC nodes (no replacement by `replacenode`). function _mark_species_bc(node) ((node isa SymbolicT) && isspecies(node) && !isbc(node)) || return nothing - return MT.setmetadata(node, VariableBCSpecies, true) + MT.setmetadata(node, VariableBCSpecies, true) end """ @@ -885,13 +825,11 @@ Keyword arguments override the defaults extracted from `sys`: - `checks`: defaults to `false` - `disable_forbidden_symbol_check`: defaults to `false` """ -function system_to_reactionsystem( - sys::MT.AbstractSystem; +function system_to_reactionsystem(sys::MT.AbstractSystem; name = nameof(sys), combinatoric_ratelaws = true, checks = false, - disable_forbidden_symbol_check = false - ) + disable_forbidden_symbol_check = false) eqs = equations(sys) iv = get_iv(sys) us = unknowns(sys) @@ -911,17 +849,12 @@ function system_to_reactionsystem( # The ReactionSystem constructor rejects differentials of non-BC species, but ODE/SDE # equations naturally contain D(species). BC metadata is added to both the unknowns # vector and inside equation expressions (which hold separate symbolic references). - us = [ - isspecies(u) && !isbc(u) ? MT.setmetadata(u, VariableBCSpecies, true) : u - for u in us - ] - eqs = [ - replacenode(eq.lhs, _mark_species_bc) ~ replacenode(eq.rhs, _mark_species_bc) - for eq in eqs - ] - - return ReactionSystem( - eqs, iv, us, ps, browns; + us = [isspecies(u) && !isbc(u) ? MT.setmetadata(u, VariableBCSpecies, true) : u + for u in us] + eqs = [replacenode(eq.lhs, _mark_species_bc) ~ replacenode(eq.rhs, _mark_species_bc) + for eq in eqs] + + ReactionSystem(eqs, iv, us, ps, browns; poissonians = poiss, jumps = jmps, observed = obs, @@ -934,8 +867,7 @@ function system_to_reactionsystem( tstops = tstps, metadata = meta, checks, - disable_forbidden_symbol_check - ) + disable_forbidden_symbol_check) end """ @@ -962,13 +894,11 @@ Keyword args and default values: explicitly include the conservation laws as system equations, rather than eliminating them as observables. Primarily used by internal Catalyst functions. """ -function ss_ode_model( - rs::ReactionSystem; name = nameof(rs), +function ss_ode_model(rs::ReactionSystem; name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), remove_conserved = false, checks = false, initial_conditions = Dict(), all_differentials_permitted = false, expand_catalyst_funs = true, - include_cl_as_eqs = false, kwargs... - ) + include_cl_as_eqs = false, kwargs...) # Error checks. iscomplete(rs) || error(COMPLETENESS_ERROR) spatial_convert_err(rs::ReactionSystem, System) @@ -978,14 +908,10 @@ function ss_ode_model( fullrs = Catalyst.flatten(rs) remove_conserved && conservationlaws(fullrs) ists, ispcs = get_indep_sts(fullrs, (remove_conserved && !include_cl_as_eqs)) - eqs = assemble_drift( - fullrs, ispcs; combinatoric_ratelaws, remove_conserved, - as_odes = false, include_zero_odes = false, expand_catalyst_funs - ) - eqs, us, ps, obs, ics, initeqs = addconstraints!( - eqs, fullrs, ists, ispcs; - remove_conserved, compute_cl_initeqs = !include_cl_as_eqs, include_cl_as_eqs - ) + eqs = assemble_drift(fullrs, ispcs; combinatoric_ratelaws, remove_conserved, + as_odes = false, include_zero_odes = false, expand_catalyst_funs) + eqs, us, ps, obs, ics, initeqs = addconstraints!(eqs, fullrs, ists, ispcs; + remove_conserved, compute_cl_initeqs = !include_cl_as_eqs, include_cl_as_eqs) # Comoutes the correct initial conditions and bindings. initial_conditions, bindings = MT.convert_bindings_for_time_independent_system(rs) @@ -995,16 +921,14 @@ function ss_ode_model( # Next, sets all differential terms to `0`. all_differentials_permitted || nonlinear_convert_differentials_check(rs) eqs = Equation[remove_diffs(eq.lhs) ~ remove_diffs(eq.rhs) for eq in eqs] - return System( - eqs, us, ps; + System(eqs, us, ps; name, observed = obs, initialization_eqs = initeqs, bindings, initial_conditions, checks, metadata = MT.get_metadata(rs), - kwargs... - ) + kwargs...) end # Ideally, when `ReactionSystem`s are converted to nonlinear systems, any coupled ODEs should be @@ -1020,12 +944,11 @@ function nonlinear_convert_differentials_check(rs::ReactionSystem) # If the content of the differential is not a variable (and nothing more). # If either of this is a case, throws the warning. if hasnode(Symbolics.is_derivative, eq.rhs) || - !Symbolics.is_derivative(eq.lhs) || - !isequal(Symbolics.operation(eq.lhs), Differential(get_iv(rs))) || - (length(arguments(eq.lhs)) != 1) || - !any(isequal(arguments(eq.lhs)[1]), nonspecies(rs)) - error( - "You are attempting to convert a `ReactionSystem` coupled with differential equations to a nonlinear system. However, some of these differentials are not of the form `D(x) ~ ...` where: + !Symbolics.is_derivative(eq.lhs) || + !isequal(Symbolics.operation(eq.lhs), Differential(get_iv(rs))) || + (length(arguments(eq.lhs)) != 1) || + !any(isequal(arguments(eq.lhs)[1]), nonspecies(rs)) + error("You are attempting to convert a `ReactionSystem` coupled with differential equations to a nonlinear system. However, some of these differentials are not of the form `D(x) ~ ...` where: (1) The left-hand side is a differential of a single variable with respect to the time independent variable, and (2) The right-hand side does not contain any differentials. This is generally not permitted. @@ -1035,7 +958,6 @@ function nonlinear_convert_differentials_check(rs::ReactionSystem) ) end end - return end """ @@ -1064,33 +986,27 @@ Keyword args and default values: jump/stochastic rate law (binomials) instead of the ODE rate law (powers). This gives the mathematically correct CLE derived from the CME when species populations are integers. """ -function sde_model( - rs::ReactionSystem; +function sde_model(rs::ReactionSystem; name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), include_zero_odes = true, checks = false, remove_conserved = false, initial_conditions = Dict(), expand_catalyst_funs = true, use_legacy_noise = true, use_jump_ratelaws = false, - kwargs... - ) + kwargs...) # Flatten once upfront and check for constraints. flatrs = Catalyst.flatten(rs) # Error if ReactionSystem has coupled jumps or poissonians (SDE + jumps hybrid not supported yet). if !isempty(MT.jumps(flatrs)) - error( - """Cannot convert ReactionSystem with coupled jumps to a pure SDE system. - Found $(length(MT.jumps(flatrs))) jump(s). - Use `HybridProblem` instead. Note: SDE+Jump hybrids require special handling.""" - ) + error("""Cannot convert ReactionSystem with coupled jumps to a pure SDE system. + Found $(length(MT.jumps(flatrs))) jump(s). + Use `HybridProblem` instead. Note: SDE+Jump hybrids require special handling.""") end if !isempty(MT.poissonians(flatrs)) - error( - """Cannot convert ReactionSystem with poissonians to a pure SDE system. - Found $(length(MT.poissonians(flatrs))) poissonian(s). - Use `HybridProblem` instead.""" - ) + error("""Cannot convert ReactionSystem with poissonians to a pure SDE system. + Found $(length(MT.poissonians(flatrs))) poissonian(s). + Use `HybridProblem` instead.""") end has_constraints = has_alg_equations(flatrs) || any(isbc, get_unknowns(flatrs)) @@ -1106,18 +1022,13 @@ function sde_model( remove_conserved && conservationlaws(flatrs) ists, ispcs = get_indep_sts(flatrs, remove_conserved) - eqs = assemble_drift( - flatrs, ispcs; combinatoric_ratelaws, include_zero_odes, - remove_conserved, expand_catalyst_funs, use_jump_ratelaws - ) - noiseeqs = assemble_diffusion( - flatrs, ists, ispcs; combinatoric_ratelaws, - remove_conserved, expand_catalyst_funs, use_jump_ratelaws - ) + eqs = assemble_drift(flatrs, ispcs; combinatoric_ratelaws, include_zero_odes, + remove_conserved, expand_catalyst_funs, use_jump_ratelaws) + noiseeqs = assemble_diffusion(flatrs, ists, ispcs; combinatoric_ratelaws, + remove_conserved, expand_catalyst_funs, use_jump_ratelaws) eqs, us, ps, obs, ics = addconstraints!(eqs, flatrs, ists, ispcs; remove_conserved) - return MT.System( - eqs, get_iv(flatrs), us, ps; + return MT.System(eqs, get_iv(flatrs), us, ps; noise_eqs = noiseeqs, observed = obs, name, @@ -1127,17 +1038,14 @@ function sde_model( discrete_events = MT.get_discrete_events(flatrs), tstops = MT.get_tstops(flatrs), metadata = MT.get_metadata(rs), - kwargs... - ) + kwargs...) else # New path: Brownians via hybrid_model (requires mtkcompile for SDEProblem). - return hybrid_model( - flatrs; + return hybrid_model(flatrs; _override_all_scales = PhysicalScale.SDE, name, combinatoric_ratelaws, include_zero_odes, remove_conserved, checks, initial_conditions, - expand_catalyst_funs, use_jump_ratelaws, kwargs... - ) + expand_catalyst_funs, use_jump_ratelaws, kwargs...) end end @@ -1169,7 +1077,7 @@ function merge_physical_scales(rxs, physical_scales, default) end end - return scales + scales end # Overload for when physical_scales is already a fully-resolved vector of scales. @@ -1180,7 +1088,7 @@ function merge_physical_scales(rxs, physical_scales::AbstractVector{<:PhysicalSc for (idx, s) in enumerate(scales) s == PhysicalScale.Auto && (scales[idx] = default) end - return scales + scales end # Returns (has_ode, has_sde, has_jump) for the resolved scales. @@ -1188,7 +1096,7 @@ function detect_scale_types(scales) has_ode = any(==(PhysicalScale.ODE), scales) has_sde = any(==(PhysicalScale.SDE), scales) has_jump = any(s -> s in (PhysicalScale.Jump, PhysicalScale.VariableRateJump), scales) - return (has_ode, has_sde, has_jump) + (has_ode, has_sde, has_jump) end """ @@ -1215,13 +1123,11 @@ Keyword args and default values: `VariableRateJump` to save the solution before and/or after the jump occurs. Defaults to true for both. """ -function jump_model( - rs::ReactionSystem; name = nameof(rs), +function jump_model(rs::ReactionSystem; name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), remove_conserved = nothing, checks = false, initial_conditions = Dict(), expand_catalyst_funs = true, save_positions = (true, true), - physical_scales = nothing, kwargs... - ) + physical_scales = nothing, kwargs...) (remove_conserved !== nothing) && throw(ArgumentError("Catalyst does not support removing conserved species when converting to jump Systems.")) @@ -1231,21 +1137,17 @@ function jump_model( # Error if ReactionSystem has unprocessed poissonians (jump_model cannot handle them). if !isempty(MT.poissonians(flatrs)) - error( - """Cannot convert ReactionSystem with unprocessed poissonians to a pure Jump system via jump_model. - Use `HybridProblem` instead, which will process poissonians via mtkcompile.""" - ) + error("""Cannot convert ReactionSystem with unprocessed poissonians to a pure Jump system via jump_model. + Use `HybridProblem` instead, which will process poissonians via mtkcompile.""") end # Error on non-reaction ODE/algebraic/SDE equations (pure Jump only supports reactions). # This also catches brownians since they appear in SDE equations. non_rxn_eqs = filter(eq -> !(eq isa Reaction), equations(flatrs)) if !isempty(non_rxn_eqs) - error( - """Cannot convert ReactionSystem with ODE, SDE, or algebraic equations to a pure Jump system. - Found $(length(non_rxn_eqs)) non-reaction equation(s). - Use `HybridProblem` instead for mixed ODE+Jump or SDE+Jump systems.""" - ) + error("""Cannot convert ReactionSystem with ODE, SDE, or algebraic equations to a pure Jump system. + Found $(length(non_rxn_eqs)) non-reaction equation(s). + Use `HybridProblem` instead for mixed ODE+Jump or SDE+Jump systems.""") end jump_scales = map(reactions(flatrs)) do rx @@ -1260,31 +1162,25 @@ function jump_model( end end - return hybrid_model( - flatrs; + hybrid_model(flatrs; physical_scales = jump_scales, default_scale = PhysicalScale.Jump, name, combinatoric_ratelaws, checks, initial_conditions, - expand_catalyst_funs, save_positions, kwargs... - ) + expand_catalyst_funs, save_positions, kwargs...) end ### Problems ### # ODEProblem from AbstractReactionNetwork -function DiffEqBase.ODEProblem( - rs::ReactionSystem, u0, tspan, +function DiffEqBase.ODEProblem(rs::ReactionSystem, u0, tspan, p = DiffEqBase.NullParameters(), args...; check_length = false, name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), include_zero_odes = true, remove_conserved = false, checks = false, expand_catalyst_funs = true, mtkcompile = false, - use_jump_ratelaws = false, kwargs... - ) - osys = ode_model( - rs; name, combinatoric_ratelaws, include_zero_odes, checks, - remove_conserved, expand_catalyst_funs, use_jump_ratelaws - ) + use_jump_ratelaws = false, kwargs...) + osys = ode_model(rs; name, combinatoric_ratelaws, include_zero_odes, checks, + remove_conserved, expand_catalyst_funs, use_jump_ratelaws) # Handles potential differential algebraic equations (which requires `mtkcompile`). if mtkcompile @@ -1295,7 +1191,7 @@ function DiffEqBase.ODEProblem( osys = complete(osys) end - prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any, Any}(u0), Dict{Any, Any}(p)) + prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any,Any}(u0), Dict{Any,Any}(p)) return ODEProblem(osys, prob_cond, tspan, args...; check_length, kwargs...) end @@ -1328,42 +1224,32 @@ Keyword args and default values: with their rational function representation when converting to another system type. Set to `false`` to disable. """ -function DiffEqBase.NonlinearProblem( - rs::ReactionSystem, u0, +function DiffEqBase.NonlinearProblem(rs::ReactionSystem, u0, p = DiffEqBase.NullParameters(), args...; name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), - remove_conserved = false, checks = false, check_length = false, expand_catalyst_funs = true, - mtkcompile = false, all_differentials_permitted = false, kwargs... - ) - nlsys = ss_ode_model( - rs; name, combinatoric_ratelaws, checks, all_differentials_permitted, - remove_conserved, expand_catalyst_funs - ) + remove_conserved = false, checks = false, check_length = false, expand_catalyst_funs = true, + mtkcompile = false, all_differentials_permitted = false, kwargs...) + nlsys = ss_ode_model(rs; name, combinatoric_ratelaws, checks, all_differentials_permitted, + remove_conserved, expand_catalyst_funs) nlsys = mtkcompile ? MT.mtkcompile(nlsys) : complete(nlsys) - prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any, Any}(u0), Dict{Any, Any}(p)) - return NonlinearProblem( - nlsys, prob_cond, args...; check_length, - kwargs... - ) + prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any,Any}(u0), Dict{Any,Any}(p)) + return NonlinearProblem(nlsys, prob_cond, args...; check_length, + kwargs...) end # SDEProblem from AbstractReactionNetwork -function DiffEqBase.SDEProblem( - rs::ReactionSystem, u0, tspan, +function DiffEqBase.SDEProblem(rs::ReactionSystem, u0, tspan, p = DiffEqBase.NullParameters(), args...; name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), include_zero_odes = true, checks = false, check_length = false, remove_conserved = false, mtkcompile = false, expand_catalyst_funs = true, use_legacy_noise = true, - use_jump_ratelaws = false, kwargs... - ) + use_jump_ratelaws = false, kwargs...) # Flatten once upfront and pass to sde_model. flatrs = Catalyst.flatten(rs) - sde_sys = sde_model( - flatrs; name, combinatoric_ratelaws, expand_catalyst_funs, - include_zero_odes, checks, remove_conserved, use_legacy_noise, use_jump_ratelaws - ) + sde_sys = sde_model(flatrs; name, combinatoric_ratelaws, expand_catalyst_funs, + include_zero_odes, checks, remove_conserved, use_legacy_noise, use_jump_ratelaws) # Determine if we need mtkcompile: # - If user brownians are present, sde_model routes through hybrid_model which requires mtkcompile @@ -1373,12 +1259,12 @@ function DiffEqBase.SDEProblem( has_constraints = has_alg_equations(flatrs) || any(isbc, get_unknowns(flatrs)) has_user_brownians = !isempty(MT.brownians(flatrs)) needs_mtkcompile = mtkcompile || - has_alg_equations(flatrs) || - !use_legacy_noise || - has_user_brownians || - has_constraints + has_alg_equations(flatrs) || + !use_legacy_noise || + has_user_brownians || + has_constraints - prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any, Any}(u0), Dict{Any, Any}(p)) + prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any,Any}(u0), Dict{Any,Any}(p)) if needs_mtkcompile if !mtkcompile && has_alg_equations(flatrs) @@ -1390,10 +1276,8 @@ function DiffEqBase.SDEProblem( # Legacy path: complete + noise_rate_prototype sde_sys = complete(sde_sys) p_matrix = zeros(length(get_unknowns(sde_sys)), numreactions(flatrs)) - return SDEProblem( - sde_sys, prob_cond, tspan, args...; check_length, - noise_rate_prototype = p_matrix, kwargs... - ) + return SDEProblem(sde_sys, prob_cond, tspan, args...; check_length, + noise_rate_prototype = p_matrix, kwargs...) end end @@ -1430,25 +1314,19 @@ end # JumpProblem from ReactionSystem # Note: For hybrid ODE+Jump systems, use HybridProblem instead. -function JumpProcesses.JumpProblem( - rs::ReactionSystem, u0, tspan, +function JumpProcesses.JumpProblem(rs::ReactionSystem, u0, tspan, p = DiffEqBase.NullParameters(); name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), expand_catalyst_funs = true, save_positions = (true, true), checks = false, - kwargs... - ) + kwargs...) # Pure jump system - use HybridProblem for hybrid ODE+SDE+Jump systems. - jsys = complete( - jump_model( - rs; name, combinatoric_ratelaws, checks, - expand_catalyst_funs, save_positions - ) - ) + jsys = complete(jump_model(rs; name, combinatoric_ratelaws, checks, + expand_catalyst_funs, save_positions)) # Use Dict{Any,Any} to prevent type promotion during merge (MTK converts to this anyway). - op = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any, Any}(u0), Dict{Any, Any}(p)) + op = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any,Any}(u0), Dict{Any,Any}(p)) # Auto-detect integer u0 and set u0_eltype if not already provided. if !haskey(kwargs, :u0_eltype) @@ -1527,8 +1405,7 @@ prob_sde_jump = HybridProblem(rn_sde_jump, [:S => 100.0, :P => 0.0], (0.0, 10.0) sol = solve(prob_sde_jump, SRIW1()) ``` """ -function HybridProblem( - rs::ReactionSystem, u0, tspan, +function HybridProblem(rs::ReactionSystem, u0, tspan, p = DiffEqBase.NullParameters(); name = nameof(rs), physical_scales = nothing, @@ -1539,8 +1416,7 @@ function HybridProblem( checks = false, mtkcompile = false, use_jump_ratelaws = false, - kwargs... - ) + kwargs...) # Determine which scale types are present. flatrs = Catalyst.flatten(rs) resolved_scales = merge_physical_scales(reactions(flatrs), physical_scales, default_scale) @@ -1556,15 +1432,13 @@ function HybridProblem( has_jump = has_jump || user_has_jump || user_has_poissonian # Build the unified System from the flattened ReactionSystem. - sys = hybrid_model( - flatrs; name, physical_scales, default_scale, + sys = hybrid_model(flatrs; name, physical_scales, default_scale, combinatoric_ratelaws, expand_catalyst_funs, save_positions, checks, - use_jump_ratelaws - ) + use_jump_ratelaws) # Build problem conditions (u0 + p merged). # Use Dict{Any,Any} to prevent type promotion during merge (MTK converts to this anyway). - prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any, Any}(u0), Dict{Any, Any}(p)) + prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any,Any}(u0), Dict{Any,Any}(p)) if has_jump # Any jumps present → JumpProblem (wrapping ODEProblem or SDEProblem as needed) @@ -1603,18 +1477,14 @@ function HybridProblem( end # SteadyStateProblem from AbstractReactionNetwork -function DiffEqBase.SteadyStateProblem( - rs::ReactionSystem, u0, +function DiffEqBase.SteadyStateProblem(rs::ReactionSystem, u0, p = DiffEqBase.NullParameters(), args...; check_length = false, name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs), remove_conserved = false, include_zero_odes = true, checks = false, - expand_catalyst_funs = true, mtkcompile = false, kwargs... - ) - osys = ode_model( - rs; name, combinatoric_ratelaws, include_zero_odes, checks, - remove_conserved, expand_catalyst_funs - ) + expand_catalyst_funs = true, mtkcompile = false, kwargs...) + osys = ode_model(rs; name, combinatoric_ratelaws, include_zero_odes, checks, + remove_conserved, expand_catalyst_funs) # Handles potential differential algebraic equations (which requires `mtkcompile`). if mtkcompile @@ -1625,7 +1495,7 @@ function DiffEqBase.SteadyStateProblem( osys = complete(osys) end - prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any, Any}(u0), Dict{Any, Any}(p)) + prob_cond = (p isa DiffEqBase.NullParameters) ? u0 : merge(Dict{Any,Any}(u0), Dict{Any,Any}(p)) return SteadyStateProblem(osys, prob_cond, args...; check_length, kwargs...) end @@ -1646,7 +1516,7 @@ function _symbol_to_var(sys, sym) throw(ArgumentError("System $(nameof(sys)): variable $sym does not exist")) end end - return var + var end """ @@ -1702,11 +1572,11 @@ function symmap_to_varmap(sys, symmap::Tuple) end function symmap_to_varmap(sys, symmap::AbstractArray{Pair{Symbol, T}}) where {T} - return [_symbol_to_var(sys, sym) => val for (sym, val) in symmap] + [_symbol_to_var(sys, sym) => val for (sym, val) in symmap] end function symmap_to_varmap(sys, symmap::Dict{Symbol, T}) where {T} - return Dict(_symbol_to_var(sys, sym) => val for (sym, val) in symmap) + Dict(_symbol_to_var(sys, sym) => val for (sym, val) in symmap) end # don't permute any other types and let varmap_to_vars handle erroring @@ -1722,7 +1592,7 @@ Convert the given system of polynomial equations to multivariate polynomial repr For example, this can be used in HomotopyContinuation.jl functions. """ function to_multivariate_poly(polyeqs::AbstractVector{Symbolics.SymbolicT}) - @assert length(polyeqs) >= 1 "At least one expression must be passed to `multivariate_poly`." + @assert length(polyeqs)>=1 "At least one expression must be passed to `multivariate_poly`." poly_to_bs = Dict{SymbolicUtils.PolyVarT, Symbolics.SymbolicT}() bs_to_poly = Dict{Symbolics.SymbolicT, SymbolicUtils.PolyVarT}() @@ -1734,5 +1604,5 @@ function to_multivariate_poly(polyeqs::AbstractVector{Symbolics.SymbolicT}) end end - return ps + ps end diff --git a/src/reactionsystem_serialisation/serialisation_support.jl b/src/reactionsystem_serialisation/serialisation_support.jl index ee9fc8c84a..1c13f2bb6a 100644 --- a/src/reactionsystem_serialisation/serialisation_support.jl +++ b/src/reactionsystem_serialisation/serialisation_support.jl @@ -31,10 +31,8 @@ get_substring_end(str, idx1, offset) = String(collect(str)[idx1:(end + offset)]) ### Field Serialisation Support Functions ### # Function which handles the addition of a single component to the file string. -function push_field( - file_text::String, rn::ReactionSystem, - annotate::Bool, top_level::Bool, comp_funcs::Tuple - ) +function push_field(file_text::String, rn::ReactionSystem, + annotate::Bool, top_level::Bool, comp_funcs::Tuple) has_component, get_comp_string, get_comp_annotation = comp_funcs has_component(rn) || (return (file_text, false)) @@ -71,15 +69,13 @@ end # Converts a numeric expression (e.g. p*X + 2Y) to a string (e.g. "p*X + 2Y"). Also ensures that for # any variables (e.g. X(t)) the call part is stripped, and only variable name (e.g. X) is written. -function expression_2_string( - expr; - strip_call_dict = make_strip_call_dict(Symbolics.get_variables(expr)) - ) +function expression_2_string(expr; + strip_call_dict = make_strip_call_dict(Symbolics.get_variables(expr))) strip_called_expr = substitute(expr, strip_call_dict; filterer = sub_inside_filter) return repr(strip_called_expr) end function sub_inside_filter(ex) - return if operation(ex) isa Union{Differential, Pre} + return if operation(ex) isa Union{Differential,Pre} true else SymbolicUtils.default_substitute_filter(ex) @@ -101,10 +97,8 @@ function syms_2_declaration_string(syms; multiline_format = false) decs_string = (multiline_format ? " begin" : "") for sym in syms delimiter = (multiline_format ? "\n\t" : " ") - @string_append! decs_string delimiter sym_2_declaration_string( - sym; - multiline_format - ) + @string_append! decs_string delimiter sym_2_declaration_string(sym; + multiline_format) end multiline_format && (@string_append! decs_string "\nend") return decs_string @@ -224,28 +218,24 @@ end # List of all recognised metadata (we should add as many as possible), and th keyword used to declare # them in code. -const RECOGNISED_METADATA = Dict( - [ - Catalyst.ParameterConstantSpecies => "isconstantspecies" - Catalyst.VariableBCSpecies => "isbcspecies" - Catalyst.VariableSpecies => "isspecies" - Catalyst.EdgeParameter => "edgeparameter" - Catalyst.CompoundSpecies => "iscompound" - Catalyst.CompoundComponents => "components" - Catalyst.CompoundCoefficients => "coefficients" - MT.VariableDescription => "description" - MT.VariableBounds => "bounds" - MT.VariableUnit => "unit" - MT.VariableConnectType => "connect" - #MT.VariableNoiseType => "noise" - MT.VariableInput => "input" - MT.VariableOutput => "output" - MT.VariableIrreducible => "irreducible" - MT.VariableStatePriority => "state_priority" - MT.VariableMisc => "misc" - MT.TimeDomain => "timedomain" - ] -) +const RECOGNISED_METADATA = Dict([Catalyst.ParameterConstantSpecies => "isconstantspecies" + Catalyst.VariableBCSpecies => "isbcspecies" + Catalyst.VariableSpecies => "isspecies" + Catalyst.EdgeParameter => "edgeparameter" + Catalyst.CompoundSpecies => "iscompound" + Catalyst.CompoundComponents => "components" + Catalyst.CompoundCoefficients => "coefficients" + MT.VariableDescription => "description" + MT.VariableBounds => "bounds" + MT.VariableUnit => "unit" + MT.VariableConnectType => "connect" + #MT.VariableNoiseType => "noise" + MT.VariableInput => "input" + MT.VariableOutput => "output" + MT.VariableIrreducible => "irreducible" + MT.VariableStatePriority => "state_priority" + MT.VariableMisc => "misc" + MT.TimeDomain => "timedomain"]) # List of metadata that does not need to be explicitly declared to be added (or which is handled separately). const SKIPPED_METADATA = [ @@ -253,14 +243,13 @@ const SKIPPED_METADATA = [ MT.MTKVariableTypeCtx, MT.SymScope, Symbolics.VariableDefaultValue, - Symbolics.VariableSource, -] + Symbolics.VariableSource] # List of metadata keys in an `AbstractSystem` which shouldn't be serialized. const SKIPPED_SYSTEM_METADATA = [MT.MutableCacheKey] function system_has_serializable_metadata(sys::MT.AbstractSystem) - return any(!in(SKIPPED_SYSTEM_METADATA), keys(MT.get_metadata(sys))) + any(!in(SKIPPED_SYSTEM_METADATA), keys(MT.get_metadata(sys))) end function system_metadata_to_string(meta::Base.ImmutableDict) diff --git a/src/reactionsystem_serialisation/serialise_fields.jl b/src/reactionsystem_serialisation/serialise_fields.jl index 2809a39d66..5c6d5cf6d4 100644 --- a/src/reactionsystem_serialisation/serialise_fields.jl +++ b/src/reactionsystem_serialisation/serialise_fields.jl @@ -43,10 +43,8 @@ const SIVS_FS = (seri_has_sivs, get_sivs_string, get_sivs_annotation) # Function which handles the addition of species, variable, and parameter declarations to the file # text. These must be handled as a unity in case there are default value dependencies between these. -function handle_us_n_ps( - file_text::String, rn::ReactionSystem, annotate::Bool, - top_level::Bool - ) +function handle_us_n_ps(file_text::String, rn::ReactionSystem, annotate::Bool, + top_level::Bool) # Fetches the system's parameters, species, and variables. Computes the `has_` `Bool`s. ps_all = filter(!_is_discrete, get_ps(rn)) discs_all = filter(_is_discrete, get_ps(rn)) @@ -180,7 +178,7 @@ end function seri_has_discretes(rn::ReactionSystem) return any(_is_discrete(p) for p in get_ps(rn)) end -# Temporary function, replace with better after reply from Aayush. +# Temporary function, repalce with better after reply from Aayush. _is_discrete(param) = occursin('(', "$(param)") # Extract a string which declares the system's discretes. Uses multiline declaration (a @@ -263,7 +261,7 @@ function get_reactions_string(rn::ReactionSystem) # Creates the string corresponding to the code which generates the system's reactions. rxs_string = "rxs = [" for rx in get_rxs(rn) - @string_append! rxs_string "\n\t" * reaction_string(rx, strip_call_dict) "," + @string_append! rxs_string "\n\t"*reaction_string(rx, strip_call_dict) "," end # Updates the string (including removing the last `,`) and returns it. @@ -455,8 +453,7 @@ function get_continuous_events_string(rn::ReactionSystem) continuous_events_string = "continuous_events = [" for continuous_event in MT.get_continuous_events(rn) @string_append! continuous_events_string "\n\t" continuous_event_string( - continuous_event, strip_call_dict - ) "," + continuous_event, strip_call_dict) "," end # Updates the string (including removing the last `,`) and returns it. @@ -520,8 +517,7 @@ function get_discrete_events_string(rn::ReactionSystem) discrete_events_string = "discrete_events = [" for discrete_event in MT.get_discrete_events(rn) @string_append! discrete_events_string "\n\t" discrete_event_string( - discrete_event, strip_call_dict - ) "," + discrete_event, strip_call_dict) "," end # Updates the string (including removing the last `,`) and returns it. @@ -572,12 +568,12 @@ end # Extract a string which declares the system's brownian types. function get_brownian_type_string(rn::ReactionSystem) - return get_unsupported_comp_string("brownian types") + get_unsupported_comp_string("brownian types") end # Creates an annotation for the system's brownian types. function get_brownian_type_annotation(rn::ReactionSystem) - return get_unsupported_comp_annotation("Brownian types:") + get_unsupported_comp_annotation("Brownian types:") end # Combines the 3 brownian types-related functions in a constant tuple. @@ -592,12 +588,12 @@ end # Extract a string which declares the system's jump types. function get_jump_type_string(rn::ReactionSystem) - return get_unsupported_comp_string("jump types") + get_unsupported_comp_string("jump types") end # Creates an annotation for the system's jump types. function get_jump_type_annotation(rn::ReactionSystem) - return get_unsupported_comp_annotation("Jump types:") + get_unsupported_comp_annotation("Jump types:") end # Combines the 3 jump types-related functions in a constant tuple. @@ -608,10 +604,8 @@ const JUMP_TYPE_FS = (seri_has_jump_type, get_jump_type_string, get_jump_type_an # Specific `push_field` function, which is used for the system field (where the annotation option # must be passed to the `get_component_string` function). Since non-ReactionSystem systems cannot be # written to file, this function throws an error if any such systems are encountered. -function push_systems_field( - file_text::String, rn::ReactionSystem, annotate::Bool, - top_level::Bool - ) +function push_systems_field(file_text::String, rn::ReactionSystem, annotate::Bool, + top_level::Bool) # Checks whether there are any subsystems, and if these are ReactionSystems. seri_has_systems(rn) || (return (file_text, false)) if any(!(system isa ReactionSystem) for system in MT.get_systems(rn)) diff --git a/src/reactionsystem_serialisation/serialise_reactionsystem.jl b/src/reactionsystem_serialisation/serialise_reactionsystem.jl index 622acfc8d2..884cd1b7b4 100644 --- a/src/reactionsystem_serialisation/serialise_reactionsystem.jl +++ b/src/reactionsystem_serialisation/serialise_reactionsystem.jl @@ -30,18 +30,14 @@ Notes: - Reaction systems with components that have units cannot currently be saved. - The `ReactionSystem` is saved using *programmatic* (not DSL) format for model creation. """ -function save_reactionsystem( - filename::String, rn::ReactionSystem; - annotate = true, safety_check = true - ) +function save_reactionsystem(filename::String, rn::ReactionSystem; + annotate = true, safety_check = true) # Error and warning checks. reactionsystem_uptodate_check() if !isempty(MT.get_tstops(rn)) || (!isempty(get_systems(rn)) && !isempty(MT.symbolic_tstops(rn))) - error( - "Serialization of ReactionSystems with tstops is not yet supported. " * - "Please remove tstops before saving, or track " * - "https://github.com/SciML/Catalyst.jl/issues for updates." - ) + error("Serialization of ReactionSystems with tstops is not yet supported. " * + "Please remove tstops before saving, or track " * + "https://github.com/SciML/Catalyst.jl/issues for updates.") end if !isempty(get_networkproperties(rn)) @warn "The serialised network has cached network properties (e.g. computed conservation laws). This will not be saved as part of the network, and must be recomputed when it is loaded." @@ -78,29 +74,20 @@ function get_full_system_string(rn::ReactionSystem, annotate::Bool, top_level::B file_text, _ = push_field(file_text, rn, annotate, top_level, IV_FS) file_text, has_sivs = push_field(file_text, rn, annotate, top_level, SIVS_FS) file_text, has_parameters, has_discretes, has_species, has_variables = handle_us_n_ps( - file_text, rn, annotate, top_level - ) + file_text, rn, annotate, top_level) file_text, has_reactions = push_field(file_text, rn, annotate, top_level, REACTIONS_FS) file_text, has_equations = push_field(file_text, rn, annotate, top_level, EQUATIONS_FS) file_text, has_observed = push_field(file_text, rn, annotate, top_level, OBSERVED_FS) file_text, has_bindings = push_field(file_text, rn, annotate, top_level, BINDINGS_FS) file_text, has_initial_conditions = push_field(file_text, rn, annotate, top_level, INITIAL_CONDITIONS_FS) - file_text, has_continuous_events = push_field( - file_text, rn, annotate, - top_level, CONTINUOUS_EVENTS_FS - ) - file_text, has_discrete_events = push_field( - file_text, rn, annotate, - top_level, DISCRETE_EVENTS_FS - ) - file_text, has_brownians = push_field( - file_text, rn, annotate, - top_level, BROWNIAN_TYPE_FS - ) - file_text, has_jumps = push_field( - file_text, rn, annotate, - top_level, JUMP_TYPE_FS - ) + file_text, has_continuous_events = push_field(file_text, rn, annotate, + top_level, CONTINUOUS_EVENTS_FS) + file_text, has_discrete_events = push_field(file_text, rn, annotate, + top_level, DISCRETE_EVENTS_FS) + file_text, has_brownians = push_field(file_text, rn, annotate, + top_level, BROWNIAN_TYPE_FS) + file_text, has_jumps = push_field(file_text, rn, annotate, + top_level, JUMP_TYPE_FS) file_text, has_systems = push_systems_field(file_text, rn, annotate, top_level) # Finalise the system. Creates the final `ReactionSystem` call. @@ -109,8 +96,7 @@ function get_full_system_string(rn::ReactionSystem, annotate::Bool, top_level::B rn, annotate, top_level, has_sivs, has_species, has_variables, has_parameters, has_discretes, has_reactions, has_equations, has_observed, has_bindings, has_initial_conditions, has_continuous_events, - has_discrete_events, has_brownians, has_jumps, has_systems - ) + has_discrete_events, has_brownians, has_jumps, has_systems) annotate || (@string_prepend! "\n" file_text) annotate && top_level && @string_prepend! "\n# Serialised using Catalyst version v$(Catalyst.VERSION)." file_text @@ -122,12 +108,10 @@ end # Creates a ReactionSystem call for creating the model. Adds all the correct inputs to it. The input # `has_` `Bool`s described which inputs are used. If the model is `complete`, this is handled here. -function make_reaction_system_call( - rs::ReactionSystem, annotate, top_level, has_sivs, +function make_reaction_system_call(rs::ReactionSystem, annotate, top_level, has_sivs, has_species, has_variables, has_parameters, has_discretes, has_reactions, has_equations, - has_observed, has_bindings, has_initial_conditions, has_continuous_events, has_discrete_events, has_brownians, - has_jumps, has_systems - ) + has_observed, has_bindings, has_initial_conditions, has_continuous_events, has_discrete_events, has_brownians, + has_jumps, has_systems) # Gets the independent variable input. iv = x_2_string(get_iv(rs)) diff --git a/src/registered_functions.jl b/src/registered_functions.jl index 58cb4e50fc..25bf660e08 100644 --- a/src/registered_functions.jl +++ b/src/registered_functions.jl @@ -57,10 +57,8 @@ hillar(X, Y, v, K, n) = v * (X^n) / (X^n + Y^n + K^n) @register_derivative hillar(X, Y, v, K, n) 2 -v * n * (Y^(n - 1)) * (X^n) / (X^n + Y^n + K^n)^2 @register_derivative hillar(X, Y, v, K, n) 3 (X^n) / (X^n + Y^n + K^n) @register_derivative hillar(X, Y, v, K, n) 4 -v * n * (K^(n - 1)) * (X^n) / (X^n + Y^n + K^n)^2 -@register_derivative hillar(X, Y, v, K, n) 5 v * (X^n) * ( - log(X) * (Y^n + K^n) - (Y^n) * log(Y) - - (K^n) * log(K) -) / (X^n + Y^n + K^n)^2 +@register_derivative hillar(X, Y, v, K, n) 5 v * (X^n) * (log(X) * (Y^n + K^n) - (Y^n) * log(Y) - + (K^n) * log(K)) / (X^n + Y^n + K^n)^2 # Tuple storing all registered function (for use in various functionalities). @@ -105,16 +103,14 @@ function expand_catalyst_function(expr) return args[2] * (args[3]^args[4]) / ((args[1])^args[4] + (args[3])^args[4]) elseif operation(expr) == Catalyst.hillar return args[3] * (args[1]^args[5]) / - ((args[1])^args[5] + (args[2])^args[5] + (args[4])^args[5]) + ((args[1])^args[5] + (args[2])^args[5] + (args[4])^args[5]) end end # If applied to a Reaction, return a reaction with its rate modified. function expand_registered_functions(rx::Reaction) - return Reaction( - expand_registered_functions(rx.rate), rx.substrates, rx.products, - rx.substoich, rx.prodstoich, rx.netstoich, rx.only_use_rate, rx.metadata - ) + Reaction(expand_registered_functions(rx.rate), rx.substrates, rx.products, + rx.substoich, rx.prodstoich, rx.netstoich, rx.only_use_rate, rx.metadata) end # If applied to a Equation, returns it with it applied to lhs and rhs. @@ -128,10 +124,8 @@ function expand_registered_functions(ce::MT.SymbolicContinuousCallback) affect_eqs = isnothing(ce.affect) ? nothing : expand_registered_functions(ce.affect.affect) affect_neg_eqs = isnothing(ce.affect_neg) ? nothing : expand_registered_functions(ce.affect_neg.affect) disc_ps = isnothing(ce.affect) ? MT.SymbolicT[] : ce.affect.discrete_parameters - return MT.SymbolicContinuousCallback( - conditions, affect_eqs; - affect_neg = affect_neg_eqs, discrete_parameters = disc_ps - ) + return MT.SymbolicContinuousCallback(conditions, affect_eqs; + affect_neg = affect_neg_eqs, discrete_parameters = disc_ps) end # If applied to a discrete event, returns it applied to condition and affect. diff --git a/src/spatial_reaction_systems/discrete_space_jump_systems.jl b/src/spatial_reaction_systems/discrete_space_jump_systems.jl index 6d17eef61d..cf8d750bea 100644 --- a/src/spatial_reaction_systems/discrete_space_jump_systems.jl +++ b/src/spatial_reaction_systems/discrete_space_jump_systems.jl @@ -1,13 +1,11 @@ ### JumpProblem ### # Builds a spatial JumpProblem from a DiscreteProblem containing a `DiscreteSpaceReactionSystem`. -function JumpProcesses.JumpProblem( - dsrs::DiscreteSpaceReactionSystem, u0_in, tspan, +function JumpProcesses.JumpProblem(dsrs::DiscreteSpaceReactionSystem, u0_in, tspan, p_in = DiffEqBase.NullParameters(), args...; aggregator = JumpProcesses.NSM(), combinatoric_ratelaws = get_combinatoric_ratelaws(reactionsystem(dsrs)), - name = nameof(reactionsystem(dsrs)), kwargs... - ) - + name = nameof(reactionsystem(dsrs)), kwargs...) + # Creates a `DiscreteProblem` from the input, which is uses as the internal problem. dprob = DiffEqBase.DiscreteProblem(dsrs, u0_in, tspan, p_in) @@ -18,24 +16,18 @@ function JumpProcesses.JumpProblem( # The non-spatial DiscreteProblem have a u0 matrix with entries for all combinations of species and vertices. hopping_constants = make_hopping_constants(dprob, dsrs) sma_jumps = make_spatial_majumps(dprob, dsrs) - non_spat_dprob = DiscreteProblem( - reshape(dprob.u0, num_species(dsrs), num_verts(dsrs)), - dprob.tspan, first.(dprob.p[1]) - ) + non_spat_dprob = DiscreteProblem(reshape(dprob.u0, num_species(dsrs), num_verts(dsrs)), + dprob.tspan, first.(dprob.p[1])) # Creates and returns a spatial JumpProblem (masked spaces are not supported by these). spatial_system = has_masked_dspace(dsrs) ? get_dspace_graph(dsrs) : dspace(dsrs) - return JumpProblem( - non_spat_dprob, aggregator, sma_jumps; - hopping_constants, spatial_system, name, kwargs... - ) + return JumpProblem(non_spat_dprob, aggregator, sma_jumps; + hopping_constants, spatial_system, name, kwargs...) end # Builds a spatial DiscreteProblem from a Lattice Reaction System. -function DiffEqBase.DiscreteProblem( - dsrs::DiscreteSpaceReactionSystem, u0_in, tspan, - p_in = DiffEqBase.NullParameters(), args...; kwargs... - ) +function DiffEqBase.DiscreteProblem(dsrs::DiscreteSpaceReactionSystem, u0_in, tspan, + p_in = DiffEqBase.NullParameters(), args...; kwargs...) if !is_transport_system(dsrs) error("Currently discrete space Jump simulations only supported when all spatial reactions are transport reactions.") end @@ -53,10 +45,8 @@ function DiffEqBase.DiscreteProblem( # edge_ps values are sparse matrices. Here, index (i,j) is a parameter's value in the edge from vertex i to vertex j. # Uniform vertex/edge parameters store only a single value (a length 1 vector, or size 1x1 sparse matrix). vert_ps, - edge_ps = dspace_process_p( - p_in, vertex_parameters(dsrs), - edge_parameters(dsrs), dsrs - ) + edge_ps = dspace_process_p(p_in, vertex_parameters(dsrs), + edge_parameters(dsrs), dsrs) # Returns a DiscreteProblem (which basically just stores the processed input). return DiscreteProblem(u0, tspan, [vert_ps; edge_ps], args...; kwargs...) @@ -67,10 +57,8 @@ function make_hopping_constants(dprob::DiscreteProblem, dsrs::DiscreteSpaceReact # Creates the all_diff_rates vector, containing for each species, its transport rate across all edges. # If the transport rate is uniform for one species, the vector has a single element, else one for each edge. spatial_rates_dict = Dict(compute_all_transport_rates(Dict(dprob.p), dsrs)) - all_diff_rates = [ - haskey(spatial_rates_dict, s) ? spatial_rates_dict[s] : [0.0] - for s in species(dsrs) - ] + all_diff_rates = [haskey(spatial_rates_dict, s) ? spatial_rates_dict[s] : [0.0] + for s in species(dsrs)] # Creates an array (of the same size as the hopping constant array) containing all edges. # First the array is a NxM matrix (number of species x number of vertices). Each element is a @@ -84,13 +72,9 @@ function make_hopping_constants(dprob::DiscreteProblem, dsrs::DiscreteSpaceReact # Creates the hopping constants array. It has the same shape as the edge array, but each # element is that species transportation rate along that edge - hopping_constants = [ - [ - Catalyst.get_edge_value(all_diff_rates[s_idx], e) - for e in edge_array[s_idx, src_idx] - ] - for s_idx in 1:num_species(dsrs), src_idx in 1:num_verts(dsrs) - ] + hopping_constants = [[Catalyst.get_edge_value(all_diff_rates[s_idx], e) + for e in edge_array[s_idx, src_idx]] + for s_idx in 1:num_species(dsrs), src_idx in 1:num_verts(dsrs)] return hopping_constants end @@ -99,10 +83,8 @@ end # Not sure if there is any form of performance improvement from that though. Likely not the case. function make_spatial_majumps(dprob, dsrs::DiscreteSpaceReactionSystem) # Creates a vector, storing which reactions have spatial components. - is_spatials = [ - has_spatial_vertex_component(rx.rate, dprob.p) - for rx in reactions(reactionsystem(dsrs)) - ] + is_spatials = [has_spatial_vertex_component(rx.rate, dprob.p) + for rx in reactions(reactionsystem(dsrs))] # Creates templates for the rates (uniform and spatial) and the stoichiometries. # We cannot fetch reactant_stoich and net_stoich from a (non-spatial) MassActionJump. @@ -126,10 +108,8 @@ function make_spatial_majumps(dprob, dsrs::DiscreteSpaceReactionSystem) # Loops through reactions with spatial rates, computes their rates and stoichiometries. for (is_spat, rx) in zip(is_spatials, reactions(reactionsystem(dsrs))) is_spat || continue - s_rates[cur_rx - length(u_rates), :] .= compute_vertex_value( - rx.rate, dsrs; - ps = dprob.p - ) + s_rates[cur_rx - length(u_rates), :] .= compute_vertex_value(rx.rate, dsrs; + ps = dprob.p) substoich_map = Pair.(rx.substrates, rx.substoich) reactant_stoich[cur_rx] = int_map(substoich_map, reactionsystem(dsrs)) net_stoich[cur_rx] = int_map(rx.netstoich, reactionsystem(dsrs)) diff --git a/src/spatial_reaction_systems/discrete_space_reaction_systems.jl b/src/spatial_reaction_systems/discrete_space_reaction_systems.jl index b011ec3396..d1807bd847 100644 --- a/src/spatial_reaction_systems/discrete_space_reaction_systems.jl +++ b/src/spatial_reaction_systems/discrete_space_reaction_systems.jl @@ -102,11 +102,9 @@ struct DiscreteSpaceReactionSystem{Q, R, S, T} <: MT.AbstractSystem "The name of the discrete space reaction system. Typically taken directly from the base `ReactionSystem`." name::Symbol - function DiscreteSpaceReactionSystem( - rs::ReactionSystem{Q}, spatial_reactions::Vector{R}, + function DiscreteSpaceReactionSystem(rs::ReactionSystem{Q}, spatial_reactions::Vector{R}, dspace::S, num_verts::Int64, num_edges::Int64, edge_iterator::T; - name::Symbol = MT.nameof(rs) - ) where {Q, R, S, T} + name::Symbol = MT.nameof(rs)) where {Q, R, S, T} # Error checks. if !(R <: AbstractSpatialReaction) throw(ArgumentError("The second argument must be a vector of AbstractSpatialReaction subtypes.")) @@ -135,12 +133,8 @@ struct DiscreteSpaceReactionSystem{Q, R, S, T} <: MT.AbstractSystem if isempty(spatial_reactions) spat_species = SymbolicT[] else - spat_species = unique( - reduce( - vcat, - [spatial_species(sr) for sr in spatial_reactions] - ) - ) + spat_species = unique(reduce(vcat, + [spatial_species(sr) for sr in spatial_reactions])) end num_species = length(unique([species(rs); spat_species])) @@ -150,8 +144,7 @@ struct DiscreteSpaceReactionSystem{Q, R, S, T} <: MT.AbstractSystem srs_edge_parameters = SymbolicT[] else srs_edge_parameters = setdiff( - reduce(vcat, [parameters(sr) for sr in spatial_reactions]), parameters(rs) - ) + reduce(vcat, [parameters(sr) for sr in spatial_reactions]), parameters(rs)) end edge_parameters = unique([rs_edge_parameters; srs_edge_parameters]) vertex_parameters = filter(!isedgeparameter, parameters(rs)) @@ -162,8 +155,7 @@ struct DiscreteSpaceReactionSystem{Q, R, S, T} <: MT.AbstractSystem # Checks that all spatial reactions are valid for this reaction system. foreach( sr -> check_spatial_reaction_validity(rs, sr; edge_parameters = edge_parameters), - spatial_reactions - ) + spatial_reactions) # Additional error checks. if any(is_array_symvar(symvar) for symvar in [ps; species(rs)]) @@ -172,8 +164,7 @@ struct DiscreteSpaceReactionSystem{Q, R, S, T} <: MT.AbstractSystem return new{Q, R, S, T}( rs, spatial_reactions, dspace, num_verts, num_edges, num_species, - spat_species, ps, vertex_parameters, edge_parameters, edge_iterator - ) + spat_species, ps, vertex_parameters, edge_parameters, edge_iterator) end end @@ -189,15 +180,13 @@ function DiscreteSpaceReactionSystem(rs, srs, dspace::DiGraph; kwargs...) end # Creates a DiscreteSpaceReactionSystem from a (undirected) Graph dspace (graph grid). function DiscreteSpaceReactionSystem(rs, srs, dspace::SimpleGraph; kwargs...) - return DiscreteSpaceReactionSystem(rs, srs, DiGraph(dspace); kwargs...) + DiscreteSpaceReactionSystem(rs, srs, DiGraph(dspace); kwargs...) end # Creates a DiscreteSpaceReactionSystem from a CartesianGrid dspace (cartesian grid) or a Boolean Array # dspace (masked grid). These two are quite similar, so much code can be reused in a single interface. -function DiscreteSpaceReactionSystem( - rs, srs, dspace::GridLattice{N, T}; - diagonal_connections = false, kwargs... - ) where {N, T} +function DiscreteSpaceReactionSystem(rs, srs, dspace::GridLattice{N, T}; + diagonal_connections = false, kwargs...) where {N, T} # Error checks. (N > 3) && error("Grids of higher dimension than 3 is currently not supported.") @@ -218,10 +207,8 @@ function DiscreteSpaceReactionSystem( g_size = grid_size(dspace) edge_iterator = Vector{Pair{Int64, Int64}}(undef, num_edges) for (fspat_idx, grid_idx) in enumerate(fspat_to_grid_idx) - for neighbour_grid_idx in get_neighbours( - dspace, grid_idx, g_size; - diagonal_connections - ) + for neighbour_grid_idx in get_neighbours(dspace, grid_idx, g_size; + diagonal_connections) cur_vert += 1 edge_iterator[cur_vert] = fspat_idx => grid_to_fspat_idx[neighbour_grid_idx...] end @@ -240,16 +227,14 @@ count_verts(grid::Array{Bool, N}) where {N} = count(grid) # Counts and edges on a Cartesian grid. The formula counts the number of internal, side, edge, and # corner vertices (on the grid). `l,m,n = grid_dims(grid),1,1` ensures that "extra" dimensions get # length 1. The formula holds even if one or more of l, m, and n are 1. -function count_edges( - grid::CartesianGridRej{N, T}; - diagonal_connections = false - ) where {N, T} +function count_edges(grid::CartesianGridRej{N, T}; + diagonal_connections = false) where {N, T} l, m, n = grid_size(grid)..., 1, 1 (ni, ns, ne, nc) = diagonal_connections ? (26, 17, 11, 7) : (6, 5, 4, 3) num_edges = ni * (l - 2) * (m - 2) * (n - 2) + # Edges from internal vertices. - ns * (2(l - 2) * (m - 2) + 2(l - 2) * (n - 2) + 2(m - 2) * (n - 2)) + # Edges from side vertices. - ne * (4(l - 2) + 4(m - 2) + 4(n - 2)) + # Edges from edge vertices. - nc * 8 # Edges from corner vertices. + ns * (2(l - 2) * (m - 2) + 2(l - 2) * (n - 2) + 2(m - 2) * (n - 2)) + # Edges from side vertices. + ne * (4(l - 2) + 4(m - 2) + 4(n - 2)) + # Edges from edge vertices. + nc * 8 # Edges from corner vertices. return num_edges end @@ -260,12 +245,8 @@ function count_edges(grid::Array{Bool, N}; diagonal_connections = false) where { num_edges = 0 for grid_idx in get_grid_indices(grid) grid[grid_idx] || continue - num_edges += length( - get_neighbours( - grid, Tuple(grid_idx), g_size; - diagonal_connections - ) - ) + num_edges += length(get_neighbours(grid, Tuple(grid_idx), g_size; + diagonal_connections)) end return num_edges end @@ -291,30 +272,24 @@ function get_index_converters(grid::GridLattice{N, T}, num_verts) where {N, T} end # For a vertex's grid index, and a dspace, returns the grid indices of all its (valid) neighbours. -function get_neighbours( - grid::GridLattice{N, T}, grid_idx, g_size; - diagonal_connections = false - ) where {N, T} +function get_neighbours(grid::GridLattice{N, T}, grid_idx, g_size; + diagonal_connections = false) where {N, T} # Depending on the grid's dimension, find all potential neighbours. if grid_dims(grid) == 1 potential_neighbours = [grid_idx .+ (i) for i in -1:1] elseif grid_dims(grid) == 2 potential_neighbours = [grid_idx .+ (i, j) for i in -1:1 for j in -1:1] else - potential_neighbours = [ - grid_idx .+ (i, j, k) for i in -1:1 for j in -1:1 - for k in -1:1 - ] + potential_neighbours = [grid_idx .+ (i, j, k) for i in -1:1 for j in -1:1 + for k in -1:1] end # Depending on whether diagonal connections are used or not, find valid neighbours. if diagonal_connections filter!(n_idx -> n_idx !== grid_idx, potential_neighbours) else - filter!( - n_idx -> count(n_idx .== grid_idx) == (length(g_size) - 1), - potential_neighbours - ) + filter!(n_idx -> count(n_idx .== grid_idx) == (length(g_size) - 1), + potential_neighbours) end # Removes neighbours outside of the grid, and returns the full list. @@ -333,7 +308,7 @@ end # Gets an iterator over a grid's grid indices. Separate function so we can handle the two grid types # separately (i.e. not calling `CartesianIndices(ones(grid_size(grid)))` unnecessarily for masked grids). function get_grid_indices(grid::CartesianGridRej{N, T}) where {N, T} - return CartesianIndices(ones(grid_size(grid))) + CartesianIndices(ones(grid_size(grid))) end get_grid_indices(grid::Array{Bool, N}) where {N} = CartesianIndices(grid) @@ -433,7 +408,7 @@ Returns `true` if `dsrs` was created using a cartesian grid discrete space (e.g. Otherwise, returns `false`. """ function has_cartesian_dspace(dsrs::DiscreteSpaceReactionSystem) - return dspace(dsrs) isa CartesianGridRej{N, T} where {N, T} + dspace(dsrs) isa CartesianGridRej{N, T} where {N, T} end """ @@ -493,17 +468,15 @@ Returns dsrs's discrete space as a graph. Currently does not work for Cartesian """ function get_dspace_graph(dsrs::DiscreteSpaceReactionSystem) has_graph_dspace(dsrs) && return dspace(dsrs) - return Graphs.SimpleGraphFromIterator( - Graphs.SimpleEdge(e[1], e[2]) - for e in edge_iterator(dsrs) - ) + return Graphs.SimpleGraphFromIterator(Graphs.SimpleEdge(e[1], e[2]) + for e in edge_iterator(dsrs)) end ### Catalyst-based Getters ### # Get all species. function species(dsrs::DiscreteSpaceReactionSystem) - return unique([species(reactionsystem(dsrs)); spatial_species(dsrs)]) + unique([species(reactionsystem(dsrs)); spatial_species(dsrs)]) end # Generic ones (simply forwards call to the non-spatial system). @@ -522,13 +495,13 @@ MT.get_metadata(dsrs::DiscreteSpaceReactionSystem) = MT.get_metadata(reactionsys # Lattice reaction systems should not be combined with compositional modelling. # Maybe these should be allowed anyway? Still feel a bit weird function MT.get_eqs(dsrs::DiscreteSpaceReactionSystem) - return MT.get_eqs(reactionsystem(dsrs)) + MT.get_eqs(reactionsystem(dsrs)) end function MT.get_unknowns(dsrs::DiscreteSpaceReactionSystem) - return MT.get_unknowns(reactionsystem(dsrs)) + MT.get_unknowns(reactionsystem(dsrs)) end function MT.get_ps(dsrs::DiscreteSpaceReactionSystem) - return MT.get_ps(reactionsystem(dsrs)) + MT.get_ps(reactionsystem(dsrs)) end # Technically should not be used, but has to be declared for the `show` function to work. @@ -538,7 +511,7 @@ end # Other non-relevant getters. function MT.independent_variables(dsrs::DiscreteSpaceReactionSystem) - return MT.independent_variables(reactionsystem(dsrs)) + MT.independent_variables(reactionsystem(dsrs)) end ### Edge Parameter Value Generators ### @@ -600,10 +573,8 @@ function make_edge_p_values(dsrs::DiscreteSpaceReactionSystem, make_edge_p_value # If not, then the sparse matrix simply becomes empty in that position. values[e[1], e[2]] = eps() - values[e[1], e[2]] = make_edge_p_value( - fspat_to_grid_idx[e[1]], - fspat_to_grid_idx[e[2]] - ) + values[e[1], e[2]] = make_edge_p_value(fspat_to_grid_idx[e[1]], + fspat_to_grid_idx[e[2]]) end return values @@ -652,11 +623,9 @@ D_vals = make_directed_edge_values(dsrs, (0.1, 0.1), (0.1, 0.0)) ``` Here, since we have a 2d grid, we only provide the first two Tuples to `make_directed_edge_values`. """ -function make_directed_edge_values( - dsrs::DiscreteSpaceReactionSystem, x_vals::Tuple{T, T}, +function make_directed_edge_values(dsrs::DiscreteSpaceReactionSystem, x_vals::Tuple{T, T}, y_vals::Union{Nothing, Tuple{T, T}} = nothing, - z_vals::Union{Nothing, Tuple{T, T}} = nothing - ) where {T} + z_vals::Union{Nothing, Tuple{T, T}} = nothing) where {T} # Error checks. if has_graph_dspace(dsrs) error("The `make_directed_edge_values` function is only meant for discrete spaces with (Cartesian or masked) grid structures. It cannot be applied to graph discrete spaces.") diff --git a/src/spatial_reaction_systems/discrete_space_sim_struct_interfacing.jl b/src/spatial_reaction_systems/discrete_space_sim_struct_interfacing.jl index 777b862e3f..f956584ba1 100644 --- a/src/spatial_reaction_systems/discrete_space_sim_struct_interfacing.jl +++ b/src/spatial_reaction_systems/discrete_space_sim_struct_interfacing.jl @@ -46,13 +46,13 @@ function spat_setp!(sim_struct, p, dsrs::DiscreteSpaceReactionSystem, p_vals) # Reshapes the values to a vector of the correct form, and calls spat_setp! on the input structure. p_vals_reshaped = vertex_value_form(p_vals, dsrs, p) - return spat_setp!(sim_struct, p_idx, p_vals_reshaped, num_verts(dsrs)) + spat_setp!(sim_struct, p_idx, p_vals_reshaped, num_verts(dsrs)) end # Note: currently, `spat_setp!(oprob::ODEProblem, ...`) and `spat_setp!(SciMLBase.AbstractODEIntegrator, ...`) # are identical and could be merged to a single function. function spat_setp!(oprob::ODEProblem, p_idx::Int64, p_vals, num_verts) - return if length(p_vals) == 1 + if length(p_vals) == 1 foreach(idx -> (oprob.p[p_idx][idx] = p_vals[1]), 1:num_verts) elseif length(p_vals) == length(oprob.p[p_idx]) foreach(idx -> (oprob.p[p_idx][idx] = p_vals[idx]), 1:num_verts) @@ -65,7 +65,7 @@ function spat_setp!(jprob::JumpProblem, p_idx::Int64, p_vals, num_verts) error("The `spat_setp!` function is currently not supported for `JumpProblem`s.") end function spat_setp!(oint::SciMLBase.AbstractODEIntegrator, p_idx::Int64, p_vals, num_verts) - return if length(p_vals) == 1 + if length(p_vals) == 1 foreach(idx -> (oint.p[p_idx][idx] = p_vals[1]), 1:num_verts) elseif length(p_vals) == length(oint.p[p_idx]) foreach(idx -> (oint.p[p_idx][idx] = p_vals[idx]), 1:num_verts) @@ -120,7 +120,7 @@ spat_getp(oprob, :k1, dsrs) # Retrieves the value of `k1`. function spat_getp(sim_struct, p, dsrs::DiscreteSpaceReactionSystem) edge_param_check(p, dsrs) p_idx, _ = get_p_idxs(p, dsrs) - return spat_getp(sim_struct, p_idx, extract_dspace(dsrs), num_verts(dsrs)) + spat_getp(sim_struct, p_idx, extract_dspace(dsrs), num_verts(dsrs)) end # Retrieves the discrete space values for problem or integrator structures. @@ -186,38 +186,34 @@ function spat_setu!(sim_struct, sp, dsrs::DiscreteSpaceReactionSystem, u) # Reshapes the values to a vector of the correct form, and calls spat_setu! on the input structure. u_reshaped = vertex_value_form(u, dsrs, sp) - return spat_setu!(sim_struct, sp_idx, sp_tot, u_reshaped, num_verts(dsrs)) + spat_setu!(sim_struct, sp_idx, sp_tot, u_reshaped, num_verts(dsrs)) end function spat_setu!(oprob::ODEProblem, sp_idx::Int64, sp_tot::Int64, u, num_verts) - return if length(u) == 1 + if length(u) == 1 foreach(idx -> (oprob.u0[sp_idx + (idx - 1) * sp_tot] = u[1]), 1:num_verts) else foreach(idx -> (oprob.u0[sp_idx + (idx - 1) * sp_tot] = u[idx]), 1:num_verts) end end function spat_setu!(jprob::JumpProblem, sp_idx::Int64, sp_tot::Int64, u, num_verts) - return if length(u) == 1 + if length(u) == 1 foreach(idx -> (jprob.prob.u0[sp_idx, idx] = u[1]), 1:num_verts) else foreach(idx -> (jprob.prob.u0[sp_idx, idx] = u[idx]), 1:num_verts) end end -function spat_setu!( - oint::SciMLBase.AbstractODEIntegrator, sp_idx::Int64, sp_tot::Int64, - u, num_verts - ) - return if length(u) == 1 +function spat_setu!(oint::SciMLBase.AbstractODEIntegrator, sp_idx::Int64, sp_tot::Int64, + u, num_verts) + if length(u) == 1 foreach(idx -> (oint.u[sp_idx + (idx - 1) * sp_tot] = u[1]), 1:num_verts) else foreach(idx -> (oint.u[sp_idx + (idx - 1) * sp_tot] = u[idx]), 1:num_verts) end end -function spat_setu!( - jint::JumpProcesses.SSAIntegrator, sp_idx::Int64, sp_tot::Int64, - u, num_verts - ) - return if length(u) == 1 +function spat_setu!(jint::JumpProcesses.SSAIntegrator, sp_idx::Int64, sp_tot::Int64, + u, num_verts) + if length(u) == 1 foreach(idx -> (jint.u[sp_idx, idx] = u[1]), 1:num_verts) else foreach(idx -> (jint.u[sp_idx, idx] = u[idx]), 1:num_verts) @@ -264,7 +260,7 @@ spat_getu(oprob, :X1, dsrs) # Retrieves the value of `X1`. """ function spat_getu(sim_struct, sp, dsrs::DiscreteSpaceReactionSystem) sp_idx, sp_tot = get_sp_idxs(sp, dsrs) - return spat_getu(sim_struct, sp_idx, sp_tot, extract_dspace(dsrs)) + spat_getu(sim_struct, sp_idx, sp_tot, extract_dspace(dsrs)) end # Retrieves the discrete space values for problem or integrator structures. @@ -334,7 +330,7 @@ spat_getu(osol, :X1, dsrs; t = 0.0:10.0) # Returns the value of X1 at times 0.0, function spat_getu(sol::ODESolution, sp, dsrs::DiscreteSpaceReactionSystem; t = nothing) (t isa Number) && error("The input `t` to `spat_getu` must be a `AbstractVector`.") sp_idx, sp_tot = get_sp_idxs(sp, dsrs) - return spat_getu(sol, extract_dspace(dsrs), t, sp_idx, sp_tot) + spat_getu(sol, extract_dspace(dsrs), t, sp_idx, sp_tot) end # Function which handles the input in the case where `t` is `nothing` (i.e. return `sp`s value @@ -354,10 +350,8 @@ end # Function which handles the input in the case where `t` is a range of values (i.e. return `sp`s # value at all designated time points. -function spat_getu( - sol::ODESolution, dspace, t::AbstractVector{T}, sp_idx::Int64, - sp_tot::Int64 - ) where {T <: Number} +function spat_getu(sol::ODESolution, dspace, t::AbstractVector{T}, sp_idx::Int64, + sp_tot::Int64) where {T <: Number} # Checks that an appropriate `t` is provided (however, DiffEq does permit out-of-range `t`s). if (minimum(t) < sol.t[1]) || (maximum(t) > sol.t[end]) error("The range of the t values provided for sampling, ($(minimum(t)),$(maximum(t))) is not fully within the range of the simulation time span ($(sol.t[1]),$(sol.t[end])).") @@ -419,20 +413,18 @@ rebuild_spat_internals!(oprob) """ function rebuild_spat_internals!(oprob::ODEProblem) lt_ofun = SciMLBase.unwrapped_f(oprob.f.f) - return rebuild_spat_internals!(lt_ofun, oprob.p, lt_ofun.dsrs) + rebuild_spat_internals!(lt_ofun, oprob.p, lt_ofun.dsrs) end # Function for rebuilding a `DiscreteSpaceReactionSystem` integrator after it has been updated. function rebuild_spat_internals!(integrator::SciMLBase.AbstractODEIntegrator) lt_ofun = SciMLBase.unwrapped_f(integrator.f.f) - return rebuild_spat_internals!(lt_ofun, integrator.p, lt_ofun.dsrs) + rebuild_spat_internals!(lt_ofun, integrator.p, lt_ofun.dsrs) end # Function which rebuilds a `LatticeTransportODEFunction` functor for a new parameter set. -function rebuild_spat_internals!( - lt_ofun::LatticeTransportODEFunction, ps_new, - dsrs::DiscreteSpaceReactionSystem - ) +function rebuild_spat_internals!(lt_ofun::LatticeTransportODEFunction, ps_new, + dsrs::DiscreteSpaceReactionSystem) # Computes Jacobian properties. jac = !isnothing(lt_ofun.jac_transport) sparse = lt_ofun.sparse @@ -441,10 +433,8 @@ function rebuild_spat_internals!( ps_new = [(length(p) == 1) ? p[1] : p for p in deepcopy(ps_new)] ps_new = [p => p_val for (p, p_val) in zip(parameters(dsrs), deepcopy(ps_new))] vert_ps, - edge_ps = dspace_process_p( - ps_new, vertex_parameters(dsrs), - edge_parameters(dsrs), dsrs - ) + edge_ps = dspace_process_p(ps_new, vertex_parameters(dsrs), + edge_parameters(dsrs), dsrs) ps_new = [vert_ps; edge_ps] # Creates the new transport rates and transport Jacobian part. @@ -487,7 +477,7 @@ function replace_vec!(vec1, vec2) vec1[i] = v end foreach(idx -> deleteat!(vec1, idx), l1:-1:(l2 + 1)) - return foreach(val -> push!(vec1, val), vec2[(l1 + 1):l2]) + foreach(val -> push!(vec1, val), vec2[(l1 + 1):l2]) end # Currently not implemented. @@ -551,26 +541,26 @@ end function check_dspace_format(dspace::CartesianGridRej, u) (u isa AbstractArray) || error("The input u should be an AbstractArray. It is a $(typeof(u)).") - return (size(u) == dspace.dims) || + (size(u) == dspace.dims) || error("The input u should have size $(dspace.dims), but has size $(size(u)).") end function check_dspace_format(dspace::AbstractSparseArray, u) (u isa AbstractArray) || error("The input u should be an AbstractArray. It is a $(typeof(u)).") - return (size(u) == size(dspace)) || + (size(u) == size(dspace)) || error("The input u should have size $(size(dspace)), but has size $(size(u)).") end function check_dspace_format(dspace::DiGraph, u) (u isa AbstractArray) || error("The input u should be an AbstractVector. It is a $(typeof(u)).") - return (length(u) == nv(dspace)) || + (length(u) == nv(dspace)) || error("The input u should have length $(nv(dspace)), but has length $(length(u)).") end # Throws an error when interfacing with an edge parameter. function edge_param_check(p, dsrs) (p isa Symbol) && (p = _symbol_to_var(dsrs, p)) - return if isedgeparameter(p) + if isedgeparameter(p) throw(ArgumentError("The `spat_getp` and `spat_setp!` functions currently does not support edge parameter updating. If you require this functionality, please raise an issue on the Catalyst GitHub page and we can add this feature.")) end end diff --git a/src/spatial_reaction_systems/spatial_ODE_systems.jl b/src/spatial_reaction_systems/spatial_ODE_systems.jl index b12bdf3b91..bbf13ee614 100644 --- a/src/spatial_reaction_systems/spatial_ODE_systems.jl +++ b/src/spatial_reaction_systems/spatial_ODE_systems.jl @@ -64,28 +64,22 @@ struct LatticeTransportODEFunction{P, Q, R, S, T} """Remove when we add this as problem metadata""" dsrs::DiscreteSpaceReactionSystem - function LatticeTransportODEFunction( - ofunc::P, ps::Vector{<:Pair}, + function LatticeTransportODEFunction(ofunc::P, ps::Vector{<:Pair}, dsrs::DiscreteSpaceReactionSystem, sparse::Bool, jac_transport::Union{Nothing, Matrix{S}, SparseMatrixCSC{S, Int64}}, - transport_rates::Vector{Pair{Int64, SparseMatrixCSC{S, Int64}}} - ) where {P, S} + transport_rates::Vector{Pair{Int64, SparseMatrixCSC{S, Int64}}}) where {P, S} # Computes `LatticeTransportODEFunction` functor fields. heterogeneous_vert_p_idxs = make_heterogeneous_vert_p_idxs(ps, dsrs) mtk_ps, p_setters = make_mtk_ps_structs(ps, dsrs, heterogeneous_vert_p_idxs) t_rate_idx_types, - leaving_rates = make_t_types_and_leaving_rates( - transport_rates, - dsrs - ) + leaving_rates = make_t_types_and_leaving_rates(transport_rates, + dsrs) # Creates and returns the `LatticeTransportODEFunction` functor. - return new{P, typeof(mtk_ps), typeof(p_setters), S, typeof(jac_transport)}( - ofunc, + new{P, typeof(mtk_ps), typeof(p_setters), S, typeof(jac_transport)}(ofunc, num_verts(dsrs), num_species(dsrs), heterogeneous_vert_p_idxs, mtk_ps, p_setters, transport_rates, t_rate_idx_types, leaving_rates, Catalyst.edge_iterator(dsrs), - jac_transport, sparse, dsrs - ) + jac_transport, sparse, dsrs) end end @@ -94,10 +88,8 @@ end # Creates a vector with the heterogeneous vertex parameters' indexes in the full parameter vector. function make_heterogeneous_vert_p_idxs(ps, dsrs) p_dict = Dict(ps) - return findall( - (p_dict[p] isa Vector) && (length(p_dict[p]) > 1) - for p in parameters(dsrs) - ) + return findall((p_dict[p] isa Vector) && (length(p_dict[p]) > 1) + for p in parameters(dsrs)) end # Creates the MTKParameters structure and `p_setters` vector (which are used to manage @@ -107,10 +99,8 @@ function make_mtk_ps_structs(ps, dsrs, heterogeneous_vert_p_idxs) nonspatial_osys = complete(ode_model(reactionsystem(dsrs))) p_init = [p => p_dict[p][1] for p in parameters(nonspatial_osys)] mtk_ps = MT.MTKParameters(nonspatial_osys, p_init) - p_setters = [ - MT.setp(nonspatial_osys, p) - for p in parameters(dsrs)[heterogeneous_vert_p_idxs] - ] + p_setters = [MT.setp(nonspatial_osys, p) + for p in parameters(dsrs)[heterogeneous_vert_p_idxs]] return mtk_ps, p_setters end @@ -121,10 +111,8 @@ function make_t_types_and_leaving_rates(transport_rates, dsrs) for (s_idx, tr_pair) in enumerate(transport_rates) for e in Catalyst.edge_iterator(dsrs) # Updates the exit rate for species s_idx from vertex e.src. - leaving_rates[s_idx, e[1]] += get_transport_rate( - tr_pair[2], e, - t_rate_idx_types[s_idx] - ) + leaving_rates[s_idx, e[1]] += get_transport_rate(tr_pair[2], e, + t_rate_idx_types[s_idx]) end end return t_rate_idx_types, leaving_rates @@ -158,10 +146,9 @@ function (lt_ofun::LatticeTransportODEFunction)(du::AbstractVector, u, p, t) idx_src = get_index(e[1], s, lt_ofun.num_species) idx_dst = get_index(e[2], s, lt_ofun.num_species) du[idx_dst] += get_transport_rate(rates, e, lt_ofun.t_rate_idx_types[s_idx]) * - u[idx_src] + u[idx_src] end end - return end # Defines the functor's effect when applied as a Jacobian. @@ -181,20 +168,18 @@ function (lt_ofun::LatticeTransportODEFunction)(J::AbstractMatrix, u, p, t) end # Updates for the spatial reactions (adds the Jacobian values from the transportation reactions). - return J .+= lt_ofun.jac_transport + J .+= lt_ofun.jac_transport end ### ODEProblem ### # Creates an ODEProblem from a DiscreteSpaceReactionSystem. -function DiffEqBase.ODEProblem( - dsrs::DiscreteSpaceReactionSystem, u0_in, tspan, +function DiffEqBase.ODEProblem(dsrs::DiscreteSpaceReactionSystem, u0_in, tspan, p_in = DiffEqBase.NullParameters(), args...; jac = false, sparse = false, name = nameof(dsrs), include_zero_odes = true, combinatoric_ratelaws = get_combinatoric_ratelaws(reactionsystem(dsrs)), - remove_conserved = false, checks = false, kwargs... - ) + remove_conserved = false, checks = false, kwargs...) if !is_transport_system(dsrs) error("Currently discrete space ODE simulations are only supported when all spatial reactions are `TransportReaction`s.") end @@ -213,16 +198,12 @@ function DiffEqBase.ODEProblem( # Uniform vertex/edge parameters store only a single value (a length 1 vector, or size 1x1 sparse matrix). # In the `ODEProblem` vert_ps and edge_ps are merged (but for building the ODEFunction, they are separate). vert_ps, - edge_ps = dspace_process_p( - p_in, vertex_parameters(dsrs), - edge_parameters(dsrs), dsrs - ) + edge_ps = dspace_process_p(p_in, vertex_parameters(dsrs), + edge_parameters(dsrs), dsrs) # Creates the ODEFunction. - ofun = build_odefunction( - dsrs, vert_ps, edge_ps, jac, sparse, name, include_zero_odes, - combinatoric_ratelaws, remove_conserved, checks - ) + ofun = build_odefunction(dsrs, vert_ps, edge_ps, jac, sparse, name, include_zero_odes, + combinatoric_ratelaws, remove_conserved, checks) # Combines `vert_ps` and `edge_ps` to a single vector with values only (not a map). Creates ODEProblem. pval_dict = Dict([vert_ps; edge_ps]) @@ -231,24 +212,18 @@ function DiffEqBase.ODEProblem( end # Builds an ODEFunction for a spatial ODEProblem. -function build_odefunction( - dsrs::DiscreteSpaceReactionSystem, vert_ps::Vector{Pair{R, Vector{T}}}, +function build_odefunction(dsrs::DiscreteSpaceReactionSystem, vert_ps::Vector{Pair{R, Vector{T}}}, edge_ps::Vector{Pair{S, SparseMatrixCSC{T, Int64}}}, jac::Bool, sparse::Bool, name, include_zero_odes, combinatoric_ratelaws, - remove_conserved, checks - ) where {R, S, T} + remove_conserved, checks) where {R, S, T} # Error check. if remove_conserved throw(ArgumentError("Removal of conserved quantities is currently not supported for `DiscreteSpaceReactionSystem`s")) end # Prepares the inputs to the `LatticeTransportODEFunction` functor. - osys = complete( - ode_model( - reactionsystem(dsrs); - name, combinatoric_ratelaws, include_zero_odes, checks - ) - ) + osys = complete(ode_model(reactionsystem(dsrs); + name, combinatoric_ratelaws, include_zero_odes, checks)) ofunc_dense = ODEFunction(osys; jac = true, sparse = false) ofunc_sparse = ODEFunction(osys; jac = true, sparse = true) transport_rates = make_sidxs_to_transrate_map(vert_ps, edge_ps, dsrs) @@ -258,20 +233,16 @@ function build_odefunction( jac_transport = nothing jac_prototype = nothing else - jac_sparse = build_jac_prototype( - ofunc_sparse.jac_prototype, transport_rates, dsrs; - set_nonzero = jac - ) + jac_sparse = build_jac_prototype(ofunc_sparse.jac_prototype, transport_rates, dsrs; + set_nonzero = jac) jac_dense = Matrix(jac_sparse) jac_transport = (jac ? (sparse ? jac_sparse : jac_dense) : nothing) jac_prototype = (sparse ? jac_sparse : nothing) end # Creates the `LatticeTransportODEFunction` functor (if `jac`, sets it as the Jacobian as well). - f = LatticeTransportODEFunction( - ofunc_dense, [vert_ps; edge_ps], dsrs, sparse, - jac_transport, transport_rates - ) + f = LatticeTransportODEFunction(ofunc_dense, [vert_ps; edge_ps], dsrs, sparse, + jac_transport, transport_rates) J = (jac ? f : nothing) # Extracts the `Symbol` form for parameters (but not species). Creates and returns the `ODEFunction`. @@ -282,18 +253,14 @@ end # Builds a Jacobian prototype. # If requested, populate it with the constant values of the Jacobian's transportation part. -function build_jac_prototype( - ns_jac_prototype::SparseMatrixCSC{Float64, Int64}, +function build_jac_prototype(ns_jac_prototype::SparseMatrixCSC{Float64, Int64}, transport_rates::Vector{Pair{Int64, SparseMatrixCSC{T, Int64}}}, - dsrs::DiscreteSpaceReactionSystem; set_nonzero = false - ) where {T} + dsrs::DiscreteSpaceReactionSystem; set_nonzero = false) where {T} # Finds the indices of both the transport species, # and the species with transport only (that is, with no non-spatial dynamics but with spatial dynamics). trans_species = [tr[1] for tr in transport_rates] - trans_only_species = filter( - s_idx -> !Base.isstored(ns_jac_prototype, s_idx, s_idx), - trans_species - ) + trans_only_species = filter(s_idx -> !Base.isstored(ns_jac_prototype, s_idx, s_idx), + trans_species) # Finds the indices of all terms in the non-spatial jacobian. ns_jac_prototype_idxs = findnz(ns_jac_prototype) @@ -303,7 +270,7 @@ function build_jac_prototype( # Prepares vectors to store i and j indices of Jacobian entries. idx = 1 num_entries = num_verts(dsrs) * length(ns_i_idxs) + - num_edges(dsrs) * (length(trans_only_species) + length(trans_species)) + num_edges(dsrs) * (length(trans_only_species) + length(trans_species)) i_idxs = Vector{Int}(undef, num_entries) j_idxs = Vector{Int}(undef, num_entries) @@ -357,5 +324,4 @@ function set_jac_transport_values!(jac_prototype, transport_rates, dsrs) # Term due to species arriving to destination vertex. jac_prototype[idx_src, idx_dst] += val end - return end diff --git a/src/spatial_reaction_systems/spatial_reactions.jl b/src/spatial_reaction_systems/spatial_reactions.jl index 1a8f5fbf5b..89757a82f9 100644 --- a/src/spatial_reaction_systems/spatial_reactions.jl +++ b/src/spatial_reaction_systems/spatial_reactions.jl @@ -20,7 +20,7 @@ function isedgeparameter(x, default = false) if iscall(x) && operation(x) === getindex x = first(arguments(x)) end - return Symbolics.getmetadata(x, EdgeParameter, default) + Symbolics.getmetadata(x, EdgeParameter, default) end ### Transport Reaction Structures ### @@ -38,13 +38,13 @@ struct TransportReaction <: AbstractSpatialReaction if any(!MT.isparameter(var) for var in MT.get_variables(rate)) error("TransportReaction rate contains variables: $(filter(var -> !MT.isparameter(var), MT.get_variables(rate))). The rate must consist of parameters only.") end - return new(rate, species.val) + new(rate, species.val) end end # Macro for creating a `TransportReaction`. macro transport_reaction(rateex::ExprValues, species::ExprValues) - return make_transport_reaction(striplines(rateex), species) + make_transport_reaction(striplines(rateex), species) end function make_transport_reaction(rateex, species) # Handle interpolation of variables @@ -69,7 +69,7 @@ function make_transport_reaction(rateex, species) insert!(pexprs.args, idx, :([edgeparameter = true])) end - return quote + quote $pexprs $iv $sexprs @@ -84,10 +84,8 @@ MT.parameters(tr::TransportReaction) = collect(Symbolics.get_variables(tr.rate)) spatial_species(tr::TransportReaction) = [tr.species] # Checks that a `TransportReaction` is valid for a given reaction system. -function check_spatial_reaction_validity( - rs::ReactionSystem, tr::TransportReaction; - edge_parameters = [] - ) +function check_spatial_reaction_validity(rs::ReactionSystem, tr::TransportReaction; + edge_parameters = []) # Checks that the species exist in the reaction system. # (ODE simulation code becomes difficult if this is not required, # as non-spatial jacobian and f function generated from rs are of the wrong size). @@ -105,18 +103,14 @@ function check_spatial_reaction_validity( if any(isequal(tr.species, s) && !isequivalent(tr.species, s) for s in species(rs)) error("A transport reaction used a species, $(tr.species), with metadata not matching its discrete space reaction system. Please fetch this species from the reaction system and use it during transport reaction creation.") end - if any( - isequal(rs_p, tr_p) && !isequivalent(rs_p, tr_p) - for rs_p in parameters(rs), tr_p in Symbolics.get_variables(tr.rate) - ) + if any(isequal(rs_p, tr_p) && !isequivalent(rs_p, tr_p) + for rs_p in parameters(rs), tr_p in Symbolics.get_variables(tr.rate)) error("A transport reaction used a parameter with metadata not matching its discrete space reaction system. Please fetch this parameter from the reaction system and use it during transport reaction creation.") end # Checks that no edge parameter occurs among rates of non-spatial reactions. - return if any( - !isempty(intersect(Symbolics.get_variables(r.rate), edge_parameters)) - for r in reactions(rs) - ) + if any(!isempty(intersect(Symbolics.get_variables(r.rate), edge_parameters)) + for r in reactions(rs)) error("Edge parameter(s) were found as a rate of a non-spatial reaction.") end end @@ -130,15 +124,11 @@ end const ep_metadata = Catalyst.EdgeParameter => true function isequivalent(sym1, sym2; ignored_metadata = [MT.SymScope]) isequal(sym1, sym2) || (return false) - if any( - (md1 != ep_metadata) && (md1[1] ∉ ignored_metadata) && (md1 ∉ sym2.metadata) - for md1 in sym1.metadata - ) + if any((md1 != ep_metadata) && (md1[1] ∉ ignored_metadata) && (md1 ∉ sym2.metadata) + for md1 in sym1.metadata) return false - elseif any( - (md2 != ep_metadata) && (md2[1] ∉ ignored_metadata) && (md2 ∉ sym1.metadata) - for md2 in sym2.metadata - ) + elseif any((md2 != ep_metadata) && (md2[1] ∉ ignored_metadata) && (md2 ∉ sym1.metadata) + for md2 in sym2.metadata) return false elseif typeof(sym1) != typeof(sym2) return false @@ -161,7 +151,7 @@ end # Implements custom `hash`. function hash(tr::TransportReaction, h::UInt) h = Base.hash(tr.rate, h) - return Base.hash(tr.species, h) + Base.hash(tr.species, h) end ### Utility ### diff --git a/src/spatial_reaction_systems/utility.jl b/src/spatial_reaction_systems/utility.jl index 94bc704232..e1a16b1999 100644 --- a/src/spatial_reaction_systems/utility.jl +++ b/src/spatial_reaction_systems/utility.jl @@ -32,10 +32,8 @@ end # From a parameter input, split it into vertex parameters and edge parameters. # Store these in the desired internal format. -function dspace_process_p( - ps_in, ps_vertex_syms::Vector, - ps_edge_syms::Vector, dsrs::DiscreteSpaceReactionSystem - ) +function dspace_process_p(ps_in, ps_vertex_syms::Vector, + ps_edge_syms::Vector, dsrs::DiscreteSpaceReactionSystem) # p values can be given in various forms. This converts it to a Vector{Pair{Symbolics,...}} form. # Top-level vector: Maps each parameter to its value(s). # Second-level: Contains either a vector (vertex parameters) or a sparse matrix (edge parameters). @@ -112,10 +110,8 @@ function vertex_value_form(values, dsrs::DiscreteSpaceReactionSystem, sym::Symbo end # Converts values to the correct vector form for a Cartesian grid space. -function vertex_value_form( - values::AbstractArray, num_verts::Int64, - dspace::CartesianGridRej{N, T}, sym::SymbolicT - ) where {N, T} +function vertex_value_form(values::AbstractArray, num_verts::Int64, + dspace::CartesianGridRej{N, T}, sym::SymbolicT) where {N, T} if size(values) != dspace.dims throw(ArgumentError("The values for $sym did not have the same format as the space. Expected a $(dspace.dims) array, got one of size $(size(values))")) end @@ -126,10 +122,8 @@ function vertex_value_form( end # Converts values to the correct vector form for a masked grid space. -function vertex_value_form( - values::AbstractArray, num_verts::Int64, - dspace::Array{Bool, T}, sym::SymbolicT - ) where {T} +function vertex_value_form(values::AbstractArray, num_verts::Int64, + dspace::Array{Bool, T}, sym::SymbolicT) where {T} if size(values) != size(dspace) throw(ArgumentError("The values for $sym did not have the same format as the space. Expected a $(size(dspace)) array, got one of size $(size(values))")) end @@ -179,22 +173,18 @@ end # The species is represented by its index (in species(dsrs). # If the rate is uniform across all edges, the transportation rate will be a size (1,1) sparse matrix. # Else, the rate will be a size (num_verts,num_verts) sparse matrix. -function make_sidxs_to_transrate_map( - vert_ps::Vector{Pair{R, Vector{T}}}, +function make_sidxs_to_transrate_map(vert_ps::Vector{Pair{R, Vector{T}}}, edge_ps::Vector{Pair{S, SparseMatrixCSC{T, Int64}}}, - dsrs::DiscreteSpaceReactionSystem - ) where {R, S, T} + dsrs::DiscreteSpaceReactionSystem) where {R, S, T} # Creates a dictionary with each parameter's value(s). p_val_dict = Dict(vcat(vert_ps, edge_ps)) # First, compute a map from species in their symbolics form to their values. # Next, convert to map from species index to values. transport_rates_speciesmap = compute_all_transport_rates(p_val_dict, dsrs) - return Pair{Int64, SparseMatrixCSC{T, Int64}}[ - speciesmap(reactionsystem(dsrs))[spat_rates[1]] => spat_rates[2] - for spat_rates in - transport_rates_speciesmap - ] + return Pair{Int64, SparseMatrixCSC{T, Int64}}[speciesmap(reactionsystem(dsrs))[spat_rates[1]] => spat_rates[2] + for spat_rates in + transport_rates_speciesmap] end # Computes the transport rates for all species with transportation rates. Output is a map @@ -202,10 +192,8 @@ end function compute_all_transport_rates(p_val_dict, dsrs::DiscreteSpaceReactionSystem) # For all species with transportation, compute their transportation rate (across all edges). # This is a vector, pairing each species to these rates. - unsorted_rates = [ - s => compute_transport_rates(s, p_val_dict, dsrs) - for s in spatial_species(dsrs) - ] + unsorted_rates = [s => compute_transport_rates(s, p_val_dict, dsrs) + for s in spatial_species(dsrs)] # Sorts all the species => rate pairs according to their species index in species(dsrs). return sort(unsorted_rates; by = rate -> findfirst(isequal(rate[1]), species(dsrs))) @@ -219,13 +207,8 @@ function compute_transport_rates(s::SymbolicT, p_val_dict, dsrs::DiscreteSpaceRe # Find parameters involved in the rate and create a function evaluating the rate law. rate_law = get_transport_rate_law(s, dsrs) relevant_ps = Symbolics.get_variables(rate_law) - rate_law_func = drop_expr( - @RuntimeGeneratedFunction( - build_function( - rate_law, relevant_ps... - ) - ) - ) + rate_law_func = drop_expr(@RuntimeGeneratedFunction(build_function( + rate_law, relevant_ps...))) # If all these parameters are spatially uniform, the rates become a size (1,1) sparse matrix. # Else, the rates become a size (num_verts,num_verts) sparse matrix. @@ -255,7 +238,7 @@ end # Converts a vector of vectors to a single, long, vector. # These are used when the initial condition is converted to a single vector (from vector of vector form). function expand_component_values(values::Vector{<:Vector}, num_verts::Int64) - return vcat([get_vertex_value.(values, vert) for vert in 1:num_verts]...) + vcat([get_vertex_value.(values, vert) for vert in 1:num_verts]...) end # Gets the index in the u array of species s in vertex vert (when there are num_species species). @@ -269,10 +252,8 @@ end function get_edge_value(values::Vector{T}, edge::Pair{Int64, Int64}) where {T} return (length(values) == 1) ? values[1] : values[edge[1]] end -function get_edge_value( - values::SparseMatrixCSC{T, Int64}, - edge::Pair{Int64, Int64} - ) where {T} +function get_edge_value(values::SparseMatrixCSC{T, Int64}, + edge::Pair{Int64, Int64}) where {T} return (size(values) == (1, 1)) ? values[1, 1] : values[edge[1], edge[2]] end @@ -282,23 +263,18 @@ function get_vertex_value(values::Vector{T}, vert_idx::Int64) where {T} end # Finds the transport rate of a parameter along a specific edge. -function get_transport_rate( - transport_rate::SparseMatrixCSC{T, Int64}, - edge::Pair{Int64, Int64}, t_rate_idx_types::Bool - ) where {T} +function get_transport_rate(transport_rate::SparseMatrixCSC{T, Int64}, + edge::Pair{Int64, Int64}, t_rate_idx_types::Bool) where {T} return t_rate_idx_types ? transport_rate[1, 1] : transport_rate[edge[1], edge[2]] end # For a `LatticeTransportODEFunction`, update its stored parameters (in `mtk_ps`) so that they # the heterogeneous parameters' values correspond to the values in the specified vertex. -function update_mtk_ps!( - lt_ofun::LatticeTransportODEFunction, all_ps::Vector{T}, - vert::Int64 - ) where {T} +function update_mtk_ps!(lt_ofun::LatticeTransportODEFunction, all_ps::Vector{T}, + vert::Int64) where {T} for (setp, idx) in zip(lt_ofun.p_setters, lt_ofun.heterogeneous_vert_p_idxs) setp(lt_ofun.mtk_ps, all_ps[idx][vert]) end - return end # For an expression, compute its values using the provided state and parameter vectors. @@ -322,10 +298,8 @@ function compute_vertex_value(exp, dsrs::DiscreteSpaceReactionSystem; u = [], ps if all(length(value_dict[sym]) == 1 for sym in relevant_syms) return [exp_func([value_dict[sym][1] for sym in relevant_syms]...)] end - return [ - exp_func([get_vertex_value(value_dict[sym], vert_idx) for sym in relevant_syms]...) - for vert_idx in 1:num_verts(dsrs) - ] + return [exp_func([get_vertex_value(value_dict[sym], vert_idx) for sym in relevant_syms]...) + for vert_idx in 1:num_verts(dsrs)] end ### System Property Checks ### diff --git a/src/steady_state_stability.jl b/src/steady_state_stability.jl index 3d031863f5..e9a57838b8 100644 --- a/src/steady_state_stability.jl +++ b/src/steady_state_stability.jl @@ -45,10 +45,8 @@ Notes: whether a computed eigenvalue is far away enough from 0 to be reliably used. This selected threshold can be changed through the `tol` argument. """ -function steady_state_stability( - u::Vector, rs::ReactionSystem, ps; - tol = 10 * sqrt(eps(ss_val_type(u))), ss_jac = steady_state_jac(rs; u0 = u) - ) +function steady_state_stability(u::Vector, rs::ReactionSystem, ps; + tol = 10 * sqrt(eps(ss_val_type(u))), ss_jac = steady_state_jac(rs; u0 = u)) # Warning checks. if !isautonomous(rs) error("Attempting to compute stability for a non-autonomous system (e.g. where some rate depend on $(get_iv(rs))). This is not possible.") @@ -110,10 +108,8 @@ Notes: - In practice, this function returns an `ODEProblem` (with a computed Jacobian) set up in such a way that it can be used by the `steady_state_stability` function. """ -function steady_state_jac( - rs::ReactionSystem; u0 = [sp => 0.0 for sp in unknowns(rs)], - combinatoric_ratelaws = get_combinatoric_ratelaws(rs) - ) +function steady_state_jac(rs::ReactionSystem; u0 = [sp => 0.0 for sp in unknowns(rs)], + combinatoric_ratelaws = get_combinatoric_ratelaws(rs)) # If u0 is a vector of values, must be converted to something MTK understands. # Converts u0 to values MTK understands, and checks that potential conservation laws are accounted for. @@ -122,10 +118,8 @@ function steady_state_jac( # Creates an `ODEProblem` with a Jacobian. Dummy values for `u0` and `ps` must be provided. ps = [p => 0.0 for p in parameters(rs)] - return ODEProblem( - rs, u0, 0, ps; jac = true, combinatoric_ratelaws, - remove_conserved = true - ) + return ODEProblem(rs, u0, 0, ps; jac = true, combinatoric_ratelaws, + remove_conserved = true) end # Converts a `u` vector from a vector of values to a map. diff --git a/src/unit_helpers.jl b/src/unit_helpers.jl index 171e240309..d735264444 100644 --- a/src/unit_helpers.jl +++ b/src/unit_helpers.jl @@ -80,7 +80,6 @@ function Base.showerror(io::IO, err::UnitValidationError) (issue.rhs_unit === nothing) || print(io, "\nrhs unit: ", issue.rhs_unit) isempty(issue.detail) || print(io, "\ndetail: ", issue.detail) end - return end # Emit @warn messages for a vector of UnitValidationIssues. @@ -88,77 +87,41 @@ end function _warn_unit_issues(issues::Vector{UnitValidationIssue}) for issue in issues if issue.kind == :species_unit_mismatch - @warn( - string( - "Species are expected to have units of ", issue.lhs_unit, - " however, species ", issue.context, " has units ", issue.rhs_unit, "." - ) - ) + @warn(string("Species are expected to have units of ", issue.lhs_unit, + " however, species ", issue.context, " has units ", issue.rhs_unit, ".")) elseif issue.kind == :reaction_species_unit_mismatch @warn(string("In ", issue.context, " the ", issue.detail, ".")) elseif issue.kind == :reaction_rate_unit_mismatch - @warn( - string( - "Reaction rate laws are expected to have units of ", issue.lhs_unit, - " however, ", issue.context, " has units of ", issue.rhs_unit, "." - ) - ) + @warn(string( + "Reaction rate laws are expected to have units of ", issue.lhs_unit, + " however, ", issue.context, " has units of ", issue.rhs_unit, ".")) elseif issue.kind == :reaction_side_unit_mismatch - @warn( - string( - "in ", issue.context, - " the substrate units are not consistent with the product units." - ) - ) + @warn(string("in ", issue.context, + " the substrate units are not consistent with the product units.")) elseif issue.kind == :additive_term_unit_mismatch - @warn( - string( - issue.context, ": additive terms have mismatched units [", - issue.lhs_unit, "] and [", issue.rhs_unit, "]." - ) - ) + @warn(string(issue.context, ": additive terms have mismatched units [", + issue.lhs_unit, "] and [", issue.rhs_unit, "].")) elseif issue.kind == :equation_unit_mismatch - @warn( - string( - "Equation unit mismatch in ", issue.context, - ": lhs has units ", issue.lhs_unit, ", rhs has units ", issue.rhs_unit, "." - ) - ) + @warn(string("Equation unit mismatch in ", issue.context, + ": lhs has units ", issue.lhs_unit, ", rhs has units ", issue.rhs_unit, ".")) elseif issue.kind == :comparison_unit_mismatch - @warn( - string( - issue.context, ": comparison operands have mismatched units [", - issue.lhs_unit, "] and [", issue.rhs_unit, "]." - ) - ) + @warn(string(issue.context, ": comparison operands have mismatched units [", + issue.lhs_unit, "] and [", issue.rhs_unit, "].")) elseif issue.kind == :conditional_condition_unit_mismatch - @warn( - string( - issue.context, ": ifelse condition must be unitless, got [", - issue.rhs_unit, "]." - ) - ) + @warn(string(issue.context, ": ifelse condition must be unitless, got [", + issue.rhs_unit, "].")) elseif issue.kind == :conditional_branch_unit_mismatch - @warn( - string( - issue.context, ": ifelse branches have mismatched units [", - issue.lhs_unit, "] and [", issue.rhs_unit, "]." - ) - ) + @warn(string(issue.context, ": ifelse branches have mismatched units [", + issue.lhs_unit, "] and [", issue.rhs_unit, "].")) elseif issue.kind == :exponent_unit_mismatch - @warn( - string( - issue.context, ": exponent must be unitless, got [", - issue.rhs_unit, "]." - ) - ) + @warn(string(issue.context, ": exponent must be unitless, got [", + issue.rhs_unit, "].")) elseif issue.kind == :symbolic_stoichiometry @warn(string(issue.context, ": ", issue.detail, ".")) elseif issue.kind == :symbolic_exponent @warn(string(issue.context, ": ", issue.detail, ".")) end end - return end _validation_push!(issues, issue::UnitValidationIssue) = isnothing(issues) ? nothing : push!(issues, issue) @@ -180,7 +143,7 @@ from the system's brownian and poissonian variables: - Brownians get units `time^(-1/2)` (from the Wiener process derivative `dW/dt`). - Poissonians get units from their associated rate parameter (via `getpoissonianrate`). """ -function catalyst_get_unit(x, noise_units = nothing) +function catalyst_get_unit(x, noise_units=nothing) # Unwrap Symbolics wrappers (Num, CallAndWrap, etc.) to the underlying symbolic x = Symbolics.unwrap(x) if x isa DQ.AbstractQuantity @@ -197,12 +160,9 @@ end # lose precision in compound arithmetic, leading to silent unit-check failures. function _check_symbolic_dims(u::DQ.AbstractQuantity, x) if !(DQ.dimension(u) isa DQ.SymbolicDimensions) - throw( - ArgumentError( - "Unit metadata on $x uses concrete Dimensions (from u\"...\"). " * - "Catalyst requires SymbolicDimensions — use us\"...\" instead." - ) - ) + throw(ArgumentError( + "Unit metadata on $x uses concrete Dimensions (from u\"...\"). " * + "Catalyst requires SymbolicDimensions — use us\"...\" instead.")) end return u end @@ -246,7 +206,7 @@ function _cgu_symbolic(x, noise_units) _is_unitless(exp_u) || return SYM_UNITLESS exp_val = pargs[2] if SymbolicUtils.isconst(exp_val) - return base_u^value(exp_val) + return base_u ^ value(exp_val) else # Symbolic exponent on unitful base — unit is indeterminate. # Validation code pre-checks via _has_symbolic_unitful_pow before @@ -350,8 +310,8 @@ function _has_symbolic_unitful_pow(x) # Only match unitful base with unitless symbolic exponent (e.g. A^n). # Unitful exponents (e.g. V^t) are separately caught as :exponent_unit_mismatch. if !_is_unitless(catalyst_get_unit(args[1])) && - !SymbolicUtils.isconst(args[2]) && - _is_unitless(catalyst_get_unit(args[2])) + !SymbolicUtils.isconst(args[2]) && + _is_unitless(catalyst_get_unit(args[2])) return true end end @@ -368,13 +328,13 @@ Walk a symbolic expression tree and check that all additive terms have consisten units. Returns `true` if valid, `false` if any addition has terms with mismatched units. Issues warnings for each mismatch found. """ -function _validate_unit_expr(expr, label, noise_units = nothing; issues = nothing, warn::Bool = true) +function _validate_unit_expr(expr, label, noise_units=nothing; issues=nothing, warn::Bool = true) x = expr isa Symbolics.Num ? Symbolics.unwrap(expr) : expr x isa SymbolicT || return true return _vue_walk(x, label, noise_units; issues, warn) end -function _vue_walk(x, label, noise_units; issues = nothing, warn::Bool = true) +function _vue_walk(x, label, noise_units; issues=nothing, warn::Bool = true) valid = true if SymbolicUtils.isadd(x) args = SymbolicUtils.arguments(x) @@ -383,10 +343,8 @@ function _vue_walk(x, label, noise_units; issues = nothing, warn::Bool = true) other_unit = catalyst_get_unit(args[i], noise_units) if !_units_match(first_unit, other_unit) valid = false - issue = UnitValidationIssue( - :additive_term_unit_mismatch, string(label), - first_unit, other_unit, "Additive terms have mismatched units." - ) + issue = UnitValidationIssue(:additive_term_unit_mismatch, string(label), + first_unit, other_unit, "Additive terms have mismatched units.") _validation_push!(issues, issue) warn && @warn("$label: additive terms have mismatched units [$first_unit] and [$other_unit] in $x.") end @@ -404,10 +362,8 @@ function _vue_walk(x, label, noise_units; issues = nothing, warn::Bool = true) exp_unit = catalyst_get_unit(args[2], noise_units) if !_is_unitless(exp_unit) valid = false - issue = UnitValidationIssue( - :exponent_unit_mismatch, string(label), SYM_UNITLESS, - exp_unit, "Exponent must be unitless." - ) + issue = UnitValidationIssue(:exponent_unit_mismatch, string(label), SYM_UNITLESS, + exp_unit, "Exponent must be unitless.") _validation_push!(issues, issue) warn && @warn("$label: exponent has non-unitless unit [$exp_unit] in $x.") end @@ -427,10 +383,8 @@ function _vue_walk(x, label, noise_units; issues = nothing, warn::Bool = true) rhs_unit = catalyst_get_unit(args[2], noise_units) if !_units_match(lhs_unit, rhs_unit) valid = false - issue = UnitValidationIssue( - :comparison_unit_mismatch, string(label), lhs_unit, - rhs_unit, "Comparison operands must have matching units." - ) + issue = UnitValidationIssue(:comparison_unit_mismatch, string(label), lhs_unit, + rhs_unit, "Comparison operands must have matching units.") _validation_push!(issues, issue) warn && @warn("$label: comparison operands have mismatched units [$lhs_unit] and [$rhs_unit] in $x.") end @@ -439,10 +393,8 @@ function _vue_walk(x, label, noise_units; issues = nothing, warn::Bool = true) cond_unit = catalyst_get_unit(args[1], noise_units) if !_is_unitless(cond_unit) valid = false - issue = UnitValidationIssue( - :conditional_condition_unit_mismatch, string(label), - SYM_UNITLESS, cond_unit, "ifelse condition must be unitless." - ) + issue = UnitValidationIssue(:conditional_condition_unit_mismatch, string(label), + SYM_UNITLESS, cond_unit, "ifelse condition must be unitless.") _validation_push!(issues, issue) warn && @warn("$label: ifelse condition is not unitless [$cond_unit] in $x.") end @@ -450,10 +402,8 @@ function _vue_walk(x, label, noise_units; issues = nothing, warn::Bool = true) false_unit = catalyst_get_unit(args[3], noise_units) if !_units_match(true_unit, false_unit) valid = false - issue = UnitValidationIssue( - :conditional_branch_unit_mismatch, string(label), - true_unit, false_unit, "ifelse branches must have matching units." - ) + issue = UnitValidationIssue(:conditional_branch_unit_mismatch, string(label), + true_unit, false_unit, "ifelse branches must have matching units.") _validation_push!(issues, issue) warn && @warn("$label: ifelse branches have mismatched units [$true_unit] and [$false_unit] in $x.") end @@ -479,22 +429,16 @@ Validate units in an `Equation`. Checks: The optional `noise_units` keyword is a `Dict` mapping noise variable symbols to their effective units (built by `_build_noise_units` during system validation). """ -function _validate_equation(eq::Equation; noise_units = nothing, issues = nothing, warn::Bool = true) +function _validate_equation(eq::Equation; noise_units=nothing, issues=nothing, warn::Bool = true) valid = true eq_str = string(eq) if _has_symbolic_unitful_pow(eq.lhs) || _has_symbolic_unitful_pow(eq.rhs) valid = false - issue = UnitValidationIssue( - :symbolic_exponent, eq_str, nothing, nothing, - "Symbolic exponent on unitful base is not supported for unit validation" - ) + issue = UnitValidationIssue(:symbolic_exponent, eq_str, nothing, nothing, + "Symbolic exponent on unitful base is not supported for unit validation") _validation_push!(issues, issue) - warn && @warn( - string( - eq_str, ": symbolic exponent on unitful base, ", - "cannot validate units." - ) - ) + warn && @warn(string(eq_str, ": symbolic exponent on unitful base, ", + "cannot validate units.")) return valid end valid &= _validate_unit_expr(eq.lhs, string(eq_str, ": lhs"), noise_units; issues, warn) @@ -503,17 +447,11 @@ function _validate_equation(eq::Equation; noise_units = nothing, issues = nothin rhs_unit = catalyst_get_unit(eq.rhs, noise_units) if !_units_match(lhs_unit, rhs_unit) valid = false - issue = UnitValidationIssue( - :equation_unit_mismatch, string(eq), lhs_unit, - rhs_unit, "LHS and RHS units do not match." - ) + issue = UnitValidationIssue(:equation_unit_mismatch, string(eq), lhs_unit, + rhs_unit, "LHS and RHS units do not match.") _validation_push!(issues, issue) - warn && @warn( - string( - "Equation unit mismatch in ", eq, - ": lhs has units ", lhs_unit, ", rhs has units ", rhs_unit, "." - ) - ) + warn && @warn(string("Equation unit mismatch in ", eq, + ": lhs has units ", lhs_unit, ", rhs has units ", rhs_unit, ".")) end return valid end @@ -532,7 +470,7 @@ function _build_noise_units(rs) timeunit = catalyst_get_unit(get_iv(rs)) noise_units = Dict{Any, Any}() for b in MT.get_brownians(rs) - noise_units[Symbolics.unwrap(b)] = timeunit^(-1 // 2) + noise_units[Symbolics.unwrap(b)] = timeunit^(-1//2) end for p in MT.get_poissonians(rs) rate = MT.getpoissonianrate(p) diff --git a/test/dsl/dsl_basic_model_construction.jl b/test/dsl/dsl_basic_model_construction.jl index 00eed3051a..1a321d6478 100644 --- a/test/dsl/dsl_basic_model_construction.jl +++ b/test/dsl/dsl_basic_model_construction.jl @@ -3,7 +3,7 @@ # Fetch packages. using DiffEqBase, Catalyst, Random, Test using ModelingToolkitBase: operation, get_unknowns, get_ps, get_eqs, get_systems, - get_iv, nameof + get_iv, nameof using Symbolics: iscall # Sets stable rng number. @@ -20,7 +20,7 @@ include("../test_functions.jl") ### Declares Testing Functions ### function unpacksys(sys) - return get_eqs(sys), get_iv(sys), get_unknowns(sys), get_ps(sys), nameof(sys), get_systems(sys) + get_eqs(sys), get_iv(sys), get_unknowns(sys), get_ps(sys), nameof(sys), get_systems(sys) end opname(x) = iscall(x) ? nameof(operation(x)) : nameof(x) @@ -50,7 +50,7 @@ function basic_test(rn, N, unknowns_syms, p_syms) @test issetequal(map(opname, unknowns), unknowns_syms) @test all_reactants(eqs) == Set(unknowns_syms) @test length(ps) == length(p_syms) - return @test issetequal(map(opname, ps), p_syms) + @test issetequal(map(opname, ps), p_syms) end ## Run Tests ### @@ -116,35 +116,23 @@ end # Test basic properties of networks. let - basic_test( - reaction_networks_standard[1], 10, [:X1, :X2, :X3], - [:p1, :p2, :p3, :k1, :k2, :k3, :k4, :d1, :d2, :d3] - ) + 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]) + 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] - ) + 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_conserved[1], 6, [:X1, :X2, :X3], - [:k1, :k2, :k3, :k4, :k5, :k6] - ) + 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_conserved[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]) @@ -191,13 +179,13 @@ let different_arrow_8 = @reaction_network begin p, 2X1 < ∅ k1, X2 ← X1 - (k2, k3), X3 ⟻ (X2, X2) + (k2, k3), X3 ⟻ (X2,X2) d, ∅ ↼ X3 end push!(identical_networks_1, reaction_networks_standard[8] => different_arrow_8) for networks in identical_networks_1 - for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2, 1.0e3] + for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] u0 = rnd_u0(networks[1], rng; factor) p = rnd_ps(networks[1], rng; factor) t = rand(rng) @@ -223,14 +211,12 @@ let # 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 [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2, 1.0e3] + 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], - ] + 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) @@ -281,7 +267,7 @@ let push!(identical_networks_2, reaction_networks_standard[7] => differently_written_8) for networks in identical_networks_2 - for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2, 1.0e3] + for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] u0 = rnd_u0(networks[1], rng; factor) p = rnd_ps(networks[1], rng; factor) t = rand(rng) @@ -307,32 +293,24 @@ 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, [ - :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), - ] - ) + 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, X1) → X2 - (0.0, 2.1), X2 → (X3, X3) - (901.0, 63.5), (X3, X3) → (X4, X4) + (3.1, 3.2), (X1,X1) → X2 + (0.0, 2.1), X2 → (X3,X3) + (901.0, 63.5), (X3,X3) → (X4,X4) 7, X4 → X5 8, X4 → X5 1.0, X5 ⟶ ∅ end push!(identical_networks_3, reaction_networks_standard[10] => 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, - ] - ) + 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 (networks, p_1) in zip(identical_networks_3, parameter_sets) - for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2, 1.0e3] + for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] u0 = rnd_u0(networks[1], rng; factor) t = rand(rng) @@ -349,33 +327,27 @@ let @parameters v1 K1 v2 K2 k1 k2 k3 k4 k5 p d @species X1(t) X2(t) X3(t) X4(t) X5(t) - rxs_1 = [ - Reaction(p, nothing, [X1], nothing, [2]), + 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), - ] + 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]), + 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), - ] + 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_conserved[3] => rs_2) - rxs_3 = [ - Reaction(k1, [X1], [X2], [1], [1]), + 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]), - ] + 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) @@ -384,7 +356,7 @@ let @test alleq(get_unknowns(networks[1]), get_unknowns(networks[2])) @test alleq(get_ps(networks[1]), get_ps(networks[2])) @test ModelingToolkitBase.get_systems(networks[1]) == - ModelingToolkitBase.get_systems(networks[2]) + ModelingToolkitBase.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) @@ -408,14 +380,12 @@ let (t, k6), X3 ↔ X1 end - for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2, 1.0e3] + for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3] τ = rand(rng) u = rnd_u0(reaction_networks_conserved[1], rng; factor) p_2 = rnd_ps(time_network, rng; factor) - p_1 = [ - p_2; reaction_networks_conserved[1].k1 => τ; - reaction_networks_conserved[1].k4 => τ; reaction_networks_conserved[1].k5 => τ - ] + p_1 = [p_2; reaction_networks_conserved[1].k1 => τ; + reaction_networks_conserved[1].k4 => τ; reaction_networks_conserved[1].k5 => τ] @test f_eval(reaction_networks_conserved[1], u, p_1, τ) ≈ f_eval(time_network, u, p_2, τ) @test jac_eval(reaction_networks_conserved[1], u, p_1, τ) ≈ jac_eval(time_network, u, p_2, τ) @@ -440,9 +410,7 @@ let (m, M), z ⟷ Z end - @reaction_network begin - (1.0, 1.0), i ⟷ T - end + @reaction_network begin (1.0, 1.0), i ⟷ T end @reaction_network begin (å, Å), ü ⟷ Ü @@ -535,29 +503,21 @@ end # Test that symbols with special meanings are handled properly. let - test_network = @reaction_network begin - t * k, X --> ∅ - end + 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_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_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_network = @reaction_network begin ℯ, X --> ∅ end @test length(species(test_network)) == 1 @test length(parameters(test_network)) == 0 @test reactions(test_network)[1].rate == ℯ @@ -568,10 +528,10 @@ end # Erroneous `@reaction` usage. let # Bi-directional reaction using the `@reaction` macro. - @test_throws Exception @eval @reaction (k1, k2), X1 <--> X2 + @test_throws Exception @eval @reaction (k1,k2), X1 <--> X2 # Bundles reactions. - @test_throws Exception @eval @reaction k, (X1, X2) --> 0 + @test_throws Exception @eval @reaction k, (X1,X2) --> 0 end # Tests that malformed reactions yields errors. @@ -581,12 +541,12 @@ let d, X --> 0, Y --> 0 end @test_throws Exception @eval @reaction_network begin - d, X --> 0, [misc = "Ok metadata"], [description = "Metadata in (erroneously) extra []."] + d, X --> 0, [misc="Ok metadata"], [description="Metadata in (erroneously) extra []."] end # Checks that incorrect bundling yields error. @test_throws Exception @eval @reaction_network begin - (k1, k2, k3), (X1, X2) --> 0 + (k1,k2,k3), (X1,X2) --> 0 end # Checks that incorrect stoichiometric expression yields an error. @@ -613,41 +573,19 @@ let @test_throws Exception @eval @reaction 1, 0 --> t # @reaction_network macro, symbols that cannot be in the rate. - @test_throws Exception @eval @reaction_network begin - im, 0 --> X - end - @test_throws Exception @eval @reaction_network begin - nothing, 0 --> X - end - @test_throws Exception @eval @reaction_network begin - Γ, 0 --> X - end - @test_throws Exception @eval @reaction_network begin - ∅, 0 --> X - end + @test_throws Exception @eval @reaction_network begin im, 0 --> X end + @test_throws Exception @eval @reaction_network begin nothing, 0 --> X end + @test_throws Exception @eval @reaction_network begin Γ, 0 --> X end + @test_throws Exception @eval @reaction_network begin ∅, 0 --> X end # @reaction_network macro, symbols that cannot be a reactant. - @test_throws Exception @eval @reaction_network begin - 1, 0 --> im - end - @test_throws Exception @eval @reaction_network begin - 1, 0 --> nothing - end - @test_throws Exception @eval @reaction_network begin - 1, 0 --> Γ - end - @test_throws Exception @eval @reaction_network begin - 1, 0 --> ℯ - end - @test_throws Exception @eval @reaction_network begin - 1, 0 --> pi - end - @test_throws Exception @eval @reaction_network begin - 1, 0 --> π - end - @test_throws Exception @eval @reaction_network begin - 1, 0 --> t - end + @test_throws Exception @eval @reaction_network begin 1, 0 --> im end + @test_throws Exception @eval @reaction_network begin 1, 0 --> nothing end + @test_throws Exception @eval @reaction_network begin 1, 0 --> Γ end + @test_throws Exception @eval @reaction_network begin 1, 0 --> ℯ end + @test_throws Exception @eval @reaction_network begin 1, 0 --> pi end + @test_throws Exception @eval @reaction_network begin 1, 0 --> π end + @test_throws Exception @eval @reaction_network begin 1, 0 --> t end # Checks that non-supported arrow type usage yields error. @test_throws Exception @eval @reaction_network begin @@ -659,15 +597,15 @@ end # Checks for two, more than two, and for functional rates. Uses different arrow types and directions. let @test_throws Exception @eval @reaction_network begin - (k1, k2), X --> 0 + (k1,k2), X --> 0 end @test_throws Exception @eval @reaction_network begin - (k1, k2, k3), X → 0 + (k1,k2,k3), X → 0 end @test_throws Exception @eval @reaction_network begin - (k1, k1), X => Y + (k1,k1), X => Y end @test_throws Exception @eval @reaction_network begin - (mm(X, v1, K1), mm(X, v2, K2)), 0 <-- 0 + (mm(X,v1,K1),mm(X,v2,K2)), 0 <-- 0 end end diff --git a/test/extensions/bifurcation_kit.jl b/test/extensions/bifurcation_kit.jl index ec30219d70..9ccdff6eee 100644 --- a/test/extensions/bifurcation_kit.jl +++ b/test/extensions/bifurcation_kit.jl @@ -26,7 +26,7 @@ let 1, 2X + Y → 3X B, X → Y 1, X → ∅ - (k1 * Y, k2), V <--> W + (k1*Y, k2), V <--> W end @unpack A, B, k1 = extended_brusselator u0_guess = [:X => 1.0, :Y => 1.0, :V => 0.0, :W => 0.0] @@ -41,15 +41,15 @@ let # Checks computed V values are correct (Formula: V = k2*(V0+W0)/(k1*Y+k2), where Y=2*B.) B_vals = getfield.(bif_dia.γ.branch, :param) V_vals = getfield.(bif_dia.γ.branch, :x) - @test all(SymbolicUtils.unwrap_const.(V_vals) .≈ 0.5 * (1.0 + 2.0) ./ (0.1 .* 2 * B_vals .+ 0.5)) + @test all(SymbolicUtils.unwrap_const.(V_vals) .≈ 0.5*(1.0+2.0) ./ (0.1 .* 2*B_vals .+ 0.5)) # Checks that the bifurcation point is correct. @test length(bif_dia.γ.specialpoint) == 3 # Includes start and end point. hopf_bif_point = filter(sp -> sp.type == :hopf, bif_dia.γ.specialpoint)[1] - @test isapprox(hopf_bif_point.param, 1.5, atol = 1.0e-5) + @test isapprox(hopf_bif_point.param, 1.5, atol=1e-5) # Tests that an error is thrown if information of conserved species is not fully provided. - @test_throws Exception BifurcationProblem(extended_brusselator, u0_guess, p_start, :B; plot_var = :V, u0 = []) + @test_throws Exception BifurcationProblem(extended_brusselator, u0_guess, p_start, :B; plot_var=:V, u0 = []) end # Bistable switch. @@ -62,26 +62,26 @@ end let # Creates BifurcationProblem via Catalyst. bistable_switch = @reaction_network begin - 0.1 + hill(X, 5.0, K, 3), 0 --> X + 0.1 + hill(X,5.0,K,3), 0 --> X 1.0, X --> 0 end @unpack X, K = bistable_switch u0_guess = [X => 1.0] p_start = [K => 2.5] - bprob = BifurcationProblem(bistable_switch, u0_guess, p_start, K; jac = false, plot_var = X) + bprob = BifurcationProblem(bistable_switch, u0_guess, p_start, K; jac=false, plot_var=X) # Creates BifurcationProblem via BifurcationKit. function bistable_switch_BK(u, p) X, = u K, = p - return [0.1 + 5.0 * (X^3) / (X^3 + K^3) - 1.0 * X] + return [0.1 + 5.0*(X^3)/(X^3 + K^3) - 1.0*X] end bprob_BK = BifurcationProblem(bistable_switch_BK, [1.0], [2.5], (BifurcationKit.@optic _[1]); record_from_solution = (x, p; k...) -> x[1]) # Check the same function have been generated. bprob.u0 == bprob_BK.u0 bprob.params == bprob_BK.params - for repeat in 1:20 + for repeat = 1:20 u0 = rand(rng, 1) p = rand(rng, 1) @test bprob_BK.VF.F(u0, p) ≈ bprob.VF.F(u0, p) @@ -92,19 +92,19 @@ end # Tests with defaults within nested networks. let rn1 = @network_component rn1 begin - @parameters p = 1.0 + @parameters p=1.0 (p, d), 0 <--> X end rn2 = @network_component rn2 begin - @parameters p = 2.0 + @parameters p=2.0 (p, d), 0 <--> X end rn3 = @network_component rn3 begin - @parameters p = 3.0 + @parameters p=3.0 (p, d), 0 <--> X end rn4 = @network_component rn4 begin - @parameters p = 4.0 + @parameters p=4.0 (p, d), 0 <--> X end @named rn3 = compose(rn3, [rn4]) @@ -125,25 +125,25 @@ let bprob = BifurcationProblem(rn, u0_guess, p_start, d; plot_var = X) opts_br = ContinuationPar(dsmin = 0.0001, dsmax = 0.001, ds = 0.0001, max_steps = 10000, p_min = p_span[1], p_max = p_span[2], n_inversion = 4) bif_dia = bifurcationdiagram(bprob, PALC(), 2, opts_br; bothside = true) - @test bif_dia.γ.branch[end].x ≈ 1.0 / 6 + @test bif_dia.γ.branch[end].x ≈ 1.0/6 # Checks second layer (1). bprob = BifurcationProblem(rn, u0_guess, p_start, rn2.d; plot_var = rn2.X) opts_br = ContinuationPar(dsmin = 0.0001, dsmax = 0.001, ds = 0.0001, max_steps = 10000, p_min = p_span[1], p_max = p_span[2], n_inversion = 4) bif_dia = bifurcationdiagram(bprob, PALC(), 2, opts_br; bothside = true) - @test bif_dia.γ.branch[end].x ≈ 2.0 / 6 + @test bif_dia.γ.branch[end].x ≈ 2.0/6 # Checks second layer (2). bprob = BifurcationProblem(rn, u0_guess, p_start, rn3.d; plot_var = rn3.X) opts_br = ContinuationPar(dsmin = 0.0001, dsmax = 0.001, ds = 0.0001, max_steps = 10000, p_min = p_span[1], p_max = p_span[2], n_inversion = 4) bif_dia = bifurcationdiagram(bprob, PALC(), 2, opts_br; bothside = true) - @test bif_dia.γ.branch[end].x ≈ 3.0 / 6 + @test bif_dia.γ.branch[end].x ≈ 3.0/6 # Checks third layer. bprob = BifurcationProblem(rn, u0_guess, p_start, rn3.rn4.d; plot_var = rn3.rn4.X) opts_br = ContinuationPar(dsmin = 0.0001, dsmax = 0.001, ds = 0.0001, max_steps = 10000, p_min = p_span[1], p_max = p_span[2], n_inversion = 4) bif_dia = bifurcationdiagram(bprob, PALC(), 2, opts_br; bothside = true) - @test bif_dia.γ.branch[end].x ≈ 4.0 / 6 + @test bif_dia.γ.branch[end].x ≈ 4.0/6 end end @@ -191,7 +191,7 @@ let @species X1(t) X2(t) rxs = [ Reaction(k1, [X1], [X2]), - Reaction(k2, [X2], [X1]), + Reaction(k2, [X2], [X1]) ] @named rs = ReactionSystem(rxs, t) rs = complete(rs) @@ -222,10 +222,10 @@ let @parameters k @variables C(t) @equations begin - D(V) ~ k * X - V - 0 ~ X / V - C + D(V) ~ k*X - V + 0 ~ X/V - C end - (p / V, d / V), 0 <--> X + (p/V,d/V), 0 <--> X end u0_guess = [:X => 1.0, :V => 1.0, :C => 1.0] p_start = [:p => 2.0, :d => 1.0, :k => 5.0] @@ -255,7 +255,7 @@ end let # Create t-dependant model. rn = @reaction_network begin - (p / t, d), 0 <--> X + (p/t,d), 0 <--> X end u0_guess = [:X => 1.0] p_start = [:p => 1.0, :d => 0.2] @@ -272,15 +272,15 @@ let end rn = complete(rn) - u0_guess = [:A => 1.0, :B => 1.0] - p_start = [:k => 2.0] + u0_guess = [:A => 1., :B => 1.] + p_start = [:k => 2.] - # Computes bifurcation diagram and checks that it can be successfully plotted. - bprob = BifurcationProblem(rn, u0_guess, p_start, :k; plot_var = :A, u0 = [:A => 5.0, :B => 3.0]) + # Computes bifurcation diagram and checks that it can be sucesfully plotted. + bprob = BifurcationProblem(rn, u0_guess, p_start, :k; plot_var = :A, u0 = [:A => 5., :B => 3.]) p_span = (0.1, 6.0) opts_br = ContinuationPar(dsmin = 0.0001, dsmax = 0.001, ds = 0.0001, max_steps = 10000, p_min = p_span[1], p_max = p_span[2], n_inversion = 4) bif_dia = bifurcationdiagram(bprob, PALC(), 2, opts_br; bothside = true) - plot(bif_dia, xlabel = "k", ylabel = "A", xlims = (0, 6), ylims = (0, 8)) + plot(bif_dia, xlabel = "k", ylabel = "A", xlims = (0, 6), ylims=(0,8)) # Checks that the correct values are computed in the bifurcation diagram. As = getfield.(bif_dia.γ.branch, :x) diff --git a/test/extensions/dspace_simulation_plotting.jl b/test/extensions/dspace_simulation_plotting.jl index f066177714..c577a40531 100644 --- a/test/extensions/dspace_simulation_plotting.jl +++ b/test/extensions/dspace_simulation_plotting.jl @@ -14,7 +14,7 @@ using JumpProcesses, OrdinaryDiffEqTsit5, Test let # Creates the `DiscreteSpaceReactionSystem` model. rs = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X end diffusion_rx = @transport_reaction D X for space in [CartesianGrid(3), [true, true, false]] @@ -41,7 +41,7 @@ let # Plots the kymograph and checks that a stored value is correct. fig, ax, hm = dspace_kymograph(sol, :X, dsrs) - @test_broken hm[3].val[end, 1] ≈ sol.u[end][1] # Interface for accessing internals in makie plots have changed. Need to update test. + @test_broken hm[3].val[end,1] ≈ sol.u[end][1] # Interface for accessing internals in makie plots have changed. Need to update test. end end end @@ -53,10 +53,10 @@ end let # Creates the `DiscreteSpaceReactionSystem` model. rs = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X end diffusion_rx = @transport_reaction D X - for space in [CartesianGrid((2, 2)), [true true; false true]] + for space in [CartesianGrid((2,2)), [true true; false true]] dsrs = DiscreteSpaceReactionSystem(rs, [diffusion_rx], space) # Simulates the model (using ODE and jumps). @@ -87,7 +87,7 @@ let d, X --> 0 end diffusion_rx = @transport_reaction D X - space = CartesianGrid((2, 2, 2)) + space = CartesianGrid((2,2,2)) dsrs = DiscreteSpaceReactionSystem(rs, [diffusion_rx], space) oprob = ODEProblem(dsrs, [:X => 1.0], 1.0, [:d => 1.0, :D => 0.2]) osol = solve(oprob, Tsit5()) @@ -103,7 +103,7 @@ end let # Creates the `DiscreteSpaceReactionSystem` model. rs = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X end diffusion_rx = @transport_reaction D X space = Graphs.SimpleGraphs.cycle_graph(4) diff --git a/test/extensions/graphmakie.jl b/test/extensions/graphmakie.jl index c2eb52d816..5c3de42b23 100644 --- a/test/extensions/graphmakie.jl +++ b/test/extensions/graphmakie.jl @@ -12,21 +12,17 @@ let srg = Catalyst.species_reaction_graph(brusselator) s = length(species(brusselator)) - edgel = Graphs.Edge.( - [ - (s + 1, 1), - (1, s + 2), - (2, s + 2), - (s + 2, 1), - (s + 3, 2), - (1, s + 3), - (1, s + 4), - ] - ) + edgel = Graphs.Edge.([(s+1, 1), + (1, s+2), + (2, s+2), + (s+2, 1), + (s+3, 2), + (1, s+3), + (1, s+4)]) @test all(∈(collect(Graphs.edges(srg))), edgel) MAPK = @reaction_network MAPK begin - (k₁, k₂), KKK + E1 <--> KKKE1 + (k₁, k₂),KKK + E1 <--> KKKE1 k₃, KKKE1 --> KKK_ + E1 (k₄, k₅), KKK_ + E2 <--> KKKE2 k₆, KKKE2 --> KKK + E2 @@ -36,7 +32,7 @@ let k₁₂, KKPKKK_ --> KKPP + KKK_ (k₁₃, k₁₄), KKP + KKPase <--> KKPKKPase k₁₅, KKPPKKPase --> KKP + KKPase - k₁₆, KKPKKPase --> KK + KKPase + k₁₆,KKPKKPase --> KK + KKPase (k₁₇, k₁₈), KKPP + KKPase <--> KKPPKKPase (k₁₉, k₂₀), KKPP + K <--> KKPPK k₂₁, KKPPK --> KKPP + KP @@ -81,9 +77,9 @@ let srg = CGME.SRGraphWrap(rn) s = length(species(rn)) @test ne(srg) == 8 - @test Graphs.Edge(2, s + 3) ∈ srg.multiedges + @test Graphs.Edge(2, s+3) ∈ srg.multiedges # Since B is both a dep and a reactant - @test count(==(Graphs.Edge(2, s + 3)), edges(srg)) == 2 + @test count(==(Graphs.Edge(2, s+3)), edges(srg)) == 2 f = plot_network(rn) save("fig.png", f) @@ -103,13 +99,13 @@ let s = length(species(rn)) @test ne(srg) == 8 # Since A, B is both a dep and a reactant - @test count(==(Graphs.Edge(1, s + 2)), edges(srg)) == 2 - @test count(==(Graphs.Edge(2, s + 3)), edges(srg)) == 2 + @test count(==(Graphs.Edge(1, s+2)), edges(srg)) == 2 + @test count(==(Graphs.Edge(2, s+3)), edges(srg)) == 2 end function test_edgeorder(rn) # The initial edgelabels in `plot_complexes` is given by the order of reactions in reactions(rn). - D = incidencemat(rn; sparse = true) + D = incidencemat(rn; sparse=true) rxs = reactions(rn) edgelist = Vector{Graphs.SimpleEdge{Int}}() rows = rowvals(D) @@ -150,7 +146,7 @@ let (k2, k3), C <--> D k4, A --> B hillr(D, α, K, n), C --> D - k5 * B, A --> B + k5*B, A --> B end rxorder = test_edgeorder(rn) edgelabels = [repr(rx.rate) for rx in reactions(rn)] diff --git a/test/extensions/homotopy_continuation.jl b/test/extensions/homotopy_continuation.jl index d76a54ccc4..0657aba6ab 100644 --- a/test/extensions/homotopy_continuation.jl +++ b/test/extensions/homotopy_continuation.jl @@ -17,8 +17,8 @@ include("../test_functions.jl") let # Creates the model. rs = @reaction_network begin - (k1, k2), X1 <--> X2 - (k3, k4), 2X2 + X3 <--> X2_2X3 + (k1,k2), X1 <--> X2 + (k3,k4), 2X2 + X3 <--> X2_2X3 end @unpack k1, k2, k3, k4 = rs ps = [k1 => 1.0, k2 => 2.0, k3 => 2.0, k4 => 2.0] @@ -27,7 +27,7 @@ let # 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, seed = 0x000004d1) hc_ss = Pair.(unknowns(rs), hc_ss[1]) - @test maximum(abs.(f_eval(rs, hc_ss, ps, 0.0))) ≈ 0.0 atol = 1.0e-12 + @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) @@ -47,7 +47,7 @@ let ps = [:k3 => 1.0, :k2 => 2.0, :k4 => 1.5, :k1 => 8.0] hc_ss_1 = hc_steady_states(wilhelm_2009_model, ps; seed = 0x000004d1, show_progress = false) - @test sort(hc_ss_1, by = sol -> sol[1]) ≈ [[0.0, 0.0], [0.5, 2.0], [4.5, 6.0]] + @test sort(hc_ss_1, by = sol->sol[1]) ≈ [[0.0, 0.0], [0.5, 2.0], [4.5, 6.0]] hc_ss_2 = hc_steady_states(wilhelm_2009_model, ps; seed = 0x000004d2, show_progress = false) hc_ss_3 = hc_steady_states(wilhelm_2009_model, ps; seed = 0x000004d2, show_progress = false) @@ -61,28 +61,28 @@ end # Tests where input ps/u0 are tuples with mixed types. 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 - (kX1, kX2), X1 <--> X2 - (kY1, kY2), Y1 <--> Y2 - (kZ1, kZ2), Z1 <--> Z2 + @parameters kX1=1.0 kX2=2.0 kY1=12345.0 + @species X1(t)=0.1 X2(t)=0.2 Y1(t)=12345.0 + (kX1,kX2), X1 <--> X2 + (kY1,kY2), Y1 <--> Y2 + (kZ1,kZ2), Z1 <--> Z2 end ps = (:kY1 => 1.0, :kY2 => 3, :kZ1 => 1.0, :kZ2 => 4.0) u0_1 = (:Y1 => 1.0, :Y2 => 3, :Z1 => 10, :Z2 => 40.0) - ss_1 = sort(hc_steady_states(rs_1, ps; u0 = u0_1, show_progress = false, seed = 0x000004d1), by = sol -> sol[1]) + ss_1 = sort(hc_steady_states(rs_1, ps; u0 = u0_1, show_progress = false, seed = 0x000004d1), by = sol->sol[1]) @test ss_1 ≈ [[0.2, 0.1, 3.0, 1.0, 40.0, 10.0]] rs_2 = @reaction_network begin - @parameters kX1 = 1.0 kX2 = 2.0 kY1 = 12345.0 - @species C2(t) = 0.1 C1(t) = 0.2 B2(t) = 12345.0 - (kX1, kX2), C2 <--> C1 - (kY1, kY2), B2 <--> B1 - (kZ1, kZ2), A2 <--> A1 + @parameters kX1=1.0 kX2=2.0 kY1=12345.0 + @species C2(t)=0.1 C1(t)=0.2 B2(t)=12345.0 + (kX1,kX2), C2 <--> C1 + (kY1,kY2), B2 <--> B1 + (kZ1,kZ2), A2 <--> A1 end - u0_2 = [:B2 => 1.0, :B1 => 3.0, :A2 => 10.0, :A1 => 40.0] + u0_2 = [:B2 => 1.0, :B1 => 3.0, :A2 => 10.0, :A1 =>40.0] - ss_2 = sort(hc_steady_states(rs_2, ps; u0 = u0_2, show_progress = false, seed = 0x000004d1), by = sol -> sol[1]) + ss_2 = sort(hc_steady_states(rs_2, ps; u0 = u0_2, show_progress = false, seed = 0x000004d1), by = sol->sol[1]) @test ss_1 ≈ ss_2 end @@ -93,7 +93,7 @@ end # Tests than non-integer exponents throws an error. let rs = @reaction_network begin - v * (0.1 / v + hill(X, 1, K, n)), 0 --> X + v*(0.1/v + hill(X,1,K,n)), 0 --> X d, X --> 0 end ps = Dict([:v => 5.0, :K => 2.5, :n => 3, :d => 1.0]) @@ -101,7 +101,7 @@ let @test length(sss) == 4 for ss in sss - @test ps[:v] * (0.1 / ps[:v] + ss[1]^ps[:n] / (ss[1]^ps[:n] + ps[:K]^ps[:n])) - ps[:d] * ss[1] ≈ 0.0 atol = 1.0e-12 + @test ps[:v]*(0.1/ps[:v] + ss[1]^ps[:n]/(ss[1]^ps[:n] + ps[:K]^ps[:n])) - ps[:d]*ss[1]≈ 0.0 atol = 1e-12 end ps = [:v => 5.0, :K => 2.5, :n => 2.7, :d => 1.0] @@ -114,7 +114,7 @@ end let # Finds the model steady states. rs = @reaction_network begin - 0.01 + hillar(X, Y, 1.0, Kx, 3), ∅ --> X + 0.01 + hillar(X,Y,1.0,Kx,3), ∅ --> X 0.01 + (Y^3) / (X^3 + Y^3 + Ky^3), ∅ --> Y 1.0, (X, Y) --> ∅ end @@ -125,7 +125,7 @@ let @test length(sss) == 5 @test allunique(sss) for ss in sss - @test f_eval(rs, Pair.(unknowns(rs), ss), ps, 0.0) ≈ [0.0, 0.0] atol = 1.0e-12 rtol = 1.0e-12 + @test f_eval(rs, Pair.(unknowns(rs), ss), ps, 0.0) ≈ [0.0, 0.0] atol = 1e-12 rtol = 1e-12 end end @@ -138,10 +138,10 @@ let @parameters k @variables C(t) @equations begin - D(V) ~ k * X - V - 0 ~ X / V - C + D(V) ~ k*X - V + 0 ~ X/V - C end - (p / V, d / V), 0 <--> X + (p/V,d/V), 0 <--> X end # Checks that homotopy continuation correctly find the system's single steady state. @@ -165,7 +165,7 @@ end # Tests that non-autonomous system throws an error let rs = @reaction_network begin - (k, t), 0 <--> X + (k,t), 0 <--> X end ps = [:k => 1.0] @test_throws Exception hc_steady_states(rs, ps; show_progress = false, seed = 0x000004d1) diff --git a/test/extensions/stability_computation.jl b/test/extensions/stability_computation.jl index a9adcc762a..7d58cd5ff2 100644 --- a/test/extensions/stability_computation.jl +++ b/test/extensions/stability_computation.jl @@ -16,19 +16,17 @@ rng = StableRNG(12345) let # System which may have between 1 and 7 fixed points. rn = @reaction_network begin - v / 20.0 + hillar(X, Y, v, K, n), 0 --> X - v / 20.0 + hillar(Y, X, v, K, n), 0 --> Y - d, (X, Y) --> 0 + v/20.0 + hillar(X,Y,v,K,n), 0 --> X + v/20.0 + hillar(Y,X,v,K,n), 0 --> Y + d, (X,Y) --> 0 end ss_jac = steady_state_jac(rn) # Repeats several times, most steady state stability cases should be encountered several times. - for repeat in 1:20 + for repeat = 1:20 # Generates random parameter values (which can generate all steady states cases). - ps = ( - :v => 1.0 + 3 * rand(rng), :K => 0.5 + 2 * rand(rng), :n => rand(rng, [1, 2, 3, 4]), - :d => 0.5 + rand(rng), - ) + ps = (:v => 1.0 + 3*rand(rng), :K => 0.5 + 2*rand(rng), :n => rand(rng,[1,2,3,4]), + :d => 0.5 + rand(rng)) # Computes stability using various jacobian options. sss = hc_steady_states(rn, ps; show_progress = false) @@ -38,8 +36,8 @@ let # Confirms stability using simulations. for (idx, ss) in enumerate(sss) ssprob = SteadyStateProblem(rn, Pair.(unknowns(rn), [1.001, 0.999] .* ss), ps) - sol = solve(ssprob, DynamicSS(Vern7()); abstol = 1.0e-8, reltol = 1.0e-8) - stabs_3 = isapprox(ss, sol.u; atol = 1.0e-6) + sol = solve(ssprob, DynamicSS(Vern7()); abstol = 1e-8, reltol = 1e-8) + stabs_3 = isapprox(ss, sol.u; atol = 1e-6) @test stabs_1[idx] == stabs_2[idx] == stabs_3 # Checks stability when steady state is given on a pair form ([X => x_val, Y => y_val]). @@ -55,11 +53,11 @@ end let # Creates model. rn = @reaction_network begin - k1 + Z, Y --> 2X + k1+Z, Y --> 2X k2, 2X --> X + Y k3, X + Y --> Y k4, X --> 0 - (kD1 + X, kD2), 2Z <--> Z2 + (kD1+X, kD2), 2Z <--> Z2 end # Creates various forms of input. @@ -75,9 +73,9 @@ let # Computes stability using various input forms, and checks that the output is correct. sss = hc_steady_states(rn, ps_1; u0 = u0_1, show_progress = false) for u0 in [u0_1, u0_2, u0_3, u0_4], ps in [ps_1, ps_2, ps_3] - stab_1 = [steady_state_stability(ss, rn, ps) for ss in sss] + stab_1 = [steady_state_stability(ss, rn, ps) for ss in sss] ss_jac = steady_state_jac(rn; u0 = u0) - stab_2 = [steady_state_stability(ss, rn, ps; ss_jac = ss_jac) for ss in sss] + stab_2 = [steady_state_stability(ss, rn, ps; ss_jac = ss_jac) for ss in sss] @test length(stab_1) == length(stab_2) == 3 @test count(stab_1) == count(stab_2) == 2 end @@ -92,17 +90,17 @@ end # and error with `tol = 100`. let rn = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X end p = [:p => 1.0, :d => 1.0] u = [1.0] - @test_throws Exception steady_state_stability(u, rn, p; tol = 1.0e2) + @test_throws Exception steady_state_stability(u, rn, p; tol = 1e2) end # Test that stability computation for non-autonomous (t-dependent) systems throws error. let rn = @reaction_network begin - (p + 1 / (1 + t), d), 0 <--> X + (p + 1/(1+t),d), 0 <--> X end p = [:p => 1.0, :d => 1.0] u = [1.0] diff --git a/test/extensions/structural_identifiability.jl b/test/extensions/structural_identifiability.jl index cbd69123f4..c4becbc5e4 100644 --- a/test/extensions/structural_identifiability.jl +++ b/test/extensions/structural_identifiability.jl @@ -10,12 +10,12 @@ loglevel = Logging.Error # 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) - dict_out = Dict{Symbol, Any}() + dict_out = Dict{Symbol,Any}() for key in keys(dict_in) sym_key = Symbol(key) sym_key = Symbol(replace(String(sym_key), "(t)" => "")) dict_out[sym_key] = dict_in[key] - end + end return dict_out end @@ -24,12 +24,12 @@ end # 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) -let +let # Identifiability analysis for Catalyst model. goodwind_oscillator_catalyst = @reaction_network begin - (mmr(P, pₘ, 1), dₘ), 0 <--> M - (pₑ * M, dₑ), 0 <--> E - (pₚ * E, dₚ), 0 <--> P + (mmr(P,pₘ,1), dₘ), 0 <--> M + (pₑ*M,dₑ), 0 <--> E + (pₚ*E,dₚ), 0 <--> P end gi_1 = assess_identifiability(goodwind_oscillator_catalyst; measured_quantities = [:M], loglevel) li_1 = assess_local_identifiability(goodwind_oscillator_catalyst; measured_quantities = [:M], loglevel) @@ -43,9 +43,9 @@ let # Identifiability analysis for StructuralIdentifiability.jl model (declare this overwrites e.g. X2 variable etc.). goodwind_oscillator_si = @ODEmodel( - M'(t) = pₘ / (1 + P(t)) - dₘ * M(t), - E'(t) = -dₑ * E(t) + pₑ * M(t), - P'(t) = -dₚ * P(t) + pₚ * E(t), + M'(t) = pₘ / (1 + P(t)) - dₘ*M(t), + E'(t) = -dₑ*E(t) + pₑ*M(t), + P'(t) = -dₚ*P(t) + pₚ*E(t), y1(t) = M(t) ) gi_3 = assess_identifiability(goodwind_oscillator_si; loglevel) @@ -55,32 +55,32 @@ let # Check outputs. @test sym_dict(gi_1) == sym_dict(gi_2) == sym_dict(gi_3) @test sym_dict(li_1) == sym_dict(li_2) == sym_dict(li_3) - @test length(ifs_1) == length(ifs_2) == length(ifs_3) + @test length(ifs_1) == length(ifs_2) == length(ifs_3) # Checks output to manually checked correct answers. @test isequal(collect(keys(gi_1)), [unknowns(goodwind_oscillator_catalyst); parameters(goodwind_oscillator_catalyst)]) @test isequal(collect(values(gi_1)), [:globally, :nonidentifiable, :globally, :globally, :globally, :nonidentifiable, :locally, :nonidentifiable, :locally]) @test isequal(collect(keys(li_1)), [unknowns(goodwind_oscillator_catalyst); parameters(goodwind_oscillator_catalyst)]) - @test isequal(collect(values(li_1)), [1, 0, 1, 1, 1, 0, 1, 0, 1]) + @test isequal(collect(values(li_1)), [1, 0, 1, 1, 1, 0, 1, 0, 1]) end # Tests on a made-up reaction network with mix of identifiable and non-identifiable components. # Tests for symbolics input. # Tests using known_p argument. -let +let # Identifiability analysis for Catalyst model. rs_catalyst = @reaction_network begin (p1, d), 0 <--> X1 k1, X1 --> X2 - (k2f, k2b), X2 <--> X3 + (k2f,k2b), X2 <--> X3 k3, X3 --> X4 d, X4 --> 0 end - @unpack X2, X3 = rs_catalyst + @unpack X2, X3 = rs_catalyst gi_1 = assess_identifiability(rs_catalyst; measured_quantities = [X2, X3], known_p = [:k2f], loglevel) li_1 = assess_local_identifiability(rs_catalyst; measured_quantities = [X2, X3], known_p = [:k2f], loglevel) ifs_1 = find_identifiable_functions(rs_catalyst; measured_quantities = [X2, X3], known_p = [:k2f], loglevel) - + # Identifiability analysis for Catalyst converted to StructuralIdentifiability.jl model. rs_ode = make_si_ode(rs_catalyst; measured_quantities = [X2, X3], known_p = [:k2f]) gi_2 = assess_identifiability(rs_ode; loglevel) @@ -89,10 +89,10 @@ let # Identifiability analysis for StructuralIdentifiability.jl model (declare this overwrites e.g. X2 variable etc.). rs_si = @ODEmodel( - X1'(t) = p1 - d * X1(t) - k1 * X1(t), - X2'(t) = k1 * X1(t) + k2b * X3(t) - k2f * X2(t), - X3'(t) = -k2b * X3(t) + k2f * X2(t) - k3 * X3(t), - X4'(t) = d * X4(t) + k3 * X3(t), + X1'(t) = p1 - d*X1(t) - k1*X1(t), + X2'(t) = k1*X1(t) + k2b*X3(t) - k2f*X2(t), + X3'(t) = -k2b*X3(t) + k2f*X2(t) - k3*X3(t), + X4'(t) = d*X4(t) + k3*X3(t), y1(t) = X2, y2(t) = X3, y3(t) = k2f @@ -104,20 +104,20 @@ let # Check outputs. @test sym_dict(gi_1) == sym_dict(gi_2) == sym_dict(gi_3) @test sym_dict(li_1) == sym_dict(li_2) == sym_dict(li_3) - @test length(ifs_1) == length(ifs_2) == length(ifs_3) + @test length(ifs_1) == length(ifs_2) == length(ifs_3) - # Checks output to manually checked correct answers. - @test isequal(collect(keys(gi_1)), [unknowns(rs_catalyst); parameters(rs_catalyst)]) - @test isequal(collect(values(gi_1)), [:nonidentifiable, :globally, :globally, :nonidentifiable, :nonidentifiable, :nonidentifiable, :nonidentifiable, :globally, :globally, :globally]) - @test isequal(collect(keys(li_1)), [unknowns(rs_catalyst); parameters(rs_catalyst)]) - @test isequal(collect(values(li_1)), [0, 1, 1, 0, 0, 0, 0, 1, 1, 1]) + # Checks output to manually checked correct answers. + @test isequal(collect(keys(gi_1)),[unknowns(rs_catalyst); parameters(rs_catalyst)]) + @test isequal(collect(values(gi_1)),[:nonidentifiable, :globally, :globally, :nonidentifiable, :nonidentifiable, :nonidentifiable, :nonidentifiable, :globally, :globally, :globally]) + @test isequal(collect(keys(li_1)),[unknowns(rs_catalyst); parameters(rs_catalyst)]) + @test isequal(collect(values(li_1)),[0, 1, 1, 0, 0, 0, 0, 1, 1, 1]) end # Tests on a made-up reaction network with mix of identifiable and non-identifiable components. # Tests for system with conserved quantity. # Tests for symbolics known_p # Tests using an equation for measured quantity. -let +let # Identifiability analysis for Catalyst model. rs_catalyst = @reaction_network begin p, 0 --> X1 @@ -125,8 +125,8 @@ let k2, X2 --> X3 k3, X3 --> X4 k3, X3 --> X5 - d, (X4, X5) --> 0 - (kA * X3, kD), Yi <--> Ya + d, (X4,X5) --> 0 + (kA*X3, kD), Yi <--> Ya end @unpack X1, X2, X3, X4, k1, k2, Yi, Ya, k1, kD = rs_catalyst gi_1 = assess_identifiability(rs_catalyst; measured_quantities = [X1 + Yi, Ya], known_p = [k1, kD], loglevel) @@ -134,20 +134,20 @@ let ifs_1 = find_identifiable_functions(rs_catalyst; measured_quantities = [X1 + Yi, Ya], known_p = [k1, kD], loglevel) # Identifiability analysis for Catalyst converted to StructuralIdentifiability.jl model. - rs_ode = make_si_ode(rs_catalyst; measured_quantities = [X1 + Yi, Ya], known_p = [k1, kD], remove_conserved = false) + rs_ode = make_si_ode(rs_catalyst; measured_quantities=[X1 + Yi, Ya], known_p=[k1, kD], remove_conserved=false) gi_2 = assess_identifiability(rs_ode; loglevel) li_2 = assess_local_identifiability(rs_ode; loglevel) ifs_2 = find_identifiable_functions(rs_ode; loglevel) # Identifiability analysis for StructuralIdentifiability.jl model (declare this overwrites e.g. X2 variable etc.). rs_si = @ODEmodel( - X1'(t) = p - k1 * X1(t), - X2'(t) = k1 * X1(t) - k2 * X2(t), - X3'(t) = k2 * X2(t) - 2k3 * X3(t), - X4'(t) = -d * X4(t) + k3 * X3(t), - X5'(t) = -d * X5(t) + k3 * X3(t), - Yi'(t) = kD * Ya(t) - kA * Yi(t) * X3(t), - Ya'(t) = -kD * Ya(t) + kA * Yi(t) * X3(t), + X1'(t) = p - k1*X1(t), + X2'(t) = k1*X1(t) - k2*X2(t), + X3'(t) = k2*X2(t) - 2k3*X3(t), + X4'(t) = -d*X4(t) + k3*X3(t), + X5'(t) = -d*X5(t) + k3*X3(t), + Yi'(t) = kD*Ya(t) - kA*Yi(t)*X3(t), + Ya'(t) = -kD*Ya(t) + kA*Yi(t)*X3(t), y1(t) = X1 + Yi, y2(t) = Ya, y3(t) = k1, @@ -156,13 +156,13 @@ let gi_3 = assess_identifiability(rs_si; loglevel) li_3 = assess_local_identifiability(rs_si; loglevel) ifs_3 = find_identifiable_functions(rs_si; loglevel) - + # Check outputs. @test sym_dict(gi_1) == sym_dict(gi_2) == sym_dict(gi_3) @test sym_dict(li_1) == sym_dict(li_2) == sym_dict(li_3) @test length(ifs_1[2:end]) == length(ifs_2) == length(ifs_3) # In the first case, the conservation law parameter is also identifiable. - # Checks output to manually checked correct answers. + # Checks output to manually checked correct answers. correct_gi = Pair.([unknowns(rs_catalyst); parameters(rs_catalyst)], [:globally, :locally, :locally, :nonidentifiable, :nonidentifiable, :globally, :globally, :globally, :globally, :locally, :locally, :nonidentifiable, :locally, :globally]) correct_li = Pair.([unknowns(rs_catalyst); parameters(rs_catalyst)], [1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1]) @test issetequal(gi_1, correct_gi) @@ -170,11 +170,11 @@ let end # Tests that various inputs types work. -let +let goodwind_oscillator_catalyst = @reaction_network begin - (mmr(P, pₘ, 1), dₘ), 0 <--> M - (pₑ * M, dₑ), 0 <--> E - (pₚ * E, dₚ), 0 <--> P + (mmr(P,pₘ,1), dₘ), 0 <--> M + (pₑ*M,dₑ), 0 <--> E + (pₚ*E,dₚ), 0 <--> P end @unpack M, E, P, pₑ, pₚ, pₘ = goodwind_oscillator_catalyst si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities = [:M]) @@ -190,8 +190,8 @@ let si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities = [M], known_p = [pₑ, pₚ]) si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities = [M, E], known_p = [pₑ, pₚ]) si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities = [M + pₑ]) - si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities = [M + E, pₑ * M], known_p = [:pₑ]) - si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities = [pₑ, pₚ], known_p = [pₑ]) + si_catalyst_ode = make_si_ode(goodwind_oscillator_catalyst; measured_quantities = [M + E, pₑ*M], known_p = [:pₑ]) + 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) @@ -201,7 +201,7 @@ let @test make_si_ode(gw_osc_complt; measured_quantities = [gw_osc_complt.M, gw_osc_complt.E], 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ₑ, gw_osc_complt.pₚ]) isa ODE @test make_si_ode(gw_osc_complt; measured_quantities = [gw_osc_complt.M], known_p = [:pₚ]) isa ODE - @test make_si_ode(gw_osc_complt; measured_quantities = [gw_osc_complt.M * gw_osc_complt.E]) isa ODE + @test make_si_ode(gw_osc_complt; measured_quantities = [gw_osc_complt.M*gw_osc_complt.E]) isa ODE end # Tests for hierarchical model with conservation laws at both top and internal levels. @@ -228,10 +228,10 @@ let # Identifiability analysis for StructuralIdentifiability.jl model (declare this overwrites e.g. X2 variable etc.). rs_si = @ODEmodel( - X1'(t) = -k1 * X1(t) + k2 * X2(t), - X2'(t) = k1 * X1(t) - k2 * X2(t), - rs2₊X3'(t) = -rs2₊k3 * rs2₊X3(t) + rs2₊k4 * rs2₊X4(t), - rs2₊X4'(t) = rs2₊k3 * rs2₊X3(t) - rs2₊k4 * rs2₊X4(t), + X1'(t) = -k1*X1(t) + k2*X2(t), + X2'(t) = k1*X1(t) - k2*X2(t), + rs2₊X3'(t) = -rs2₊k3*rs2₊X3(t) + rs2₊k4*rs2₊X4(t), + rs2₊X4'(t) = rs2₊k3*rs2₊X3(t) - rs2₊k4*rs2₊X4(t), y1(t) = X1, y2(t) = X2, y3(t) = rs2₊X3, @@ -240,7 +240,7 @@ let gi_3 = assess_identifiability(rs_si; loglevel) li_3 = assess_local_identifiability(rs_si; loglevel) ifs_3 = find_identifiable_functions(rs_si; loglevel) - + # Check outputs. @test sym_dict(gi_1) == sym_dict(gi_3) @test sym_dict(li_1) == sym_dict(li_3) @@ -249,10 +249,10 @@ let # 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. # Hence, here you display identifiability for `Γ[1]` instead of X2. - gi_1_no_cq = filter(x -> !occursin("X2", String(x[1])) && !occursin("X4", String(x[1])), sym_dict(gi_1)) - gi_2_no_cq = filter(x -> !occursin("Γ", String(x[1])), sym_dict(gi_2)) - li_1_no_cq = filter(x -> !occursin("X2", String(x[1])) && !occursin("X4", String(x[1])), sym_dict(li_1)) - li_2_no_cq = filter(x -> !occursin("Γ", String(x[1])), sym_dict(li_2)) + gi_1_no_cq = filter(x -> !occursin("X2",String(x[1])) && !occursin("X4",String(x[1])), sym_dict(gi_1)) + gi_2_no_cq = filter(x -> !occursin("Γ",String(x[1])), sym_dict(gi_2)) + li_1_no_cq = filter(x -> !occursin("X2",String(x[1])) && !occursin("X4",String(x[1])), sym_dict(li_1)) + li_2_no_cq = filter(x -> !occursin("Γ",String(x[1])), sym_dict(li_2)) @test gi_1_no_cq == gi_2_no_cq @test li_1_no_cq == li_2_no_cq end @@ -293,9 +293,9 @@ let :A0 => :globally, :A1 => :globally, :A2 => :globally, - :a => :nonidentifiable, - :b => :nonidentifiable, - :c => :nonidentifiable + :a => :nonidentifiable, + :b => :nonidentifiable, + :c => :nonidentifiable ) # Test with no parameters @@ -322,52 +322,44 @@ let @parameters k c1 c2 @variables C(t) @equations begin - D(V) ~ k * X - V - C ~ (c1 + c2) * X / V + D(V) ~ k*X - V + C ~ (c1 + c2) * X/V end - (p / V, d / V), 0 <--> X + (p/V,d/V), 0 <--> X end @unpack p, d, k, c1, c2 = rs - + # Tests identifiability assessment when all unknowns are measured. remove_conserved = false gi_1 = assess_identifiability(rs; measured_quantities = [:X, :V, :C], loglevel, remove_conserved) li_1 = assess_local_identifiability(rs; measured_quantities = [:X, :V, :C], loglevel, remove_conserved) ifs_1 = find_identifiable_functions(rs; measured_quantities = [:X, :V, :C], loglevel, remove_conserved) - @test sym_dict(gi_1) == Dict( - [ - :X => :globally, :C => :globally, :V => :globally, :k => :globally, - :c1 => :nonidentifiable, :c2 => :nonidentifiable, :p => :globally, :d => :globally, - ] - ) + @test sym_dict(gi_1) == Dict([:X => :globally, :C => :globally, :V => :globally, :k => :globally, + :c1 => :nonidentifiable, :c2 => :nonidentifiable, :p => :globally, :d => :globally]) @test sym_dict(li_1) == Dict([:X => 1, :C => 1, :V => 1, :k => 1, :c1 => 0, :c2 => 0, :p => 1, :d => 1]) @test issetequal(ifs_1, [d, p, k, c1 + c2]) - - # Tests identifiability assessment when only variables are measured. + + # Tests identifiability assessment when only variables are measured. # Checks that a parameter in an equation can be set as known. gi_2 = assess_identifiability(rs; measured_quantities = [:V, :C], known_p = [:c1], loglevel, remove_conserved) li_2 = assess_local_identifiability(rs; measured_quantities = [:V, :C], known_p = [:c1], loglevel, remove_conserved) ifs_2 = find_identifiable_functions(rs; measured_quantities = [:V, :C], known_p = [:c1], loglevel, remove_conserved) - @test sym_dict(gi_2) == Dict( - [ - :X => :nonidentifiable, :C => :globally, :V => :globally, :k => :nonidentifiable, - :c1 => :globally, :c2 => :nonidentifiable, :p => :nonidentifiable, :d => :globally, - ] - ) + @test sym_dict(gi_2) == Dict([:X => :nonidentifiable, :C => :globally, :V => :globally, :k => :nonidentifiable, + :c1 => :globally, :c2 => :nonidentifiable, :p => :nonidentifiable, :d => :globally]) @test sym_dict(li_2) == Dict([:X => 0, :C => 1, :V => 1, :k => 0, :c1 => 1, :c2 => 0, :p => 0, :d => 1]) - @test issetequal(ifs_2, [d, c1, k * p, c1 * p + c2 * p]) + @test issetequal(ifs_2, [d, c1, k*p, c1*p + c2*p]) end # Checks that identifiability functions cannot be applied to non-complete `ReactionSystems`s. -let +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, loglevel) @test_throws Exception assess_local_identifiability(incomplete_network; measured_quantities, loglevel) @test_throws Exception find_identifiable_functions(incomplete_network; measured_quantities, loglevel) -end +end \ No newline at end of file diff --git a/test/miscellaneous_tests/compound_macro.jl b/test/miscellaneous_tests/compound_macro.jl index fdd9df69e3..9a2f5f14bd 100644 --- a/test/miscellaneous_tests/compound_macro.jl +++ b/test/miscellaneous_tests/compound_macro.jl @@ -15,10 +15,10 @@ let # Basic cases that should pass: @compound H2O_1 ~ 2H + O - @compound (H2O_2, [output = true]) ~ 2H + O + @compound (H2O_2, [output=true]) ~ 2H + O @compound (H2O_3 = 1.5) ~ 2H + O - @compound (H2O_4 = 4, [output = true]) ~ 2H + O - @compound (H2O_5 = p1, [output = true]) ~ 2H + p2 * O + @compound (H2O_4 = 4, [output=true]) ~ 2H + O + @compound (H2O_5 = p1, [output=true]) ~ 2H + p2*O @test iscompound(H2O_1) @test iscompound(H2O_2) @test iscompound(H2O_3) @@ -27,20 +27,20 @@ let # Other errors. @test_throws Exception @eval @compound H2O = 2H + O - @test_throws Exception @eval @compound (H2O, [output = true]) = 2H + O + @test_throws Exception @eval @compound (H2O, [output=true]) = 2H + O @test_throws Exception @eval @compound (H2O = 1.5) ~ 2H + O - @test_throws Exception @eval @compound (H2O = 4, [output = true]) ~ 2H + O - @test_throws Exception @eval @compound (H2O = p1, [output = true]) ~ 2H + p2 * O + @test_throws Exception @eval @compound (H2O = 4, [output=true]) ~ 2H + O + @test_throws Exception @eval @compound (H2O = p1, [output=true]) ~ 2H + p2*O # Compounds created in block notation. @compounds begin CO2_1 ~ 2H + O end @compounds begin - (CO2_2, [output = true]) ~ 2H + O + (CO2_2, [output=true]) ~ 2H + O (CO2_3 = 1.5) ~ 2H + O - (CO2_4 = 4, [output = true]) ~ 2H + O - (CO2_5 = p1, [output = true]) ~ 2H + p2 * O + (CO2_4 = 4, [output=true]) ~ 2H + O + (CO2_5 = p1, [output=true]) ~ 2H + p2*O end @test iscompound(CO2_1) @test iscompound(CO2_2) @@ -54,10 +54,10 @@ let @parameters p1 p2 @compounds begin NH3_1 ~ N + 3H - (NH3_2, [output = true]) ~ N + 3H + (NH3_2, [output=true]) ~ N + 3H (NH3_3 = 1.5) ~ N + 3H - (NH3_4 = 4, [output = true]) ~ N + 3H - (NH3_5 = p1, [output = true]) ~ N + p2 * H + (NH3_4 = 4, [output=true]) ~ N + 3H + (NH3_5 = p1, [output=true]) ~ N + p2*H end end @test iscompound(rn.NH3_1) @@ -123,21 +123,21 @@ end # Test using different independent variable combinations. let @parameters x y z - @species C(t) H(x) N(x) O(t) P(t, x) S(x, y) + @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 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 + @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 + @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(Symbolics.sorted_arguments(ModelingToolkitBase.unwrap(CO2)), [t]) @@ -185,22 +185,22 @@ let alpha = 2 h = H - @compound H2_1 ~ 2 * H - @compound H2_2 ~ alpha * H - @compound H2_3 ~ 2 * h - @compound H2_4 ~ alpha * H + @compound H2_1 ~ 2*H + @compound H2_2 ~ alpha*H + @compound H2_3 ~ 2*h + @compound H2_4 ~ alpha*H @test iscompound(H2_1) @test iscompound(H2_2) @test iscompound(H2_2) @test iscompound(H2_4) - @test isequal(components(H2_1), components(H2_2)) - @test isequal(components(H2_2), components(H2_3)) - @test isequal(components(H2_3), components(H2_4)) - @test isequal(coefficients(H2_1), coefficients(H2_2)) - @test isequal(coefficients(H2_2), coefficients(H2_3)) - @test isequal(coefficients(H2_3), coefficients(H2_4)) + @test isequal(components(H2_1),components(H2_2)) + @test isequal(components(H2_2),components(H2_3)) + @test isequal(components(H2_3),components(H2_4)) + @test isequal(coefficients(H2_1),coefficients(H2_2)) + @test isequal(coefficients(H2_2),coefficients(H2_3)) + @test isequal(coefficients(H2_3),coefficients(H2_4)) end # Case 4. @@ -208,13 +208,13 @@ let @parameters alpha = 2 @species H(t) - @compound H2_1 ~ alpha * H + @compound H2_1 ~ alpha*H @compound H2_2 ~ 2H @test iscompound(H2_1) @test iscompound(H2_2) - @test isequal(components(H2_1), components(H2_2)) + @test isequal(components(H2_1),components(H2_2)) @test isequal(coefficients(H2_1), @parameters alpha = 2) end @@ -228,7 +228,7 @@ let @test iscompound(A2) @test iscompound(B2) - @test isequal(components(A2), components(B2)) + @test isequal(components(A2),components(B2)) @test isequal(coefficients(A2), coefficients(B2)) @test isequal(component_coefficients(A2), component_coefficients(B2)) end @@ -249,10 +249,10 @@ let @test iscompound(OH_alt) @test iscompound(C3H5OH3_alt) - @test isequal(components(OH), components(OH_alt)) + @test isequal(components(OH),components(OH_alt)) @test isequal(coefficients(OH), coefficients(OH_alt)) @test isequal(component_coefficients(OH), component_coefficients(OH_alt)) - @test isequal(components(C3H5OH3), components(C3H5OH3_alt)) + @test isequal(components(C3H5OH3),components(C3H5OH3_alt)) @test isequal(coefficients(C3H5OH3), coefficients(C3H5OH3_alt)) @test isequal(component_coefficients(C3H5OH3), component_coefficients(C3H5OH3_alt)) end @@ -271,7 +271,7 @@ let @test iscompound(comp) @test iscompound(comp_alt) - @test isequal(components(comp), components(comp_alt)) + @test isequal(components(comp),components(comp_alt)) @test isequal(coefficients(comp), coefficients(comp_alt)) @test isequal(component_coefficients(comp), component_coefficients(comp_alt)) end diff --git a/test/miscellaneous_tests/explicit_imports.jl b/test/miscellaneous_tests/explicit_imports.jl index abeb1f367f..b630a9dbba 100644 --- a/test/miscellaneous_tests/explicit_imports.jl +++ b/test/miscellaneous_tests/explicit_imports.jl @@ -5,16 +5,12 @@ using Catalyst, ExplicitImports, Test @testset "Explicit Imports" begin # Test that there are no implicit imports # allow_unanalyzable is needed because PhysicalScale enum causes parsing issues - @test check_no_implicit_imports( - Catalyst; - allow_unanalyzable = (Catalyst, Catalyst.PhysicalScale) - ) === nothing + @test check_no_implicit_imports(Catalyst; + allow_unanalyzable=(Catalyst, Catalyst.PhysicalScale)) === nothing # Test that there are no stale explicit imports - @test check_no_stale_explicit_imports( - Catalyst; - allow_unanalyzable = (Catalyst, Catalyst.PhysicalScale) - ) === nothing + @test check_no_stale_explicit_imports(Catalyst; + allow_unanalyzable=(Catalyst, Catalyst.PhysicalScale)) === nothing # Test that all explicit imports are from owner modules @test check_all_explicit_imports_via_owners(Catalyst) === nothing diff --git a/test/miscellaneous_tests/reaction_balancing.jl b/test/miscellaneous_tests/reaction_balancing.jl index 446842da15..a5908a6614 100644 --- a/test/miscellaneous_tests/reaction_balancing.jl +++ b/test/miscellaneous_tests/reaction_balancing.jl @@ -18,12 +18,12 @@ let H2O ~ 2H + 1O end - rx = Reaction(k, [H2, O2], [H2O]) + rx = Reaction(k,[H2,O2],[H2O]) - @test isequal(Catalyst.create_matrix(rx), [2 0 -2; 0 2 -1;]) - @test isequal(Catalyst.get_balanced_stoich(rx), [[2, 1, 2]]) + @test isequal(Catalyst.create_matrix(rx),[2 0 -2; 0 2 -1;]) + @test isequal(Catalyst.get_balanced_stoich(rx),[[2, 1, 2]]) - balanced_rx = Reaction(k, [H2, O2], [H2O], [2, 1], [2]) + balanced_rx = Reaction(k,[H2,O2],[H2O],[2,1],[2]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -42,12 +42,12 @@ let @compound H2O ~ 2H + 1O @compound C6H12O6 ~ 6C + 12H + 6O - rx = Reaction(k, [CO2, H2O], [C6H12O6, O2]) + rx = Reaction(k,[CO2,H2O],[C6H12O6,O2]) - @test isequal(Catalyst.create_matrix(rx), [ 1 0 -6 0; 2 1 -6 -2; 0 2 -12 0;]) - @test isequal(Catalyst.get_balanced_stoich(rx), [[6, 6, 1, 6]]) + @test isequal(Catalyst.create_matrix(rx),[ 1 0 -6 0; 2 1 -6 -2; 0 2 -12 0;]) + @test isequal(Catalyst.get_balanced_stoich(rx),[[6, 6, 1, 6]]) - balanced_rx = Reaction(k, [CO2, H2O], [C6H12O6, O2], [6, 6], [1, 6]) + balanced_rx = Reaction(k,[CO2,H2O],[C6H12O6,O2],[6, 6], [1, 6]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -80,7 +80,7 @@ let rx = Reaction(1.0, [H, O], [H2O], [23, 1], [7]) - balanced_rx = Reaction(1.0, [H, O], [H2O], [2, 1], [1]) + balanced_rx = Reaction(1.0, [H,O], [H2O], [2, 1], [1]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -113,7 +113,7 @@ let rx = Reaction(1.0, [N2, H2], [NH3]) - balanced_rx = Reaction(1.0, [N2, H2], [NH3], [1, 3], [2]) + balanced_rx = Reaction(1.0, [N2, H2], [NH3], [1 ,3], [2]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -174,9 +174,9 @@ let @compound Na2SO4 ~ 2Na + 1S + 4O @compound H2O ~ 2H + O - rx = Reaction(1.0, [NaOH, H2SO4], [Na2SO4, H2O]) + rx = Reaction(1.0, [NaOH,H2SO4], [Na2SO4,H2O]) - balanced_rx = Reaction(1.0, [NaOH, H2SO4], [Na2SO4, H2O], [2, 1], [1, 2]) + balanced_rx = Reaction(1.0, [NaOH,H2SO4], [Na2SO4,H2O], [2, 1], [1, 2]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -205,8 +205,8 @@ let @compound HCl ~ 1H + 1Cl @compound CaCl2 ~ 1Ca + 2Cl - rx = Reaction(1.0, [CaCO3, HCl], [CaCl2, CO2, H2O]) - balanced_rx = Reaction(1.0, [CaCO3, HCl], [CaCl2, CO2, H2O], [1, 2], [1, 1, 1]) + rx = Reaction(1.0,[CaCO3,HCl],[CaCl2,CO2,H2O]) + balanced_rx = Reaction(1.0,[CaCO3,HCl],[CaCl2,CO2,H2O], [1, 2], [1, 1, 1]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -220,8 +220,8 @@ let @compound H4SiO4 ~ 4H + Si + 4O @compound HCl ~ H + Cl - rx = Reaction(1.0, [SiCl4, H2O], [H4SiO4, HCl]) - balanced_rx = Reaction(1.0, [SiCl4, H2O], [H4SiO4, HCl], [1, 4], [1, 4]) + rx = Reaction(1.0,[SiCl4,H2O],[H4SiO4,HCl]) + balanced_rx = Reaction(1.0,[SiCl4,H2O],[H4SiO4,HCl], [1,4], [1,4]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -234,8 +234,8 @@ let @compound AlCl3 ~ Al + 3Cl @compound H2 ~ 2H - rx = Reaction(1.0, [Al, HCl], [AlCl3, H2]) - balanced_rx = Reaction(1.0, [Al, HCl], [AlCl3, H2], [2, 6], [2, 3]) + rx = Reaction(1.0,[Al,HCl],[AlCl3,H2]) + balanced_rx = Reaction(1.0,[Al,HCl],[AlCl3,H2],[2,6], [2,3]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -250,8 +250,8 @@ let @compound H2O ~ 2H + O @compound CO2 ~ C + 2O - rx = Reaction(1.0, [Na2CO3, HCl], [NaCl, H2O, CO2]) - balanced_rx = Reaction(1.0, [Na2CO3, HCl], [NaCl, H2O, CO2], [1, 2], [2, 1, 1]) + rx = Reaction(1.0,[Na2CO3,HCl],[NaCl,H2O,CO2]) + balanced_rx = Reaction(1.0,[Na2CO3,HCl],[NaCl,H2O,CO2], [1,2], [2,1,1]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -265,8 +265,8 @@ let @compound CO2 ~ C + 2O @compound H2O ~ 2H + O - rx = Reaction(1.0, [C7H6O2, O2], [CO2, H2O]) - balanced_rx = Reaction(1.0, [C7H6O2, O2], [CO2, H2O], [2, 15], [14, 6]) + rx = Reaction(1.0,[C7H6O2,O2],[CO2,H2O]) + balanced_rx = Reaction(1.0,[C7H6O2,O2],[CO2,H2O], [2,15], [14,6]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -280,8 +280,8 @@ let @compound K2SO4 ~ 2K + S + 4O @compound FeO3H3 ~ Fe + 3O + 3H - rx = Reaction(1.0, [Fe2S3O12, KOH], [K2SO4, FeO3H3]) #5x4 matrix - balanced_rx = Reaction(1.0, [Fe2S3O12, KOH], [K2SO4, FeO3H3], [1, 6], [3, 2]) + rx = Reaction(1.0,[Fe2S3O12,KOH],[K2SO4,FeO3H3]) #5x4 matrix + balanced_rx = Reaction(1.0,[Fe2S3O12,KOH],[K2SO4,FeO3H3], [1,6], [3,2]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -295,8 +295,8 @@ let @compound P4O10 ~ 4P + 10O @compound CaSiO3 ~ Ca + Si + 3O - rx = Reaction(1.0, [Ca3P2O8, SiO2], [P4O10, CaSiO3]) #5x4 matrix - balanced_rx = Reaction(1.0, [Ca3P2O8, SiO2], [P4O10, CaSiO3], [2, 6], [1, 6]) + rx = Reaction(1.0,[Ca3P2O8,SiO2],[P4O10,CaSiO3]) #5x4 matrix + balanced_rx = Reaction(1.0,[Ca3P2O8,SiO2],[P4O10,CaSiO3], [2,6] , [1,6]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -309,8 +309,8 @@ let @compound KClO4 ~ K + Cl + 4O @compound KCl ~ K + Cl - rx = Reaction(1.0, [KClO3], [KClO4, KCl]) - balanced_rx = Reaction(1.0, [KClO3], [KClO4, KCl], [4], [3, 1]) + rx = Reaction(1.0,[KClO3],[KClO4,KCl]) + balanced_rx = Reaction(1.0,[KClO3],[KClO4,KCl], [4], [3,1]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -324,8 +324,8 @@ let @compound AlO3H3 ~ Al + 3O + 3H @compound CaSO4 ~ Ca + S + 4O - rx = Reaction(1.0, [Al2S3O12, CaO2H2], [AlO3H3, CaSO4]) - balanced_rx = Reaction(1.0, [Al2S3O12, CaO2H2], [AlO3H3, CaSO4], [1, 3], [2, 3]) + rx = Reaction(1.0,[Al2S3O12,CaO2H2],[AlO3H3,CaSO4]) + balanced_rx = Reaction(1.0,[Al2S3O12,CaO2H2],[AlO3H3,CaSO4], [1,3], [2,3]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -340,8 +340,8 @@ let @compound I2 ~ 2I @compound H2O ~ 2H + O - rx = Reaction(1.0, [H2SO4, HI], [H2S, I2, H2O]) - balanced_rx = Reaction(1.0, [H2SO4, HI], [H2S, I2, H2O], [1, 8], [1, 4, 4]) + rx = Reaction(1.0,[H2SO4,HI],[H2S,I2,H2O]) + balanced_rx = Reaction(1.0,[H2SO4,HI],[H2S,I2,H2O], [1,8], [1,4,4]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -355,8 +355,8 @@ let @compound CO2 ~ C + 2O @compound H2O ~ 2H + O - rx = Reaction(1.0, [C2H4, O2], [CO2, H2O]) - balanced_rx = Reaction(1.0, [C2H4, O2], [CO2, H2O], [1, 3], [2, 2]) + rx = Reaction(1.0,[C2H4,O2],[CO2,H2O]) + balanced_rx = Reaction(1.0,[C2H4,O2],[CO2,H2O],[1,3],[2,2]) brxs = balance_reaction(rx) @test length(brxs) == 1 @test isequal(balanced_rx, first(brxs)) @@ -373,9 +373,9 @@ let @compound CH4 ~ C + 4H @compound H2O ~ 2H + O - rx = Reaction(1.0, [CO, CO2, H2], [CH4, H2O]) + rx = Reaction(1.0,[CO,CO2,H2],[CH4,H2O]) brxs = balance_reaction(rx) - @test_logs (:warn, r"The space of possible balanced *") match_mode = :any balance_reaction(rx) + @test_logs (:warn, r"The space of possible balanced *") match_mode=:any balance_reaction(rx) @test length(brxs) == 2 end @@ -389,9 +389,9 @@ let @compound NO ~ N + O @compound H2SO4 ~ 2H + S + 4O - rx = Reaction(1.0, [FeS2, HNO3], [Fe2S3O12, NO, H2SO4]) + rx = Reaction(1.0,[FeS2,HNO3],[Fe2S3O12,NO,H2SO4]) brxs = balance_reaction(rx) - @test_logs (:warn, "Unable to balance reaction.") match_mode = :any balance_reaction(rx) + @test_logs (:warn, "Unable to balance reaction.") match_mode=:any balance_reaction(rx) @test isempty(brxs) end @@ -418,14 +418,14 @@ let end k, CO2 + H2O --> C6H12O6 + O2 end - + brxs = balance_reaction(reactions(rn)[1])[1] - + @test isequal(rn.k, brxs.rate) - @test isequal([rn.CO2, rn.H2O], brxs.substrates) - @test isequal([rn.C6H12O6, rn.O2], brxs.products) + @test isequal([rn.CO2, rn.H2O], brxs.substrates) + @test isequal([rn.C6H12O6, rn.O2], brxs.products) @test isequal([6, 6], brxs.substoich) - @test isequal([1, 6], brxs.prodstoich) + @test isequal([1, 6], brxs.prodstoich) end @@ -496,7 +496,7 @@ let k, CO + CO2 + H2 --> CH4 + H20 end @test_throws Exception balance_system(rs1) - + # Check system with reaction without any balanced versions. rs2 = @reaction_network begin @species Fe(t) S(t) O(t) H(t) N(t) @@ -510,4 +510,4 @@ let k, FeS2 + HNO3 --> Fe2S3O12 + NO + H2SO4 end @test_throws Exception balance_system(rs2) -end +end \ No newline at end of file diff --git a/test/miscellaneous_tests/reactionsystem_serialisation.jl b/test/miscellaneous_tests/reactionsystem_serialisation.jl index 5cbd687b05..329867b678 100644 --- a/test/miscellaneous_tests/reactionsystem_serialisation.jl +++ b/test/miscellaneous_tests/reactionsystem_serialisation.jl @@ -100,7 +100,7 @@ let bool_md = false int_md = 3 float_md = 1.2 - rat_md = 4 // 5 + rat_md = 4//5 sym_md = :sym c_md = 'c' str_md = "A string" @@ -116,30 +116,30 @@ let # Creates parameters, variables, and species (with various metadata and default values). @parameters begin - a, [input = bool_md] - b, [misc = int_md] - c = float_md, [misc = rat_md] - d1, [misc = c_md] - d2, [description = str_md] - e1, [misc = nothing_md] - e2, [misc = symb_md] + a, [input=bool_md] + b, [misc=int_md] + c = float_md, [misc=rat_md] + d1, [misc=c_md] + d2, [description=str_md] + e1, [misc=nothing_md] + e2, [misc=symb_md] end @variables begin A(t) = float_md - B(t), [misc = expr_md] - C(t), [misc = pair_md] - D1(t), [misc = tup_md] - D2(t), [misc = vec_md] - E1(t), [misc = dict_md] - E2(t), [misc = mat_md] + B(t), [misc=expr_md] + C(t), [misc=pair_md] + D1(t), [misc=tup_md] + D2(t), [misc=vec_md] + E1(t), [misc=dict_md] + E2(t), [misc=mat_md] end @species begin - X(t), [input = bool_md] - Y(t), [misc = int_md] - Z(t), [misc = float_md] - V1(t), [description = str_md] - V2(t), [misc = dict_md] - W1(t), [misc = mat_md] + X(t), [input=bool_md] + Y(t), [misc=int_md] + Z(t), [misc=float_md] + V1(t), [description=str_md] + V2(t), [misc=dict_md] + W1(t), [misc=mat_md] W2(t) = float_md end @@ -265,18 +265,18 @@ end # Tests for complete and incomplete system. let # Prepares spatial independent variables (technically not used and only supplied to systems). - sivs = @variables x y z [description = "A spatial independent variable."] + sivs = @variables x y z [description="A spatial independent variable."] # Prepares parameters, species, and variables. @parameters p d k1_1 k2_1 k1_2 k2_2 k1_3 k2_3 k1_4 k2_4 a b_1 b_2 b_3 b_4 η @parameters begin t_1 = 2.0 t_2::Float64 - t_3, [description = "A parameter."] - t_4::Float32 = p, [description = "A parameter."] + t_3, [description="A parameter."] + t_4::Float32 = p, [description="A parameter."] end - @species X(t) X2_1(t) X2_2(t) X2_3(t) X2_4(t) = p [description = "A species."] - @variables A(t) = p [description = "A variable."] B_1(t) B_2(t) B_3(t) B_4(t) + @species X(t) X2_1(t) X2_2(t) X2_3(t) X2_4(t)=p [description="A species."] + @variables A(t)=p [description="A variable."] B_1(t) B_2(t) B_3(t) B_4(t) # Prepares all equations. eqs_1 = [ @@ -285,7 +285,7 @@ let Reaction(k1_1, [X], [X2_1], [2], [1]), Reaction(k2_1, [X2_1], [X], [1], [2]), D(A) ~ a - A, - A + 2B_1^3 ~ b_1 * X, + A + 2B_1^3 ~ b_1 * X ] eqs_2 = [ Reaction(p, [], [X]; metadata = [:description => "A reaction"]), @@ -293,7 +293,7 @@ let Reaction(k1_2, [X], [X2_2], [2], [1]), Reaction(k2_2, [X2_2], [X], [1], [2]), D(A) ~ a - A, - A + 2B_2^3 ~ b_2 * X, + A + 2B_2^3 ~ b_2 * X ] eqs_3 = [ Reaction(p, [], [X]; metadata = [:description => "A reaction"]), @@ -301,7 +301,7 @@ let Reaction(k1_3, [X], [X2_3], [2], [1]), Reaction(k2_3, [X2_3], [X], [1], [2]), D(A) ~ a - A, - A + 2B_3^3 ~ b_3 * X, + A + 2B_3^3 ~ b_3 * X ] eqs_4 = [ Reaction(p, [], [X]; metadata = [:description => "A reaction"]), @@ -309,14 +309,14 @@ let Reaction(k1_4, [X], [X2_4], [2], [1]), Reaction(k2_4, [X2_4], [X], [1], [2]), D(A) ~ a - A, - A + 2B_4^3 ~ b_4 * X, + A + 2B_4^3 ~ b_4 * X ] # Prepares all events. - continuous_events_1 = [(A ~ t_1) => [A ~ A + 2.0, X ~ X / 2]] - continuous_events_2 = [(A ~ t_2) => [A ~ A + 2.0, X ~ X / 2]] - continuous_events_3 = [(A ~ t_3) => [A ~ A + 2.0, X ~ X / 2]] - continuous_events_4 = [(A ~ t_4) => [A ~ A + 2.0, X ~ X / 2]] + continuous_events_1 = [(A ~ t_1) => [A ~ A + 2.0, X ~ X/2]] + continuous_events_2 = [(A ~ t_2) => [A ~ A + 2.0, X ~ X/2]] + continuous_events_3 = [(A ~ t_3) => [A ~ A + 2.0, X ~ X/2]] + continuous_events_4 = [(A ~ t_4) => [A ~ A + 2.0, X ~ X/2]] discrete_events_1 = [ 10.0 => [X2_1 ~ X2_1 + 1.0] [5.0, 10.0] => [b_1 ~ 2 * b_1] @@ -339,26 +339,18 @@ let ] # Creates the systems. - @named rs_4 = ReactionSystem( - eqs_4, t; continuous_events = continuous_events_4, - discrete_events = discrete_events_4, spatial_ivs = sivs, - metadata = [MiscSystemData => "System 4"], systems = [] - ) - @named rs_2 = ReactionSystem( - eqs_2, t; continuous_events = continuous_events_2, - discrete_events = discrete_events_2, spatial_ivs = sivs, - metadata = [MiscSystemData => "System 2"], systems = [] - ) - @named rs_3 = ReactionSystem( - eqs_3, t; continuous_events = continuous_events_3, - discrete_events = discrete_events_3, spatial_ivs = sivs, - metadata = [MiscSystemData => "System 3"], systems = [rs_4] - ) - @named rs_1 = ReactionSystem( - eqs_1, t; continuous_events = continuous_events_1, - discrete_events = discrete_events_1, spatial_ivs = sivs, - metadata = [MiscSystemData => "System 1"], systems = [rs_2, rs_3] - ) + @named rs_4 = ReactionSystem(eqs_4, t; continuous_events = continuous_events_4, + discrete_events = discrete_events_4, spatial_ivs = sivs, + metadata = [MiscSystemData => "System 4"], systems = []) + @named rs_2 = ReactionSystem(eqs_2, t; continuous_events = continuous_events_2, + discrete_events = discrete_events_2, spatial_ivs = sivs, + metadata = [MiscSystemData => "System 2"], systems = []) + @named rs_3 = ReactionSystem(eqs_3, t; continuous_events = continuous_events_3, + discrete_events = discrete_events_3, spatial_ivs = sivs, + metadata = [MiscSystemData => "System 3"], systems = [rs_4]) + @named rs_1 = ReactionSystem(eqs_1, t; continuous_events = continuous_events_1, + discrete_events = discrete_events_1, spatial_ivs = sivs, + metadata = [MiscSystemData => "System 1"], systems = [rs_2, rs_3]) rs = complete(rs_1) # Checks that the correct system is saved (both complete and incomplete ones). @@ -382,7 +374,7 @@ let [X ~ 5.0] => [X => X + 1.0] [X ~ 20.0] => [X => X - 1.0] end - @discrete_events 5.0 => [d => d / 2] + @discrete_events 5.0 => [d => d/2] d, X --> 0 end @@ -401,9 +393,9 @@ let rs = @reaction_network begin @ivs t x y z @parameters p - @species X(t, x, y) Y(t, x, y) XY(t, x, y) Z(t, x, y) - @variables V(t, x, z) - (kB, kD), X + Y <--> XY + @species X(t,x,y) Y(t,x,y) XY(t,x,y) Z(t,x,y) + @variables V(t,x,z) + (kB,kD), X + Y <--> XY end save_reactionsystem(testpath("serialised_rs.jl"), rs) @test Catalyst.isequivalent(rs, include(testpath("serialised_rs.jl"))) @@ -421,7 +413,7 @@ let @parameters k1 k2::Int64 rxs = [ Reaction(k1, [X1], [X2]), - Reaction(k2, [X2], [X1]), + Reaction(k2, [X2], [X1]) ] initial_conditions = Dict((X1 => 1.0, k2 => 2)) @@ -430,7 +422,7 @@ let conservationlaws(rs) # Serialises model and then loads and checks it. - @test_logs (:warn,) match_mode = :any save_reactionsystem(testpath("serialised_rs.jl"), rs) + @test_logs (:warn, ) match_mode=:any save_reactionsystem(testpath("serialised_rs.jl"), rs) rs_loaded = include(testpath("serialised_rs.jl")) @test Catalyst.isequivalent(rs, rs_loaded) @test isequal(ModelingToolkitBase.get_initial_conditions(rs), ModelingToolkitBase.get_initial_conditions(rs_loaded)) @@ -445,7 +437,7 @@ let rxs = [ Reaction(p, [], [X]), - Reaction(d, [X], []), + Reaction(d, [X], []) ] eq = D(V) ~ V_max - V @@ -458,7 +450,7 @@ end let # Checks for complete system. rs_complete = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X end save_reactionsystem(testpath("serialised_rs_complete.jl"), rs_complete) rs_complete_loaded = include(testpath("serialised_rs_complete.jl")) @@ -467,7 +459,7 @@ let # Checks for non-complete system. rs_incomplete = @network_component begin - (p, d), 0 <--> X + (p,d), 0 <--> X end save_reactionsystem(testpath("serialised_rs_incomplete.jl"), rs_incomplete) rs_incomplete_loaded = include(testpath("serialised_rs_incomplete.jl")) @@ -500,8 +492,8 @@ let Xcount ~ X + X2 end p, 0 --> X - d * X2, X => 0 - (k1, k2), 2X <--> X2 + d*X2, X => 0 + (k1,k2), 2X <--> X2 end # Checks its serialisation. diff --git a/test/network_analysis/conservation_laws.jl b/test/network_analysis/conservation_laws.jl index ed27f51f6b..0929fc61cc 100644 --- a/test/network_analysis/conservation_laws.jl +++ b/test/network_analysis/conservation_laws.jl @@ -33,11 +33,9 @@ let # For the A + B <--> C subsystem one of these must occur # as a conservation law. - D = [ - 1 -1 0 0 0 0 0 0 0 0; - -1 1 0 0 0 0 0 0 0 0 - 0 1 1 0 0 0 0 0 0 0 - ] + D = [1 -1 0 0 0 0 0 0 0 0; + -1 1 0 0 0 0 0 0 0 0 + 0 1 1 0 0 0 0 0 0 0] @test any(D[j, :] == C[i, :] for i in 1:size(C, 1), j in 1:size(D, 1)) C = conservationlaws(rn) @@ -57,7 +55,7 @@ let # Networks for which there are known conservation laws (stored in `reaction_network_conslaws`). function consequiv(A, B) - return rank([A; B]) == rank(A) == rank(B) + rank([A; B]) == rank(A) == rank(B) end Cs_constraint = map(conservationlaws, reaction_networks_conserved) @test all(consequiv.(Matrix{Int}.(Cs_constraint), reaction_network_conslaws)) @@ -86,7 +84,7 @@ end let # Creates network with/without cached conservation laws. rn = @reaction_network rn begin - (k1, k2), X1 <--> X2 + (k1,k2), X1 <--> X2 end rn_cached = deepcopy(rn) conservationlaws(rn_cached) @@ -110,10 +108,8 @@ let end @unpack A, B, C, D, E, F1, F2, F3, k1, k2, m1, m2, b12, b23, b31 = rn sps = species(rn) - 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, - ] + 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] p = [k1 => 1.0, k2 => 0.1, m1 => 1.0, m2 => 2.0, b12 => 1.0, b23 => 2.0, b31 => 0.1] tspan = (0.0, 20.0) @@ -122,9 +118,9 @@ let oprob1 = ODEProblem(osys, [u0; p], tspan) oprob2 = ODEProblem(rn, u0, tspan, p) oprob3 = ODEProblem(rn, u0, tspan, p; remove_conserved = true) - osol1 = solve(oprob1, Tsit5(); abstol = 1.0e-8, reltol = 1.0e-8, saveat = 0.2) - osol2 = solve(oprob2, Tsit5(); abstol = 1.0e-8, reltol = 1.0e-8, saveat = 0.2) - osol3 = solve(oprob3, Tsit5(); abstol = 1.0e-8, reltol = 1.0e-8, saveat = 0.2) + osol1 = solve(oprob1, Tsit5(); abstol = 1e-8, reltol = 1e-8, saveat = 0.2) + osol2 = solve(oprob2, Tsit5(); abstol = 1e-8, reltol = 1e-8, saveat = 0.2) + osol3 = solve(oprob3, Tsit5(); abstol = 1e-8, reltol = 1e-8, saveat = 0.2) @test osol1[sps] ≈ osol2[sps] ≈ osol3[sps] let # SteadyStateProblem issue from MTK #4177 is now fixed. @@ -136,18 +132,18 @@ let nprob1b = NonlinearProblem{true}(nsys_ss, merge(Dict(u0), Dict(p))) nprob2 = NonlinearProblem(rn, u0, p; remove_conserved = true) nprob2b = NonlinearProblem(rn, u0, p; remove_conserved = true, strucmtkcompiletural_simplify = true) - nsol1 = solve(nprob1, NewtonRaphson(); abstol = 1.0e-8) - nsol1b = solve(nprob1b, NewtonRaphson(); abstol = 1.0e-8) - nsol2 = solve(nprob2, NewtonRaphson(); abstol = 1.0e-8) - nsol2b = solve(nprob2b, NewtonRaphson(); abstol = 1.0e-8) + nsol1 = solve(nprob1, NewtonRaphson(); abstol = 1e-8) + nsol1b = solve(nprob1b, NewtonRaphson(); abstol = 1e-8) + nsol2 = solve(nprob2, NewtonRaphson(); abstol = 1e-8) + nsol2b = solve(nprob2b, NewtonRaphson(); abstol = 1e-8) # Nonlinear problems cannot find steady states properly without removing conserved species. ssprob1 = SteadyStateProblem{true}(osys, merge(Dict(u0), Dict(p))) ssprob2 = SteadyStateProblem(rn, u0, p) ssprob3 = SteadyStateProblem(rn, u0, p; remove_conserved = true) - sssol1 = solve(ssprob1, DynamicSS(Tsit5()); abstol = 1.0e-8, reltol = 1.0e-8) - sssol2 = solve(ssprob2, DynamicSS(Tsit5()); abstol = 1.0e-8, reltol = 1.0e-8) - sssol3 = solve(ssprob3, DynamicSS(Tsit5()); abstol = 1.0e-8, reltol = 1.0e-8) + sssol1 = solve(ssprob1, DynamicSS(Tsit5()); abstol = 1e-8, reltol = 1e-8) + sssol2 = solve(ssprob2, DynamicSS(Tsit5()); abstol = 1e-8, reltol = 1e-8) + sssol3 = solve(ssprob3, DynamicSS(Tsit5()); abstol = 1e-8, reltol = 1e-8) @test nsol1[sps] ≈ nsol1b[sps] @test nsol1[sps] ≈ nsol2[sps] @test nsol1[sps] ≈ nsol2b[sps] @@ -157,10 +153,8 @@ let end # Creates SDEProblems using various approaches. - u0_sde = [ - A => 100.0, B => 20.0, C => 5.0, D => 10.0, E => 3.0, F1 => 8.0, F2 => 2.0, - F3 => 20.0, - ] + u0_sde = [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 = complete(sde_model(rn; remove_conserved = true)) sprob1 = SDEProblem(ssys, [u0_sde; p], tspan) sprob2 = SDEProblem(rn, u0_sde, tspan, p) @@ -195,7 +189,7 @@ let # Prepares the model. rn = @reaction_network rn begin @parameters kB::Int64 - (kB, kD), X + Y <--> XY + (kB,kD), X + Y <--> XY end sps = species(rn) @unpack kB, kD, X, Y, XY = rn @@ -215,7 +209,7 @@ end let # Creates `SDEProblem`s. rn = @reaction_network begin - (k1, k2), X1 <--> X2 + (k1,k2), X1 <--> X2 end u0 = Dict([:X1 => 100.0, :X2 => 120.0]) ps = [:k1 => 0.2, :k2 => 0.15] @@ -230,7 +224,7 @@ end let # Prepares `ODEProblem`s. rn = @reaction_network begin - (k1, k2), X1 <--> X2 + (k1,k2), X1 <--> X2 end osys = complete(ode_model(rn; remove_conserved = true)) u0_1 = [osys.X1 => 1.0, osys.X2 => 1.0] @@ -250,18 +244,18 @@ end # Checks `num_cons_laws` function. let rn1 = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X end @test Catalyst.num_cons_laws(rn1) == 0 rn2 = @reaction_network begin - (k1, k2), X1 <--> X2 + (k1,k2), X1 <--> X2 end @test Catalyst.num_cons_laws(rn2) == 1 rn3 = @reaction_network begin - (k1, k2), X1 <--> X2 - (k1, k2), Y1 <--> Y2 + (k1,k2), X1 <--> X2 + (k1,k2), Y1 <--> Y2 end @test Catalyst.num_cons_laws(rn3) == 2 end @@ -277,7 +271,7 @@ let @species X1(t) X2(t) rxs = [ Reaction(k1, [X1], [X2]), - Reaction(k2, [X2], [X1]), + Reaction(k2, [X2], [X1]) ] @named rs = ReactionSystem(rxs, t) osys = complete(ode_model(complete(rs); remove_conserved = true)) @@ -314,9 +308,9 @@ let end # Additional test checking that correct values are stored in systems of various types after conservation law removal. -let - rn = @reaction_network begin - (kB, kD), 2X <--> X2 + let + rn = @reaction_network begin + (kB,kD), 2X <--> X2 end u0 = [:X => 2.0, :X2 => 3.0] ps = [:kB => 0.1, :kD => 0.2] @@ -343,10 +337,10 @@ end let # Creates the model (contains both conserved and non-conserved species). rn = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X d, XY --> Y - (k1, k2), X + Y <--> XY - (k3, k4), 2Y <--> Y2 + (k1,k2), X + Y <--> XY + (k3,k4), 2Y <--> Y2 end # Finds a steady state at which we will compute the Jacobian. @@ -358,7 +352,7 @@ let # Creates a function which evaluates whether the Jacobian is singular. # Singularity means infinite condition number (here it is about 1e17). - function is_singular(prob; infthres = 1.0e12) + function is_singular(prob; infthres = 1e12) J = zeros(length(prob.u0), length(prob.u0)) ModelingToolkitBase.is_time_dependent(prob) ? prob.f.jac(J, prob.u0, prob.p, 0.0) : prob.f.jac(J, prob.u0, prob.p) return cond(J) > infthres @@ -390,8 +384,8 @@ end let # Prepares the problem inputs and computes the conservation equation. rn = @reaction_network begin - (k1, k2), 2X1 <--> X2 - (k3, k4), X1 + X2 <--> X3 + (k1,k2), 2X1 <--> X2 + (k3,k4), X1 + X2 <--> X3 end @unpack X1, X2, X3 = rn u0 = [X1 => 1.0, X2 => 1.0, X3 => 1.0] @@ -410,8 +404,8 @@ let # computed after initialization. Hence, comparisons are made for the integrators. # StochasticDiffEq defaults to abstol=reltol=0.01, which gets inherited by the # initialization NonlinearSolve. We pass tight tolerances to get precise initialization. - init_kwargs = solver isa ImplicitEM ? (; abstol = 1.0e-8, reltol = 1.0e-8) : (;) - for _ in 1:3 + init_kwargs = solver isa ImplicitEM ? (; abstol = 1e-8, reltol = 1e-8) : (;) + for _ = 1:3 integ_old = init(prob_old, solver; init_kwargs...) # Updates X2, checks the values of all species and Γ, then resets `prob_old`. X2_new = rand(rng, 1.0:10.0) @@ -485,7 +479,7 @@ end # Checks that jump `System`s with conservation laws cannot be generated. let rn = @reaction_network begin - (k1, k2), X1 <--> X2 + (k1,k2), X1 <--> X2 end @test_throws ArgumentError jump_model(rn; remove_conserved = true) end @@ -495,8 +489,8 @@ end let # Creates ODE System with conserved quantities. rs = @reaction_network begin - (k1, k2), X1 <--> X2 - (k1, k2), Y1 <--> Y2 + (k1,k2), X1 <--> X2 + (k1,k2), Y1 <--> Y2 end osys = ode_model(rs; remove_conserved = true) @@ -515,7 +509,7 @@ let @parameters k[1:2] rxs = [ Reaction(k[1], [X[1]], [X[2]]), - Reaction(k[2], [X[2]], [X[1]]), + Reaction(k[2], [X[2]], [X[1]]) ] @named rs = ReactionSystem(rxs, t) rs = complete(rs) @@ -524,7 +518,7 @@ let u0 = [X => [3.0, 9.0]] ps = [k => [1.0, 2.0]] oprob = ODEProblem(rs, u0, (0.0, 1000.0), ps; remove_conserved = true) - sol = solve(oprob, Vern7(); abstol = 1.0e-8, reltol = 1.0e-8) + sol = solve(oprob, Vern7(); abstol = 1e-8, reltol = 1e-8) @test sol[X[1]][end] ≈ 8.0 @test sol[X[2]][end] ≈ 4.0 end @@ -533,8 +527,8 @@ end let # Create models. rn = @reaction_network begin - (k1, k2), X1 <--> X2 - (k3, k4), X1 + X2 <--> 2X3 + (k1,k2), X1 <--> X2 + (k3,k4), X1 + X2 <--> 2X3 end u0 = [:X1 => 1.0, :X2 => 2.0, :X3 => 3.0] ps = [:k1 => 0.1, :k2 => 0.2, :k3 => 0.3, :k4 => 0.4] diff --git a/test/network_analysis/crn_theory.jl b/test/network_analysis/crn_theory.jl index 312317d86a..90a5d31d4d 100644 --- a/test/network_analysis/crn_theory.jl +++ b/test/network_analysis/crn_theory.jl @@ -131,7 +131,7 @@ end function testreversibility(rn, B, rev, weak_rev) @test isreversible(rn) == rev subrn = subnetworks(rn) - return @test isweaklyreversible(rn, subrn) == weak_rev + @test isweaklyreversible(rn, subrn) == weak_rev end # Tests reversibility for networks with known reversibility. @@ -256,9 +256,7 @@ let end let - rn = @reaction_network begin - (k2, k1), A + B <--> 2A - end + rn = @reaction_network begin (k2, k1), A + B <--> 2A end rev = true weak_rev = true testreversibility(rn, reactioncomplexes(rn)[2], rev, weak_rev) @@ -443,9 +441,9 @@ let end rcs, D = reactioncomplexes(rn) - rates1 = [:k1 => 1.0, :k2 => 1.0, :k3 => 1.0, :k4 => 1.0, :k5 => 1.0, :k6 => 1.0] + rates1 = [:k1=>1.0, :k2=>1.0, :k3=>1.0, :k4=>1.0, :k5=>1.0, :k6=>1.0] @test Catalyst.isdetailedbalanced(rn, rates1) == true - rates2 = [:k1 => 2.0, :k2 => 1.0, :k3 => 1.0, :k4 => 1.0, :k5 => 1.0, :k6 => 1.0] + rates2 = [:k1=>2.0, :k2=>1.0, :k3=>1.0, :k4=>1.0, :k5=>1.0, :k6=>1.0] @test Catalyst.isdetailedbalanced(rn, rates2) == false end @@ -470,9 +468,9 @@ let @test Catalyst.isdetailedbalanced(rn, rates) == false # Adjust rate constants to obey the independent cycle conditions. - rates[p[6]] = rates[p[1]] * rates[p[4]] * rates[p[5]] / (rates[p[2]] * rates[p[3]]) - rates[p[14]] = rates[p[13]] * rates[p[11]] * rates[p[8]] / (rates[p[12]] * rates[p[7]]) - rates[p[16]] = rates[p[8]] * rates[p[15]] * rates[p[9]] * rates[p[11]] / (rates[p[7]] * rates[p[12]] * rates[p[10]]) + rates[p[6]] = rates[p[1]]*rates[p[4]]*rates[p[5]] / (rates[p[2]]*rates[p[3]]) + rates[p[14]] = rates[p[13]]*rates[p[11]]*rates[p[8]] / (rates[p[12]]*rates[p[7]]) + rates[p[16]] = rates[p[8]]*rates[p[15]]*rates[p[9]]*rates[p[11]] / (rates[p[7]]*rates[p[12]]*rates[p[10]]) @test Catalyst.isdetailedbalanced(rn, rates) == true end @@ -495,16 +493,16 @@ let @test Catalyst.isdetailedbalanced(rn, rates) == false # Adjust rate constants to fulfill independent cycle conditions. - rates[p[8]] = rates[p[7]] * rates[p[5]] * rates[p[9]] / (rates[p[6]] * rates[p[10]]) - rates[p[3]] = rates[p[2]] * rates[p[4]] * rates[p[9]] / (rates[p[1]] * rates[p[10]]) + rates[p[8]] = rates[p[7]]*rates[p[5]]*rates[p[9]] / (rates[p[6]]*rates[p[10]]) + rates[p[3]] = rates[p[2]]*rates[p[4]]*rates[p[9]] / (rates[p[1]]*rates[p[10]]) @test Catalyst.isdetailedbalanced(rn, rates) == false # Should still fail - doesn't satisfy spanning forest conditions. # Adjust rate constants to fulfill spanning forest conditions. cons = rates[p[6]] / rates[p[5]] rates[p[1]] = rates[p[2]] * cons - rates[p[9]] = rates[p[10]] * cons^(3 / 2) - rates[p[8]] = rates[p[7]] * rates[p[5]] * rates[p[9]] / (rates[p[6]] * rates[p[10]]) - rates[p[3]] = rates[p[2]] * rates[p[4]] * rates[p[9]] / (rates[p[1]] * rates[p[10]]) + rates[p[9]] = rates[p[10]] * cons^(3/2) + rates[p[8]] = rates[p[7]]*rates[p[5]]*rates[p[9]] / (rates[p[6]]*rates[p[10]]) + rates[p[3]] = rates[p[2]]*rates[p[4]]*rates[p[9]] / (rates[p[1]]*rates[p[10]]) @test Catalyst.isdetailedbalanced(rn, rates) == true end diff --git a/test/network_analysis/network_properties.jl b/test/network_analysis/network_properties.jl index 662e51b7b6..77c5f8fe5c 100644 --- a/test/network_analysis/network_properties.jl +++ b/test/network_analysis/network_properties.jl @@ -25,7 +25,7 @@ end # Tests network analysis functions on MAPK network (by comparing to manually computed outputs). let MAPK = @reaction_network MAPK begin - (k₁, k₂), KKK + E1 <--> KKKE1 + (k₁, k₂),KKK + E1 <--> KKKE1 k₃, KKKE1 --> KKK_ + E1 (k₄, k₅), KKK_ + E2 <--> KKKE2 k₆, KKKE2 --> KKK + E2 @@ -35,7 +35,7 @@ let k₁₂, KKPKKK_ --> KKPP + KKK_ (k₁₃, k₁₄), KKP + KKPase <--> KKPKKPase k₁₅, KKPPKKPase --> KKP + KKPase - k₁₆, KKPKKPase --> KK + KKPase + k₁₆,KKPKKPase --> KK + KKPase (k₁₇, k₁₈), KKPP + KKPase <--> KKPPKKPase (k₁₉, k₂₀), KKPP + K <--> KKPPK k₂₁, KKPPK --> KKPP + KP @@ -63,10 +63,10 @@ let @test Catalyst.iscomplexbalanced(MAPK, rates) == false cyclemat = Catalyst.cycles(MAPK) S = netstoichmat(MAPK) - for i in 1:(size(S, 2) - 1) - if S[:, i] == -S[:, i + 1] - cycle = [(j == i) || (j == i + 1) ? 1 : 0 for j in 1:size(S, 2)] - @test rank(cyclemat) == rank(hcat(cyclemat, cycle)) + for i in 1:size(S, 2)-1 + if S[:,i] == -S[:,i+1] + cycle = [(j == i) || (j == i+1) ? 1 : 0 for j in 1:size(S,2)] + @test rank(cyclemat) == rank(hcat(cyclemat, cycle)) end end end @@ -76,9 +76,9 @@ let rn2 = @reaction_network begin (k₁, k₂), E + S1 <--> ES1 (k₃, k₄), E + S2 <--> ES2 - (k₅, k₆), S2 + ES1 <--> ES1S2 + (k₅, k₆), S2 + ES1 <--> ES1S2 (k₆, k₇), ES1S2 <--> S1 + ES2 - k₈, ES1S2 --> E + P + k₈, ES1S2 --> E+P (k₉, k₁₀), S1 <--> 0 (k₁₀, k₁₁), 0 <--> S2 k₁₂, P --> 0 @@ -104,16 +104,16 @@ let rn3 = @reaction_network begin (k₁, k₂), A11 <--> 0 (k₃, k₄), A11 <--> A13 - (k₅, k₆), 0 <--> A12 + (k₅, k₆), 0 <--> A12 (k₆, k₇), 0 <--> A2 k₈, A10 --> 0 (k₉, k₁₀), A12 <--> A6 - (k₁₁, k₁₂), A6 <--> A4 + (k₁₁, k₁₂), A6<--> A4 (k₁₃, k₁₄), A4 <--> A3 k₁₅, A8 --> A9 - (k₁₆, k₁₇), A8 <--> A3 + A11 + (k₁₆,k₁₇), A8 <--> A3 + A11 k₁₈, A9 --> A3 + A10 - k₁₉, A2 + A4 --> A2 + A6 + k₁₉, A2+A4 --> A2 + A6 end rcs, B = reactioncomplexes(rn3) @test length(rcs) == 15 @@ -148,8 +148,8 @@ let tslcs = terminallinkageclasses(rn) @test length(slcs) == 3 @test length(tslcs) == 2 - @test issubset([[1, 2], [3, 4, 5], [6, 7]], slcs) - @test issubset([[3, 4, 5], [6, 7]], tslcs) + @test issubset([[1,2], [3,4,5], [6,7]], slcs) + @test issubset([[3,4,5], [6,7]], tslcs) end # b) Makes the D + E --> G reaction irreversible. Thus, (D+E) becomes a non-terminal linkage class. Checks whether correctly identifies both (A, B+C) and (D+E) as non-terminal @@ -168,8 +168,8 @@ let tslcs = terminallinkageclasses(rn) @test length(slcs) == 4 @test length(tslcs) == 2 - @test issubset([[1, 2], [3, 4, 5], [6], [7]], slcs) - @test issubset([[3, 4, 5], [7]], tslcs) + @test issubset([[1,2], [3,4,5], [6], [7]], slcs) + @test issubset([[3,4,5], [7]], tslcs) end # From a), makes the B + C <--> D reaction reversible. Thus, the non-terminal (A, B+C) linkage class gets absorbed into the terminal (A, B+C, D, E, 2F) linkage class, and the terminal linkage classes and strong linkage classes coincide. @@ -188,8 +188,8 @@ let tslcs = terminallinkageclasses(rn) @test length(slcs) == 2 @test length(tslcs) == 2 - @test issubset([[1, 2, 3, 4, 5], [6, 7]], slcs) - @test issubset([[1, 2, 3, 4, 5], [6, 7]], tslcs) + @test issubset([[1,2,3,4,5], [6,7]], slcs) + @test issubset([[1,2,3,4,5], [6,7]], tslcs) end # Simple test for strong and terminal linkage classes @@ -209,8 +209,8 @@ let tslcs = terminallinkageclasses(rn) @test length(slcs) == 3 @test length(tslcs) == 2 - @test issubset([[1, 2], [3, 4], [5, 6, 7]], slcs) - @test issubset([[3, 4], [5, 6, 7]], tslcs) + @test issubset([[1,2], [3,4], [5,6,7]], slcs) + @test issubset([[3,4], [5,6,7]], tslcs) end # Cycle Test: Open Reaction Network @@ -231,31 +231,27 @@ end # From stoichiometric matrix. Reference: Trinh, 2008, https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2909134/ let - S = [ - 1 -1 0 0 -1 0 0 0 0; + S = [1 -1 0 0 -1 0 0 0 0; 0 0 0 0 1 -1 -1 -1 0; 0 1 -1 0 0 1 0 0 0; 0 0 1 0 0 0 0 0 -1; - 0 0 1 -1 0 0 2 0 0 - ] - - EFMs = [ - 1 0 1 1 0 1 1 1; - 1 0 0 1 0 0 1 0; - 0 1 0 1 0 0 0 1; - 0 1 0 1 2 2 2 1; - 0 0 1 0 0 1 0 1; - -1 1 0 0 0 0 -1 1; - 0 0 0 0 1 1 1 0; - 1 -1 1 0 -1 0 0 0; - 0 1 0 1 0 0 0 1 - ] - - cyclemat = Catalyst.cycles(S) - for i in 1:size(EFMs, 2) - EFM = EFMs[:, i] - @test rank(cyclemat) == rank(hcat(cyclemat, EFM)) - end + 0 0 1 -1 0 0 2 0 0] + + EFMs = [1 0 1 1 0 1 1 1; + 1 0 0 1 0 0 1 0; + 0 1 0 1 0 0 0 1; + 0 1 0 1 2 2 2 1; + 0 0 1 0 0 1 0 1; + -1 1 0 0 0 0 -1 1; + 0 0 0 0 1 1 1 0; + 1 -1 1 0 -1 0 0 0; + 0 1 0 1 0 0 0 1] + + cyclemat = Catalyst.cycles(S) + for i in 1:size(EFMs, 2) + EFM = EFMs[:, i] + @test rank(cyclemat) == rank(hcat(cyclemat, EFM)) + end end # No cycles should exist in the following network (the graph is treelike and irreversible) @@ -284,7 +280,7 @@ let # Declares network. rs = @reaction_network begin k1, X1 + X2 --> X3 + X4 - (k2, k2), X3 + X4 <--> X1 + (k2,k2), X3 + X4 <--> X1 k3, X1 --> X2 k4, X1 + X2 --> X2 end @@ -331,7 +327,7 @@ end let MAPK = @reaction_network MAPK begin - (k₁, k₂), KKK + E1 <--> KKKE1 + (k₁, k₂),KKK + E1 <--> KKKE1 k₃, KKKE1 --> KKK_ + E1 (k₄, k₅), KKK_ + E2 <--> KKKE2 k₆, KKKE2 --> KKK + E2 @@ -341,7 +337,7 @@ let k₁₂, KKPKKK_ --> KKPP + KKK_ (k₁₃, k₁₄), KKP + KKPase <--> KKPKKPase k₁₅, KKPPKKPase --> KKP + KKPase - k₁₆, KKPKKPase --> KK + KKPase + k₁₆,KKPKKPase --> KK + KKPase (k₁₇, k₁₈), KKPP + KKPase <--> KKPPKKPase (k₁₉, k₂₀), KKPP + K <--> KKPPK k₂₁, KKPPK --> KKPP + KP @@ -355,34 +351,33 @@ let Φ = Catalyst.massactionvector(MAPK) specs = species(MAPK) - truevec = [ - MAPK.KKK * MAPK.E1, - MAPK.KKKE1, - MAPK.KKK_ * MAPK.E1, - MAPK.KKK_ * MAPK.E2, - MAPK.KKKE2, - MAPK.KKK * MAPK.E2, - MAPK.KK * MAPK.KKK_, - MAPK.KK_KKK_, - MAPK.KKP * MAPK.KKK_, - MAPK.KKPKKK_, - MAPK.KKPP * MAPK.KKK_, - MAPK.KKP * MAPK.KKPase, - MAPK.KKPKKPase, - MAPK.KKPPKKPase, - MAPK.KK * MAPK.KKPase, - MAPK.KKPP * MAPK.KKPase, - MAPK.KKPP * MAPK.K, - MAPK.KKPPK, - MAPK.KKPP * MAPK.KP, - MAPK.KPKKPP, - MAPK.KPP * MAPK.KKPP, - MAPK.KP * MAPK.KPase, - MAPK.KPKPase, - MAPK.KKPPKPase, - MAPK.K * MAPK.KPase, - MAPK.KPP * MAPK.KPase, - ] + truevec = [MAPK.KKK * MAPK.E1, + MAPK.KKKE1, + MAPK.KKK_ * MAPK.E1, + MAPK.KKK_ * MAPK.E2, + MAPK.KKKE2, + MAPK.KKK * MAPK.E2, + MAPK.KK * MAPK.KKK_, + MAPK.KK_KKK_, + MAPK.KKP * MAPK.KKK_, + MAPK.KKPKKK_, + MAPK.KKPP * MAPK.KKK_, + MAPK.KKP * MAPK.KKPase, + MAPK.KKPKKPase, + MAPK.KKPPKKPase, + MAPK.KK * MAPK.KKPase, + MAPK.KKPP * MAPK.KKPase, + MAPK.KKPP * MAPK.K, + MAPK.KKPPK, + MAPK.KKPP * MAPK.KP, + MAPK.KPKKPP, + MAPK.KPP * MAPK.KKPP, + MAPK.KP * MAPK.KPase, + MAPK.KPKPase, + MAPK.KKPPKPase, + MAPK.K * MAPK.KPase, + MAPK.KPP * MAPK.KPase, + ] @test isequal(Φ, truevec) K = Catalyst.fluxmat(MAPK) @@ -398,9 +393,9 @@ let @test isequal(K[1, 1], MAPK.k₁) @test all(==(0), K[1, 2:end]) @test isequal(K[2, 2], MAPK.k₂) - @test all(==(0), vcat(K[2, 1], K[2, 3:end])) + @test all(==(0), vcat(K[2,1], K[2,3:end])) @test isequal(K[3, 2], MAPK.k₃) - @test all(==(0), vcat(K[3, 1], K[3, 3:end])) + @test all(==(0), vcat(K[3,1], K[3,3:end])) @test count(k -> !isequal(k, 0), K) == length(reactions(MAPK)) A_k = Catalyst.laplacianmat(MAPK) @@ -408,11 +403,11 @@ let S = netstoichmat(MAPK) Y = complexstoichmat(MAPK) - @test isequal(S * K, Y * A_k) + @test isequal(S*K, Y*A_k) eqs = Catalyst.assemble_oderhs(MAPK, specs) - @test all(_iszero, simplify(eqs - S * K * Φ)) - @test_broken all(_iszero, simplify(eqs - Y * A_k * Φ)) # Does not simplify to 0 (it should) due to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. + @test all(_iszero, simplify(eqs - S*K*Φ)) + @test_broken all(_iszero, simplify(eqs - Y*A_k*Φ)) # Does not simplify to 0 (it should) due to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. # Test using numbers k = rand(rng, numparams(MAPK)) @@ -430,8 +425,8 @@ let for i in 1:length(eqs) numeqs[i] = substitute(eqs[i], ratemap) end - @test all(_iszero, simplify(numeqs - S * K * Φ)) - @test all(_iszero, simplify(numeqs - Y * A_k * Φ)) + @test all(_iszero, simplify(numeqs - S*K*Φ)) + @test all(_iszero, simplify(numeqs - Y*A_k*Φ)) end # Test handling for weird complexes and combinatoric rate laws. @@ -443,19 +438,15 @@ let Φ = Catalyst.massactionvector(rn) specs = species(rn) - crvec = [ - rn.X^2 / 2 * rn.Y * rn.Z^3 / 6, - 1.0, - rn.Y^2 / 2 * rn.Z^2 / 2, - rn.X^3 / 6, - ] + crvec = [rn.X^2/2 * rn.Y * rn.Z^3/6, + 1., + rn.Y^2/2 * rn.Z^2/2, + rn.X^3/6] @test isequal(Φ, crvec) - ncrvec = [ - rn.X^2 * rn.Y * rn.Z^3, - 1.0, - rn.Y^2 * rn.Z^2, - rn.X^3, - ] + ncrvec = [rn.X^2 * rn.Y * rn.Z^3, + 1., + rn.Y^2 * rn.Z^2, + rn.X^3] Φ_2 = Catalyst.massactionvector(rn; combinatoric_ratelaws = false) @test isequal(Φ_2, ncrvec) @@ -465,12 +456,12 @@ let Y = complexstoichmat(rn) K = fluxmat(rn) A_k = laplacianmat(rn) - @test all(_iszero, simplify(eqs - S * K * Φ)) - @test_broken all(_iszero, simplify(eqs - Y * A_k * Φ)) # Does not simplify to 0 (it should) due to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. + @test all(_iszero, simplify(eqs - S*K*Φ)) + @test_broken all(_iszero, simplify(eqs - Y*A_k*Φ)) # Does not simplify to 0 (it should) due to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. eq_ncr = Catalyst.assemble_oderhs(rn, specs; combinatoric_ratelaws = false) - @test all(_iszero, simplify(eq_ncr - S * K * Φ_2)) - @test_broken all(_iszero, simplify(eq_ncr - Y * A_k * Φ_2)) # Does not simplify to 0 (it should) due to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. + @test all(_iszero, simplify(eq_ncr - S*K*Φ_2)) + @test_broken all(_iszero, simplify(eq_ncr - Y*A_k*Φ_2)) # Does not simplify to 0 (it should) due to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. # Test that the ODEs with rate constants are the same. k = rand(rng, numparams(rn)) @@ -484,37 +475,37 @@ let numeqs[i] = substitute(eqs[i], ratemap) end @test_broken false # all(_iszero, simplify(numeqs - S*K*Φ)) # Sometimes does not simplify to 0 (it should), but something very close. Potentially related to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. - @test_broken all(_iszero, simplify(numeqs - Y * A_k * Φ)) # Does not simplify to 0 (it should). Potentially related to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. + @test_broken all(_iszero, simplify(numeqs - Y*A_k*Φ)) # Does not simplify to 0 (it should). Potentially related to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. numeqs_ncr = similar(eq_ncr) for i in 1:length(eq_ncr) numeqs_ncr[i] = substitute(eq_ncr[i], ratemap) end - @test all(_iszero, simplify(numeqs_ncr - S * K * Φ_2)) - @test_broken all(_iszero, simplify(numeqs_ncr - Y * A_k * Φ_2)) # Does not simplify to 0 (it should). Potentially related to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. + @test all(_iszero, simplify(numeqs_ncr - S*K*Φ_2)) + @test_broken all(_iszero, simplify(numeqs_ncr - Y*A_k*Φ_2)) # Does not simplify to 0 (it should). Potentially related to https://github.com/JuliaSymbolics/Symbolics.jl/issues/1739. Not a Catalyst bug. # Test that handling of species concentrations is correct. - u0vec = [:X => 3.0, :Y => 0.5, :Z => 2.0] + u0vec = [:X => 3., :Y => .5, :Z => 2.] u0map = Dict(u0vec) u0tup = Tuple(u0vec) Φ = Catalyst.massactionvector(rn, u0vec) - @test isequal(Φ[1], 3.0) + @test isequal(Φ[1], 3.) Φ_2 = Catalyst.massactionvector(rn, u0tup; combinatoric_ratelaws = false) - @test isequal(Φ_2[1], 36.0) + @test isequal(Φ_2[1], 36.) Φ = Catalyst.massactionvector(rn, u0map) - @test isequal(Φ[1], 3.0) + @test isequal(Φ[1], 3.) # Test full simplification. u0map = Catalyst.symmap_to_varmap(rn, u0map) numeqs = [substitute(eq, u0map) for eq in numeqs] - for (v1, v2, v3) in zip(numeqs, S * K * Φ, Y * A_k * Φ) + for (v1,v2,v3) in zip(numeqs, S*K*Φ, Y*A_k*Φ) @test SymbolicUtils.unwrap_const(v1) ≈ SymbolicUtils.unwrap_const(ModelingToolkitBase.unwrap(v2)) @test SymbolicUtils.unwrap_const(v1) ≈ SymbolicUtils.unwrap_const(ModelingToolkitBase.unwrap(v3)) end numeqs_ncr = [substitute(eq, u0map) for eq in numeqs_ncr] - for (v1, v2, v3) in zip(numeqs_ncr, S * K * Φ_2, Y * A_k * Φ_2) + for (v1,v2,v3) in zip(numeqs_ncr, S*K*Φ_2, Y*A_k*Φ_2) @test SymbolicUtils.unwrap_const(v1) ≈ SymbolicUtils.unwrap_const(ModelingToolkitBase.unwrap(v2)) @test SymbolicUtils.unwrap_const(v1) ≈ SymbolicUtils.unwrap_const(ModelingToolkitBase.unwrap(v3)) end @@ -523,13 +514,13 @@ end ### Error Handling Tests ### # Tests that non-integer stoichiometry throws a helpful error in network analysis functions. -let +let # Create a network with non-integer stoichiometry. rn = @reaction_network begin - k1, 2.5 * A --> B + k1, 2.5*A --> B k2, B --> C end - + # Test that network analysis functions that require integer stoichiometry error appropriately. @test_throws ErrorException reactioncomplexes(rn) @test_throws ErrorException reactioncomplexmap(rn) @@ -548,10 +539,10 @@ let @test occursin("non-integer stoichiometry", e.msg) @test occursin("Network analysis functions", e.msg) end - + # Confirm that integer stoichiometry works fine. rn_int = @reaction_network begin - k1, 2 * A --> B + k1, 2*A --> B k2, B --> C end @test length(reactioncomplexes(rn_int)[1]) == 3 diff --git a/test/reactionsystem_core/coupled_equation_crn_systems.jl b/test/reactionsystem_core/coupled_equation_crn_systems.jl index d16ecfb3b4..a7daef61d2 100644 --- a/test/reactionsystem_core/coupled_equation_crn_systems.jl +++ b/test/reactionsystem_core/coupled_equation_crn_systems.jl @@ -25,7 +25,7 @@ let eqs = [ Reaction(p, nothing, [X]), Reaction(d, [X], nothing), - D(A) ~ p * X - k * A, + D(A) ~ p*X - k*A ] @named coupled_rs = ReactionSystem(eqs, t) coupled_rs = complete(coupled_rs) @@ -46,18 +46,18 @@ let # Checks that the correct steady state is found through ODEProblem. oprob = ODEProblem(coupled_rs, u0, tspan, ps) - osol = solve(oprob, Vern7(); abstol = 1.0e-8, reltol = 1.0e-8) - @test osol[[X, A]][end] ≈ [2.0, 1.0] + osol = solve(oprob, Vern7(); abstol = 1e-8, reltol = 1e-8) + @test osol[[X,A]][end] ≈ [2.0, 1.0] # Checks that the correct steady state is found through NonlinearProblem. nlprob = NonlinearProblem(coupled_rs, u0, ps) - nlsol = solve(nlprob; abstol = 1.0e-8, reltol = 1.0e-8) - @test nlsol[[X, A]] ≈ [2.0, 1.0] + nlsol = solve(nlprob; abstol = 1e-8, reltol = 1e-8) + @test nlsol[[X,A]] ≈ [2.0, 1.0] # Checks that the correct steady state is found through SteadyStateProblem. ssprob = SteadyStateProblem(coupled_rs, u0, ps) - sssol = solve(ssprob, DynamicSS(Rosenbrock23()); abstol = 1.0e-8, reltol = 1.0e-8) - @test sssol[[X, A]] ≈ [2.0, 1.0] + sssol = solve(ssprob, DynamicSS(Rosenbrock23()); abstol = 1e-8, reltol = 1e-8) + @test sssol[[X,A]] ≈ [2.0, 1.0] end # Checks that coupled systems created via the DSL, extension, and programmatically are identical. @@ -74,15 +74,15 @@ let eqs_prog = [ D(A) ~ X1 + a - A, D(B) ~ X2 + b - B, - Reaction(k1 * A, [X1], [X2]), - Reaction(k2 * B, [X2], [X1]), + Reaction(k1*A, [X1], [X2]), + Reaction(k2*B, [X2], [X1]) ] coupled_rs_prog = ReactionSystem(eqs_prog, t, [A, B, X1, X2], [k1, k2, a, b]; name = :coupled_rs) coupled_rs_prog = complete(coupled_rs_prog) # Creates model by extending a `ReactionSystem` with another ReactionSystem containing ODEs. rn_extended = @network_component begin - ($k1 * $A, $k2 * $B), X1 <--> X2 + ($k1*$A, $k2*$B), X1 <--> X2 end eqs_extended = [ D(A) ~ X1 + a - A @@ -98,7 +98,7 @@ let D(A) ~ X1 + a - A D(B) ~ X2 + b - B end - (k1 * A, k2 * B), X1 <--> X2 + (k1*A, k2*B), X1 <--> X2 end # Checks that models are equivalent and contain the correct stuff. @@ -112,12 +112,12 @@ let # Simulates the three models, checking that they all yield the correct end point. u0 = [A => 1.0, B => 1.0, X1 => 10.0, X2 => 10.0] - tspan = (0.0, 100.0) + tspan = (0.0, 100.) ps = [a => 1.0, b => 1.0, k1 => 1.0, k2 => 1.0] for coupled_rs in [coupled_rs_prog, coupled_rs_extended, coupled_rs_dsl] oprob = ODEProblem(coupled_rs, u0, tspan, ps) - osol = solve(oprob, Vern7(); abstol = 1.0e-8, reltol = 1.0e-8) - osol[[A, B, X1, X2]][end] ≈ [10.0, 10.0, 11.0, 11.0] + osol = solve(oprob, Vern7(); abstol = 1e-8, reltol = 1e-8) + osol[[A,B,X1,X2]][end] ≈ [10.0, 10.0, 11.0, 11.0] end end @@ -135,7 +135,7 @@ let eqs = [ Reaction(p, nothing, [X]), Reaction(d, [X], nothing), - a * A^2 ~ X + b, + a*A^2 ~ X + b ] @named coupled_rs = ReactionSystem(eqs, t) coupled_rs = complete(coupled_rs) @@ -159,24 +159,22 @@ let @test_throws Exception SteadyStateProblem(coupled_rs, u0, ps) # Checks that the correct steady state is found through ODEProblem. - oprob = ODEProblem( - coupled_rs, u0, tspan, ps; mtkcompile = true, - guesses = [A => 1.0] - ) - osol = solve(oprob, Rosenbrock23(); abstol = 1.0e-8, reltol = 1.0e-8) - @test osol[[X, A]][end] ≈ [2.0, 3.0] + oprob = ODEProblem(coupled_rs, u0, tspan, ps; mtkcompile = true, + guesses = [A => 1.0]) + osol = solve(oprob, Rosenbrock23(); abstol = 1e-8, reltol = 1e-8) + @test osol[[X,A]][end] ≈ [2.0, 3.0] # Checks that the correct steady state is found through NonlinearProblem. - u0 = [X => 0.1, A => 16.1 / 2] + u0 = [X => 0.1, A => 16.1/2] nlprob = NonlinearProblem(coupled_rs, u0, ps, mtkcompile = true) nlsol = solve(nlprob) - @test nlsol[[X, A]] ≈ [2.0, 3.0] + @test nlsol[[X,A]] ≈ [2.0, 3.0] # Checks that the correct steady state is found through SteadyStateProblem. u0 = [X => 0.1, A => 1.0] ssprob = SteadyStateProblem(coupled_rs, u0, ps; mtkcompile = true) - sssol = solve(ssprob, DynamicSS(Rosenbrock23()); abstol = 1.0e-8, reltol = 1.0e-8) - @test_broken sssol[[X, A]] ≈ [2.0, 3.0] # The previous lines fails to solve. Issue at: https://github.com/SciML/ModelingToolkit.jl/issues/4174. This currently also yields a warning. + sssol = solve(ssprob, DynamicSS(Rosenbrock23()); abstol = 1e-8, reltol = 1e-8) + @test_broken sssol[[X,A]] ≈ [2.0, 3.0] # The previous lines fails to solve. Issue at: https://github.com/SciML/ModelingToolkit.jl/issues/4174. This currently also yields a warning. end @@ -197,7 +195,7 @@ let Δ(B) ~ sqrt(A + X + b) - B, Reaction(p, nothing, [X], nothing, [2]), Reaction(d, [X], nothing), - (X + C) * B ~ A, + (X + C)*B ~ A ] @named coupled_rs = ReactionSystem(eqs, τ) coupled_rs = complete(coupled_rs) @@ -208,18 +206,14 @@ let # Creates and solves a ODE, SteadyState, and Nonlinear problems. # Success is tested by checking that the same steady state solution is found. - oprob = ODEProblem( - coupled_rs, u0, (0.0, 1000.0), ps; mtkcompile = true, - warn_initialize_determined = false - ) - ssprob = SteadyStateProblem( - coupled_rs, u0, ps; mtkcompile = true, - warn_initialize_determined = false - ) + oprob = ODEProblem(coupled_rs, u0, (0.0, 1000.0), ps; mtkcompile = true, + warn_initialize_determined = false) + ssprob = SteadyStateProblem(coupled_rs, u0, ps; mtkcompile = true, + warn_initialize_determined = false) nlprob = NonlinearProblem(coupled_rs, u0, ps; mtkcompile = true) - osol = solve(oprob, Rosenbrock23(); abstol = 1.0e-8, reltol = 1.0e-8) - sssol = solve(ssprob, DynamicSS(Rosenbrock23()); abstol = 1.0e-8, reltol = 1.0e-8) - nlsol = solve(nlprob; abstol = 1.0e-8, reltol = 1.0e-8) + osol = solve(oprob, Rosenbrock23(); abstol = 1e-8, reltol = 1e-8) + sssol = solve(ssprob, DynamicSS(Rosenbrock23()); abstol = 1e-8, reltol = 1e-8) + nlsol = solve(nlprob; abstol = 1e-8, reltol = 1e-8) @test osol[[A, B, C, X]][end] ≈ sssol[[A, B, C, X]] ≈ nlsol[[A, B, C, X]] end @@ -239,8 +233,8 @@ let Reaction(p, [], [X]), Reaction(d, [X], []), Reaction(d, [X], nothing, [2], nothing), - D(V) ~ X - v * V, - W^2 ~ log(V) + X, + D(V) ~ X - v*V, + W^2 ~ log(V) + X ] @named coupled_rs = ReactionSystem(eqs, t) @@ -286,8 +280,8 @@ let eqs = [ Reaction(k1, [X], [X2], [2], [1]), Reaction(k2, [X2], [X], [1], [2]), - D(A) ~ k * X2 - A, - B + A ~ b1 * X + b2 * X2, + D(A) ~ k*X2 - A, + B + A ~ b1*X + b2*X2 ] @named coupled_rs = ReactionSystem(eqs, τ) coupled_rs = complete(coupled_rs) @@ -310,25 +304,25 @@ end # Checks that metadata, types, and default values are carried through correctly. let # Creates the model - @parameters a1 [description = "Parameter a1"] a2::Rational{Int64} a3 = 0.3 a4::Rational{Int64} = 4 // 10 [description = "Parameter a4"] - @parameters b1 [description = "Parameter b1"] b2::Int64 b3 = 3 b4::Int64 = 4 [description = "Parameter b4"] - @parameters c1 [description = "Parameter c1"] c2::Float32 c3 = 30.0 c4::Float32 = 40.0 [description = "Parameter c4"] - @species A1(t) [description = "Species A1"] A2(t) = 0.2 A3(t) = 0.3 [description = "Species A3"] A4(t) - @variables B1(t) [description = "Variable B1"] B2(t) = 2.0 B3(t) = 3.0 [description = "Variable B3"] B4(t) - @variables C1(t) [description = "Variable C1"] C2(t) C3(t) [description = "Variable C3"] C4(t) + @parameters a1 [description="Parameter a1"] a2::Rational{Int64} a3=0.3 a4::Rational{Int64}=4//10 [description="Parameter a4"] + @parameters b1 [description="Parameter b1"] b2::Int64 b3 = 3 b4::Int64=4 [description="Parameter b4"] + @parameters c1 [description="Parameter c1"] c2::Float32 c3=30.0 c4::Float32=40.0 [description="Parameter c4"] + @species A1(t) [description="Species A1"] A2(t)=0.2 A3(t)=0.3 [description="Species A3"] A4(t) + @variables B1(t) [description="Variable B1"] B2(t)=2.0 B3(t)=3.0 [description="Variable B3"] B4(t) + @variables C1(t) [description="Variable C1"] C2(t) C3(t) [description="Variable C3"] C4(t) eqs = [ Reaction(a1, [A1], nothing), Reaction(a2, [A2], nothing), Reaction(a3, [A3], nothing), Reaction(a4, [A4], nothing), - D(B1) ~ b1 * B1, - D(B2) ~ b2 * B2, - D(B3) ~ b3 * B3, - D(B4) ~ b4 * B4, + D(B1) ~ b1*B1, + D(B2) ~ b2*B2, + D(B3) ~ b3*B3, + D(B4) ~ b4*B4, C1 ~ sqrt(c1 + B1^5), C2 ~ sqrt(c2 + B2^5), C3 ~ sqrt(c3 + B3^5), - C4 ~ sqrt(c4 + B4^5), + C4 ~ sqrt(c4 + B4^5) ] @named coupled_rs = ReactionSystem(eqs, t) coupled_rs = complete(coupled_rs) @@ -362,7 +356,7 @@ let @test getdescription(coupled_rs.c1) == "Parameter c1" @test getdescription(coupled_rs.c4) == "Parameter c4" @test getdefault(coupled_rs.a3) == 0.3 - @test getdefault(coupled_rs.a4) == 4 // 10 + @test getdefault(coupled_rs.a4) == 4//10 @test getdefault(coupled_rs.b3) == 3 @test getdefault(coupled_rs.b4) == 4 @test getdefault(coupled_rs.c3) == 30 @@ -382,7 +376,7 @@ let u0 = [A1 => 0.1, A4 => 0.4, B1 => 1.0, B4 => 4.0] u0_nlp = [A1 => 0.1, A4 => 0.4, B1 => 1.0, B4 => 4.0, C1 => sqrt(10.0 + 1.0^5), C2 => sqrt(20.0 + 2.0^5), C3 => sqrt(30.0 + 3.0^5), C4 => sqrt(40.0 + 4.0^5)] tspan = (0.0, 1.0) - ps = [a1 => 0.1, a2 => 2 // 10, b1 => 1.0, b2 => 2, c1 => 10.0, c2 => 20.0] + ps = [a1 => 0.1, a2 => 2//10, b1 => 1.0, b2 => 2, c1 => 10.0, c2 => 20.0] # Create ODE structures. oprob = ODEProblem(coupled_rs, u0, tspan, ps; mtkcompile = true, warn_initialize_determined = false) @@ -403,9 +397,9 @@ let for mtk_struct in [oprob, oint, osol, sprob, sint, ssol, nlprob, nlint, nlsol] # Parameters. @test mtk_struct.ps[a1] == 0.1 - @test_broken mtk_struct.ps[a2] == 2 // 10 # An equivalent value (but different nominator and denominator) is generated. https://github.com/SciML/ModelingToolkit.jl/issues/4163. + @test_broken mtk_struct.ps[a2] == 2//10 # An equivalent value (but different nominator and denominator) is generated. https://github.com/SciML/ModelingToolkit.jl/issues/4163. @test mtk_struct.ps[a3] == 0.3 - @test mtk_struct.ps[a4] == 4 // 10 + @test mtk_struct.ps[a4] == 4//10 @test mtk_struct.ps[b1] == 1.0 @test mtk_struct.ps[b2] == 2 @test mtk_struct.ps[b3] == 3.0 @@ -467,8 +461,8 @@ let eqs = [ Reaction(p, nothing, [X]; metadata = [:noise_scaling => 0.1]), Reaction(d, [X], nothing; metadata = [:noise_scaling => 0.1]), - D(A) ~ X - k1 * A, - D(B) ~ k2 - B, + D(A) ~ X - k1*A, + D(B) ~ k2 - B ] @named coupled_rs = ReactionSystem(eqs, t) coupled_rs = complete(coupled_rs) @@ -480,9 +474,9 @@ let # Checks that the simulations have the expected means (or endpoint, for B). sprob = SDEProblem(coupled_rs, u0, tspan, ps) - ssol = solve(sprob, ImplicitEM(); maxiters = 1.0e9, seed) - @test mean(ssol[:X]) ≈ 100.0 atol = 1.0e-2 rtol = 1.0e-2 - @test mean(ssol[:A]) ≈ 50.0 atol = 1.0e-2 rtol = 1.0e-2 + ssol = solve(sprob, ImplicitEM(); maxiters = 1e9, seed) + @test mean(ssol[:X]) ≈ 100.0 atol = 1e-2 rtol = 1e-2 + @test mean(ssol[:A]) ≈ 50.0 atol = 1e-2 rtol = 1e-2 @test ssol[:B][end] ≈ 20.0 end @@ -496,7 +490,7 @@ let eqs = [ Reaction(p, nothing, [X]), Reaction(d, [X], nothing), - 2 + k1 * A ~ 3 + k2 * X, + 2 + k1 * A ~ 3 + k2 * X ] @named coupled_rs = ReactionSystem(eqs, t) coupled_rs = complete(coupled_rs) @@ -511,8 +505,8 @@ let # Checks the algebraic equation holds. sprob = SDEProblem(coupled_rs, u0, tspan, ps; guesses = [A => 1.0], mtkcompile = true, warn_initialize_determined = false) - ssol = solve(sprob, ImplicitEM(); abstol = 1.0e-5, reltol = 1.0e-5) - @test (2 .+ ps[k1] * ssol[:A]) ≈ (3 .+ ps[k2] * ssol[:X]) atol = 1.0e-1 rtol = 1.0e-1 + ssol = solve(sprob, ImplicitEM(); abstol = 1e-5, reltol = 1e-5) + @test (2 .+ ps[k1] * ssol[:A]) ≈ (3 .+ ps[k2] * ssol[:X]) atol = 1e-1 rtol = 1e-1 end @@ -534,7 +528,7 @@ let @differentials D = Differential(t) @variables V(t) @equations D(D(V)) ~ 1.0 - V - (p, d), 0 <--> X + (p,d), 0 <--> X end @test_throws Exception ss_ode_model(rs) end @@ -544,7 +538,7 @@ let rs = @reaction_network begin @variables U(t) @equations D(V) ~ 1.0 - V + D(U) - (p, d), 0 <--> X + (p,d), 0 <--> X end @test_throws Exception ss_ode_model(rs) end @@ -555,7 +549,7 @@ let @differentials D = Differential(t) @variables V(t) @equations D(V) + V ~ 1.0 - V - (p, d), 0 <--> X + (p,d), 0 <--> X end @test_throws Exception ss_ode_model(rs) end @@ -574,8 +568,8 @@ end eqs = [ Reaction(p, nothing, [X]), Reaction(d, [X], nothing), - D(D(A)) + 2ω * D(A) + (ω^2) * A ~ 0, - A + k * (B + D(A)) ~ X, + D(D(A)) + 2ω*D(A) +(ω^2)*A ~ 0, + A + k*(B + D(A)) ~ X ] @named coupled_rs = ReactionSystem(eqs, t) coupled_rs = complete(coupled_rs) @@ -584,18 +578,18 @@ end # Checks that ODE an simulation of the system achieves the correct steady state. oprob = ODEProblem(coupled_rs, u0, (0.0, 1000.0), ps; mtkcompile = true) - osol = solve(oprob, Vern7(); abstol = 1.0e-8, reltol = 1.0e-8) + osol = solve(oprob, Vern7(); abstol = 1e-8, reltol = 1e-8) @test osol[X][end] ≈ 2.0 - @test osol[A][end] ≈ 0.0 atol = 1.0e-8 - @test osol[D(A)][end] ≈ 0.0 atol = 1.0e-8 + @test osol[A][end] ≈ 0.0 atol = 1e-8 + @test osol[D(A)][end] ≈ 0.0 atol = 1e-8 @test osol[B][end] ≈ 1.0 # Checks that SteadyState simulation of the system achieves the correct steady state. ssprob = SteadyStateProblem(coupled_rs, u0, ps; mtkcompile = true) - sssol = solve(ssprob, DynamicSS(Vern7()); abstol = 1.0e-8, reltol = 1.0e-8) + sssol = solve(ssprob, DynamicSS(Vern7()); abstol = 1e-8, reltol = 1e-8) @test sssol[X][end] ≈ 2.0 - @test sssol[A][end] ≈ 0.0 atol = 1.0e-8 - @test sssol[D(A)][end] ≈ 0.0 atol = 1.0e-8 + @test sssol[A][end] ≈ 0.0 atol = 1e-8 + @test sssol[D(A)][end] ≈ 0.0 atol = 1e-8 @test sssol[B][end] ≈ 1.0 # Checks that the steady state can be found by solving a nonlinear problem. @@ -619,28 +613,28 @@ end let # Creates the model programmatically. @species X1(t) X2(t) X3(t) - @variables V(t) = 5.0 [description = "Volume"] N(t) X_conc(t) X_tot(t) + @variables V(t)=5.0 [description="Volume"] N(t) X_conc(t) X_tot(t) @parameters p k1 k2 d v n x_scale::Float32 eqs = [ Reaction(p, nothing, [X1]) Reaction(k1, [X1], [X2]) Reaction(k2, [X2], [X3]) Reaction(d, [X3], nothing) - D(V) ~ X3 / (1 + X3) - v * V - D(N) ~ - n * N * X3 - V * X_conc ~ x_scale * (X1 + X2 + X3) + D(V) ~ X3/(1+X3) - v*V + D(N) ~ - n*N*X3 + V*X_conc ~ x_scale*(X1 + X2 + X3) X_tot + X1 + X2 ~ -X3 ] rs_prog = complete(ReactionSystem(eqs, t; name = :coupled_rs)) # Creates the model via the DSL. rs_dsl = @reaction_network coupled_rs begin - @variables X_conc(t) V(t) = 5.0 [description = "Volume"] X_tot(t) + @variables X_conc(t) V(t)=5.0 [description="Volume"] X_tot(t) @parameters v n x_scale::Float32 @equations begin - D(V) ~ X3 / (1 + X3) - v * V - D(N) ~ - n * N * X3 - V * X_conc ~ x_scale * (X1 + X2 + X3) + D(V) ~ X3/(1+X3) - v*V + D(N) ~ - n*N*X3 + V*X_conc ~ x_scale*(X1 + X2 + X3) X_tot + X1 + X2 ~ -X3 end p, 0 --> X1 @@ -677,13 +671,13 @@ end let # Checks for system with a single differential equation. rs_1_line = @reaction_network rs_1 begin - @equations D(M) ~ -M * I + @equations D(M) ~ -M*I i, S + I --> 2I r, I --> R end rs_1_block = @reaction_network rs_1 begin @equations begin - D(M) ~ -M * I + D(M) ~ -M*I end i, S + I --> 2I r, I --> R @@ -715,7 +709,7 @@ let rs_1 = @reaction_network begin @variables H(t) @equations begin - D(M) ~ -M * I + D(M) ~ -M*I H ~ 100 - I end i, S + I --> 2I @@ -727,10 +721,10 @@ let # Checks for system with two differential equations, and which do not use `@variables`, rs_2 = @reaction_network coupled_rs begin @equations begin - D(V) ~ X / (1 + X) - V + D(V) ~ X/(1+X) - V D(N) ~ - V end - (p, d), 0 <--> X + (p,d), 0 <--> X end issetequal(species(rs_2), [rs_2.X]) issetequal(unknowns(rs_2)[2:3], [rs_2.V, rs_2.N]) @@ -739,10 +733,10 @@ let rs_2 = @reaction_network coupled_rs begin @variables N(t) @equations begin - D(V) ~ X / (1 + X) - V + D(V) ~ X/(1+X) - V D(N) ~ - V end - (p, d), 0 <--> X + (p,d), 0 <--> X end issetequal(species(rs_2), [rs_2.X]) issetequal(unknowns(rs_2)[2:3], [rs_2.V, rs_2.N]) @@ -752,7 +746,7 @@ end # manually, have their additional inputs properly registered. let rs = @reaction_network begin - @variables V(t) = 2.0 [description = "A variable"] + @variables V(t)=2.0 [description = "A variable"] @equations D(V) ~ -1 end @test getdefault(rs.V) == 2.0 @@ -769,37 +763,37 @@ let @parameters p q @species X(t) @variables A(t) B(t) - @equations X^2 + log(A + X) ~ 1 - sqrt(B) + sin(p + X + π) / exp(A / (1 + t)) + q + @equations X^2 + log(A+X) ~ 1 - sqrt(B) + sin(p + X + π)/exp(A/(1+t)) + q end rs_2 = @reaction_network rs begin @parameters p q @species X(t) @variables A(t) B(t) - @equations X^2 + log(A + X) + sqrt(B) - sin(p + X + π) / exp(A / (1 + t)) - q ~ 1 + @equations X^2 + log(A+X) + sqrt(B) - sin(p + X + π)/exp(A/(1+t)) - q ~ 1 end rs_3 = @reaction_network rs begin @parameters p q @species X(t) @variables A(t) B(t) - @equations X^2 + log(A + X) + sqrt(B) - sin(p + X + π) / exp(A / (1 + t)) - 1 - q ~ 0 + @equations X^2 + log(A+X) + sqrt(B) - sin(p + X + π)/exp(A/(1+t)) - 1 - q ~ 0 end rs_4 = @reaction_network rs begin @parameters p q @species X(t) @variables A(t) B(t) - @equations 0 ~ X^2 + log(A + X) + sqrt(B) - sin(p + X + π) / exp(A / (1 + t)) - 1 - q + @equations 0 ~ X^2 + log(A+X) + sqrt(B) - sin(p + X + π)/exp(A/(1+t)) - 1 - q end rs_5 = @reaction_network rs begin @parameters p q @species X(t) @variables A(t) B(t) - @equations q ~ X^2 + log(A + X) + sqrt(B) - sin(p + X + π) / exp(A / (1 + t)) - 1 + @equations q ~ X^2 + log(A+X) + sqrt(B) - sin(p + X + π)/exp(A/(1+t)) - 1 end rs_6 = @reaction_network rs begin @parameters p q @species X(t) @variables A(t) B(t) - @equations X^2 + log(A + X) + (A + B)^p ~ 1 - sqrt(B) + sin(p + X + π) / exp(A / (1 + t)) + q + (A + B)^p + @equations X^2 + log(A+X) + (A + B)^p ~ 1 - sqrt(B) + sin(p + X + π)/exp(A/(1+t)) + q + (A + B)^p end # Uses a special function to check that all equations indeed are identical. @@ -841,7 +835,7 @@ let # Declares the reaction system using the default differential and iv. rs_1 = @reaction_network begin @equations D(N) ~ -N - (p, d), 0 <--> X + (p,d), 0 <--> X end # Declares the reaction system using a new iv, and overwriting the default differential. @@ -851,7 +845,7 @@ let @variables N(τ) @differentials D = Differential(τ) @equations D(N) ~ -N - (p, d), 0 <--> X + (p,d), 0 <--> X end # Declares the reaction system using a new differential and iv. @@ -861,12 +855,12 @@ let @variables N(τ) @differentials Δ = Differential(τ) @equations Δ(N) ~ -N - (p, d), 0 <--> X + (p,d), 0 <--> X end # Simulates all three models, checking that the results are identical. u0 = [:X => 5.0, :N => 10.0] - tspan = (0.0, 10.0) + tspan = (0.0, 10.) ps = [:p => 1.0, :d => 0.2] oprob_1 = ODEProblem(rs_1, u0, tspan, ps) oprob_2 = ODEProblem(rs_2, u0, tspan, ps) @@ -920,27 +914,21 @@ let E = Differential(τ) # Variables as reaction reactants. - @test_throws Exception ReactionSystem( - [ - Reaction(p1, [S1], [V1]), - ], t; name = :rs - ) + @test_throws Exception ReactionSystem([ + Reaction(p1, [S1], [V1]) + ], t; name = :rs) # Equation with variable using non-declared independent variable. - @test_throws Exception ReactionSystem( - [ - Reaction(p1, [S1], [S2]), - E(U1) ~ S1 + p2, - ], t; name = :rs - ) + @test_throws Exception ReactionSystem([ + Reaction(p1, [S1], [S2]), + E(U1) ~ S1 + p2 + ], t; name = :rs) # Differential with respect to non-declared independent variable. - @test_throws Exception ReactionSystem( - [ - Reaction(p1, [S1], [S2]), - E(V1) ~ S1 + p2, - ], [t, τ]; name = :rs - ) + @test_throws Exception ReactionSystem([ + Reaction(p1, [S1], [S2]), + E(V1) ~ S1 + p2 + ], [t, τ]; name = :rs) end # Checks that various attempts to create `ODEProblem`s from faulty systems generate errors. @@ -953,7 +941,7 @@ let eqs = [ Reaction(p1, [S1], [S2]), V1 ~ p2 - S1, - S2 ~ V1^2 + sqrt(S2), + S2 ~ V1^2 + sqrt(S2) ] @named rs = ReactionSystem(eqs, t) rs = complete(rs) diff --git a/test/reactionsystem_core/custom_crn_functions.jl b/test/reactionsystem_core/custom_crn_functions.jl index 9a9e9041c0..c2714a05b4 100644 --- a/test/reactionsystem_core/custom_crn_functions.jl +++ b/test/reactionsystem_core/custom_crn_functions.jl @@ -46,7 +46,7 @@ let v5 * (X7^2) / (K5^2 + X7^2 + Y7^2), X7 + Y7 --> Z7 end - for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2] + for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2] u0 = rnd_u0(custom_function_network_1, rng; factor) ps = rnd_ps(custom_function_network_1, rng; factor) t = rand(rng) @@ -69,55 +69,33 @@ let @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( - derivative(Catalyst.hill(X, v, K, n), X), - n * v * (K^n) * (X^(n - 1)) / (K^n + X^n)^2 - ) + @test isequal(derivative(Catalyst.hill(X, v, K, n), X), + n * v * (K^n) * (X^(n - 1)) / (K^n + X^n)^2) @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( - derivative(Catalyst.hill(X, v, K, n), n), - v * (X^n) * (K^n) * (log(X) - log(K)) / (K^n + X^n)^2 - ) + @test isequal(derivative(Catalyst.hill(X, v, K, n), K), + -n * v * (K^(n - 1)) * (X^n) / (K^n + X^n)^2) + @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( - derivative(Catalyst.hillr(X, v, K, n), X), - -n * v * (K^n) * (X^(n - 1)) / (K^n + X^n)^2 - ) + @test isequal(derivative(Catalyst.hillr(X, v, K, n), X), + -n * v * (K^n) * (X^(n - 1)) / (K^n + X^n)^2) @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( - derivative(Catalyst.hillr(X, v, K, n), n), - v * (X^n) * (K^n) * (log(K) - log(X)) / (K^n + X^n)^2 - ) - - @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( - 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( - derivative(Catalyst.hillar(X, Y, v, K, n), v), - X^n / (K^n + X^n + Y^n) - ) - @test isequal( - derivative(Catalyst.hillar(X, Y, v, K, n), K), - -n * v * (K^(n - 1)) * (X^n) / (K^n + X^n + Y^n)^2 - ) - @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 - ) + @test isequal(derivative(Catalyst.hillr(X, v, K, n), K), + n * v * (K^(n - 1)) * (X^n) / (K^n + X^n)^2) + @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(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(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(derivative(Catalyst.hillar(X, Y, v, K, n), v), + X^n / (K^n + X^n + Y^n)) + @test isequal(derivative(Catalyst.hillar(X, Y, v, K, n), K), + -n * v * (K^(n - 1)) * (X^n) / (K^n + X^n + Y^n)^2) + @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 Function Expansion ### @@ -189,17 +167,17 @@ let @variables V(t) @parameters v K eqs = [ - Reaction(mm(X, v, K), [], [X]), - mm(V, v, K) ~ V + 1, + Reaction(mm(X,v,K), [], [X]), + mm(V,v,K) ~ V + 1 ] @named rs = ReactionSystem(eqs, t) # Check that `expand_registered_functions` does not mutate original model. rs_expanded_funcs = Catalyst.expand_registered_functions(rs) - @test isequal(only(Catalyst.get_rxs(rs)).rate, Catalyst.mm(X, v, K)) - @test isequal(only(Catalyst.get_rxs(rs_expanded_funcs)).rate, v * X / (X + K)) - @test isequal(last(Catalyst.get_eqs(rs)).lhs, Catalyst.mm(V, v, K)) - @test isequal(last(Catalyst.get_eqs(rs_expanded_funcs)).lhs, v * V / (V + K)) + @test isequal(only(Catalyst.get_rxs(rs)).rate, Catalyst.mm(X,v,K)) + @test isequal(only(Catalyst.get_rxs(rs_expanded_funcs)).rate, v*X/(X + K)) + @test isequal(last(Catalyst.get_eqs(rs)).lhs, Catalyst.mm(V,v,K)) + @test isequal(last(Catalyst.get_eqs(rs_expanded_funcs)).lhs, v*V/(V + K)) end # Tests on model with events. @@ -207,14 +185,14 @@ let # Creates a model, saves it, and creates an expanded version. rs = @reaction_network begin @continuous_events begin - [mm(X, v, K) ~ 1.0] => [X => X] + [mm(X,v,K) ~ 1.0] => [X => X] end @discrete_events begin - [1.0] => [X => mmr(X, v, K) + Y * (v + K)] + [1.0] => [X => mmr(X,v,K) + Y*(v + K)] 1.0 => [X => X] - (hill(X, v, K, n) > 1000.0) => [X => hillr(X, v, K, n) + 2] + (hill(X,v,K,n) > 1000.0) => [X => hillr(X,v,K,n) + 2] end - v0 + hillar(X, Y, v, K, n), X --> Y + v0 + hillar(X,Y,v,K,n), X --> Y end rs_saved = deepcopy(rs) rs_expanded = Catalyst.expand_registered_functions(rs) @@ -227,10 +205,10 @@ let # Checks that the new system is expanded. @unpack v0, X, Y, v, K, n = rs continuous_events = [ - [v * X / (X + K) ~ 1.0] => [X ~ Pre(X)], + [v*X/(X + K) ~ 1.0] => [X ~ Pre(X)] ] discrete_events = [ - [1.0] => [X ~ v * K / (Pre(X) + K) + Pre(Y) * (v + K)] + [1.0] => [X ~ v*K/(Pre(X) + K) + Pre(Y)*(v + K)] 1.0 => [X ~ Pre(X)] (v * (X^n) / (X^n + K^n) > 1000.0) => [X ~ v * (K^n) / (Pre(X)^n + K^n) + 2] ] @@ -252,18 +230,16 @@ let [X ~ mm(X, v, K)]; # affect uses registered fn affect_neg = [X ~ hill(X, v, K, n)] # affect_neg uses DIFFERENT registered fn ) - rs = ReactionSystem( - [Reaction(mm(X, v, K), [X], [Y])], t; - continuous_events = [ce], name = :test_affect_neg - ) + rs = ReactionSystem([Reaction(mm(X, v, K), [X], [Y])], t; + continuous_events = [ce], name = :test_affect_neg) rs_exp = Catalyst.expand_registered_functions(rs) ce_exp = get_continuous_events(rs_exp)[1] # Verify conditions expanded - @test isequal(ce_exp.conditions, [v * X / (X + K) ~ 1.0]) + @test isequal(ce_exp.conditions, [v*X/(X + K) ~ 1.0]) # Verify affect expanded (mm → v*X/(X+K)) - @test isequal(ce_exp.affect.affect, [X ~ v * X / (X + K)]) + @test isequal(ce_exp.affect.affect, [X ~ v*X/(X + K)]) # Verify affect_neg expanded (hill → v*X^n/(X^n+K^n)) - @test isequal(ce_exp.affect_neg.affect, [X ~ v * (X^n) / (X^n + K^n)]) + @test isequal(ce_exp.affect_neg.affect, [X ~ v*(X^n)/(X^n + K^n)]) # 2. Continuous event with discrete_parameters preserved after expansion. @discretes α(t) = 1.0 @@ -271,13 +247,11 @@ let [mm(X, v, K) ~ 1.0] => [α ~ 0]; discrete_parameters = [α] ) - rs_dp = ReactionSystem( - [Reaction(v, [X], [Y])], t; - continuous_events = [ce_dp], name = :test_disc_ps_cont - ) + rs_dp = ReactionSystem([Reaction(v, [X], [Y])], t; + continuous_events = [ce_dp], name = :test_disc_ps_cont) rs_dp_exp = Catalyst.expand_registered_functions(rs_dp) ce_dp_exp = get_continuous_events(rs_dp_exp)[1] - @test isequal(ce_dp_exp.conditions, [v * X / (X + K) ~ 1.0]) + @test isequal(ce_dp_exp.conditions, [v*X/(X + K) ~ 1.0]) @test issetequal(ce_dp_exp.affect.discrete_parameters, [α]) # 3. Discrete event with discrete_parameters preserved and affect expanded. @@ -285,13 +259,11 @@ let 2.0 => [α ~ mm(X, v, K)]; discrete_parameters = [α] ) - rs_de = ReactionSystem( - [Reaction(v, [X], [Y])], t; - discrete_events = [de_dp], name = :test_disc_ps_disc - ) + rs_de = ReactionSystem([Reaction(v, [X], [Y])], t; + discrete_events = [de_dp], name = :test_disc_ps_disc) rs_de_exp = Catalyst.expand_registered_functions(rs_de) de_dp_exp = get_discrete_events(rs_de_exp)[1] - @test isequal(de_dp_exp.affect.affect, [α ~ v * X / (X + K)]) + @test isequal(de_dp_exp.affect.affect, [α ~ v*X/(X + K)]) @test issetequal(de_dp_exp.affect.discrete_parameters, [α]) end @@ -308,84 +280,72 @@ let D = default_time_deriv() @unpack X, v, K, n, Y, Z = rn osyseqs = equations(osys) - eqs = [ - D(X) ~ -hill(X, v, K, n) * X * Y - mm(X, v, K) * X * Y - hillr(X, v, K, n) * X * Y - mmr(X, v, K) * X * Y, - D(Y) ~ -hill(X, v, K, n) * X * Y - mm(X, v, K) * X * Y - hillr(X, v, K, n) * X * Y - mmr(X, v, K) * X * Y, - D(Z) ~ hill(X, v, K, n) * X * Y + mm(X, v, K) * X * Y + hillr(X, v, K, n) * X * Y + mmr(X, v, K) * X * Y, - ] + eqs = [D(X) ~ -hill(X, v, K, n)*X*Y - mm(X,v,K)*X*Y - hillr(X,v,K,n)*X*Y - mmr(X,v,K)*X*Y, + D(Y) ~ -hill(X, v, K, n)*X*Y - mm(X,v,K)*X*Y - hillr(X,v,K,n)*X*Y - mmr(X,v,K)*X*Y, + D(Z) ~ hill(X, v, K, n)*X*Y + mm(X,v,K)*X*Y + hillr(X,v,K,n)*X*Y + mmr(X,v,K)*X*Y] reorder = [findfirst(eq -> isequal(eq.lhs, osyseq.lhs), eqs) for osyseq in osyseqs] - for (osysidx, eqidx) in enumerate(reorder) + for (osysidx,eqidx) in enumerate(reorder) @test _iszero(simplify(eqs[eqidx].rhs - osyseqs[osysidx].rhs)) end osys2 = complete(ode_model(rn)) hill2(x, v, k, n) = v * x^n / (k^n + x^n) - mm2(X, v, K) = v * X / (X + K) - mmr2(X, v, K) = v * K / (X + K) - hillr2(X, v, K, n) = v * (K^n) / (X^n + K^n) - eqs2 = [ - D(X) ~ -hill2(X, v, K, n) * X * Y - mm2(X, v, K) * X * Y - hillr2(X, v, K, n) * X * Y - mmr2(X, v, K) * X * Y, - D(Y) ~ -hill2(X, v, K, n) * X * Y - mm2(X, v, K) * X * Y - hillr2(X, v, K, n) * X * Y - mmr2(X, v, K) * X * Y, - D(Z) ~ hill2(X, v, K, n) * X * Y + mm2(X, v, K) * X * Y + hillr2(X, v, K, n) * X * Y + mmr2(X, v, K) * X * Y, - ] + mm2(X,v,K) = v*X / (X + K) + mmr2(X,v,K) = v*K / (X + K) + hillr2(X,v,K,n) = v * (K^n) / (X^n + K^n) + eqs2 = [D(X) ~ -hill2(X, v, K, n)*X*Y - mm2(X,v,K)*X*Y - hillr2(X,v,K,n)*X*Y - mmr2(X,v,K)*X*Y, + D(Y) ~ -hill2(X, v, K, n)*X*Y - mm2(X,v,K)*X*Y - hillr2(X,v,K,n)*X*Y - mmr2(X,v,K)*X*Y, + D(Z) ~ hill2(X, v, K, n)*X*Y + mm2(X,v,K)*X*Y + hillr2(X,v,K,n)*X*Y + mmr2(X,v,K)*X*Y] osyseqs2 = equations(osys2) reorder = [findfirst(eq -> isequal(eq.lhs, osyseq.lhs), eqs2) for osyseq in osyseqs2] - for (osysidx, eqidx) in enumerate(reorder) + for (osysidx,eqidx) in enumerate(reorder) @test _iszero(simplify(eqs2[eqidx].rhs - osyseqs2[osysidx].rhs)) end nlsys = complete(ss_ode_model(rn; expand_catalyst_funs = false)) nlsyseqs = equations(nlsys) - eqs = [ - 0 ~ -hill(X, v, K, n) * X * Y - mm(X, v, K) * X * Y - hillr(X, v, K, n) * X * Y - mmr(X, v, K) * X * Y, - 0 ~ -hill(X, v, K, n) * X * Y - mm(X, v, K) * X * Y - hillr(X, v, K, n) * X * Y - mmr(X, v, K) * X * Y, - 0 ~ hill(X, v, K, n) * X * Y + mm(X, v, K) * X * Y + hillr(X, v, K, n) * X * Y + mmr(X, v, K) * X * Y, - ] + eqs = [0 ~ -hill(X, v, K, n)*X*Y - mm(X,v,K)*X*Y - hillr(X,v,K,n)*X*Y - mmr(X,v,K)*X*Y, + 0 ~ -hill(X, v, K, n)*X*Y - mm(X,v,K)*X*Y - hillr(X,v,K,n)*X*Y - mmr(X,v,K)*X*Y, + 0 ~ hill(X, v, K, n)*X*Y + mm(X,v,K)*X*Y + hillr(X,v,K,n)*X*Y + mmr(X,v,K)*X*Y] for (i, eq) in enumerate(eqs) @test _iszero(simplify(eq.rhs - nlsyseqs[i].rhs)) end nlsys2 = complete(ss_ode_model(rn)) nlsyseqs2 = equations(nlsys2) - eqs2 = [ - 0 ~ -hill2(X, v, K, n) * X * Y - mm2(X, v, K) * X * Y - hillr2(X, v, K, n) * X * Y - mmr2(X, v, K) * X * Y, - 0 ~ -hill2(X, v, K, n) * X * Y - mm2(X, v, K) * X * Y - hillr2(X, v, K, n) * X * Y - mmr2(X, v, K) * X * Y, - 0 ~ hill2(X, v, K, n) * X * Y + mm2(X, v, K) * X * Y + hillr2(X, v, K, n) * X * Y + mmr2(X, v, K) * X * Y, - ] + eqs2 = [0 ~ -hill2(X, v, K, n)*X*Y - mm2(X,v,K)*X*Y - hillr2(X,v,K,n)*X*Y - mmr2(X,v,K)*X*Y, + 0 ~ -hill2(X, v, K, n)*X*Y - mm2(X,v,K)*X*Y - hillr2(X,v,K,n)*X*Y - mmr2(X,v,K)*X*Y, + 0 ~ hill2(X, v, K, n)*X*Y + mm2(X,v,K)*X*Y + hillr2(X,v,K,n)*X*Y + mmr2(X,v,K)*X*Y] for (i, eq) in enumerate(eqs2) @test _iszero(simplify(eq.rhs - nlsyseqs2[i].rhs)) end sdesys = complete(sde_model(rn; expand_catalyst_funs = false)) sdesyseqs = equations(sdesys) - eqs = [ - D(X) ~ -hill(X, v, K, n) * X * Y - mm(X, v, K) * X * Y - hillr(X, v, K, n) * X * Y - mmr(X, v, K) * X * Y, - D(Y) ~ -hill(X, v, K, n) * X * Y - mm(X, v, K) * X * Y - hillr(X, v, K, n) * X * Y - mmr(X, v, K) * X * Y, - D(Z) ~ hill(X, v, K, n) * X * Y + mm(X, v, K) * X * Y + hillr(X, v, K, n) * X * Y + mmr(X, v, K) * X * Y, - ] + eqs = [D(X) ~ -hill(X, v, K, n)*X*Y - mm(X,v,K)*X*Y - hillr(X,v,K,n)*X*Y - mmr(X,v,K)*X*Y, + D(Y) ~ -hill(X, v, K, n)*X*Y - mm(X,v,K)*X*Y - hillr(X,v,K,n)*X*Y - mmr(X,v,K)*X*Y, + D(Z) ~ hill(X, v, K, n)*X*Y + mm(X,v,K)*X*Y + hillr(X,v,K,n)*X*Y + mmr(X,v,K)*X*Y] reorder = [findfirst(eq -> isequal(eq.lhs, sdesyseq.lhs), eqs) for sdesyseq in sdesyseqs] - for (sdesysidx, eqidx) in enumerate(reorder) + for (sdesysidx,eqidx) in enumerate(reorder) @test _iszero(simplify(eqs[eqidx].rhs - sdesyseqs[sdesysidx].rhs)) end sdesysnoiseeqs = ModelingToolkitBase.get_noise_eqs(sdesys) - neqvec = diagm(sqrt.(abs.([hill(X, v, K, n) * X * Y, mm(X, v, K) * X * Y, hillr(X, v, K, n) * X * Y, mmr(X, v, K) * X * Y]))) + neqvec = diagm(sqrt.(abs.([hill(X, v, K, n)*X*Y, mm(X,v,K)*X*Y, hillr(X,v,K,n)*X*Y, mmr(X,v,K)*X*Y]))) neqmat = [-1 -1 -1 -1; -1 -1 -1 -1; 1 1 1 1] neqmat *= neqvec @test all(_iszero, simplify.(sdesysnoiseeqs .- neqmat)) sdesys = complete(sde_model(rn)) sdesyseqs = equations(sdesys) - eqs = [ - D(X) ~ -hill2(X, v, K, n) * X * Y - mm2(X, v, K) * X * Y - hillr2(X, v, K, n) * X * Y - mmr2(X, v, K) * X * Y, - D(Y) ~ -hill2(X, v, K, n) * X * Y - mm2(X, v, K) * X * Y - hillr2(X, v, K, n) * X * Y - mmr2(X, v, K) * X * Y, - D(Z) ~ hill2(X, v, K, n) * X * Y + mm2(X, v, K) * X * Y + hillr2(X, v, K, n) * X * Y + mmr2(X, v, K) * X * Y, - ] + eqs = [D(X) ~ -hill2(X, v, K, n)*X*Y - mm2(X,v,K)*X*Y - hillr2(X,v,K,n)*X*Y - mmr2(X,v,K)*X*Y, + D(Y) ~ -hill2(X, v, K, n)*X*Y - mm2(X,v,K)*X*Y - hillr2(X,v,K,n)*X*Y - mmr2(X,v,K)*X*Y, + D(Z) ~ hill2(X, v, K, n)*X*Y + mm2(X,v,K)*X*Y + hillr2(X,v,K,n)*X*Y + mmr2(X,v,K)*X*Y] reorder = [findfirst(eq -> isequal(eq.lhs, sdesyseq.lhs), eqs) for sdesyseq in sdesyseqs] - for (sdesysidx, eqidx) in enumerate(reorder) + for (sdesysidx,eqidx) in enumerate(reorder) @test _iszero(simplify(eqs[eqidx].rhs - sdesyseqs[sdesysidx].rhs)) end sdesysnoiseeqs = ModelingToolkitBase.get_noise_eqs(sdesys) - neqvec = diagm(sqrt.(abs.([hill2(X, v, K, n) * X * Y, mm2(X, v, K) * X * Y, hillr2(X, v, K, n) * X * Y, mmr2(X, v, K) * X * Y]))) + neqvec = diagm(sqrt.(abs.([hill2(X, v, K, n)*X*Y, mm2(X,v,K)*X*Y, hillr2(X,v,K,n)*X*Y, mmr2(X,v,K)*X*Y]))) neqmat = [-1 -1 -1 -1; -1 -1 -1 -1; 1 1 1 1] neqmat *= neqvec @test all(_iszero, simplify.(sdesysnoiseeqs .- neqmat)) @@ -395,7 +355,7 @@ let jsysjumps = MT.jumps(jsys) rates = getfield.(jsysjumps, :rate) affects = getfield.(jsysjumps, :affect!) - reqs = [Y * X * hill(X, v, K, n), Y * X * mm(X, v, K), hillr(X, v, K, n) * Y * X, Y * X * mmr(X, v, K)] + reqs = [Y*X*hill(X, v, K, n), Y*X*mm(X, v, K), hillr(X, v, K, n)*Y*X, Y*X*mmr(X, v, K)] affeqs = [Z ~ 1 + Pre(Z), Y ~ -1 + Pre(Y), X ~ -1 + Pre(X)] @test all(_iszero, simplify.(rates .- reqs)) @test all(aff -> issetequal(aff, affeqs), affects) @@ -405,7 +365,7 @@ let jsysjumps = MT.jumps(jsys) rates = getfield.(jsysjumps, :rate) affects = getfield.(jsysjumps, :affect!) - reqs = [Y * X * hill2(X, v, K, n), Y * X * mm2(X, v, K), hillr2(X, v, K, n) * Y * X, Y * X * mmr2(X, v, K)] + reqs = [Y*X*hill2(X, v, K, n), Y*X*mm2(X, v, K), hillr2(X, v, K, n)*Y*X, Y*X*mmr2(X, v, K)] affeqs = [Z ~ 1 + Pre(Z), Y ~ -1 + Pre(Y), X ~ -1 + Pre(X)] @test all(_iszero, simplify.(rates .- reqs)) @test all(aff -> issetequal(aff, affeqs), affects) diff --git a/test/reactionsystem_core/events.jl b/test/reactionsystem_core/events.jl index 29f35d6bf0..a0b05d644c 100644 --- a/test/reactionsystem_core/events.jl +++ b/test/reactionsystem_core/events.jl @@ -19,7 +19,7 @@ D = default_time_deriv() # Test discrete event is propagated to ODE solver correctly. let # Creates model (essentially a jagged oscillation, where `V` is reset to 1.0 every 1.0 time units). - @variables V(t) = 1.0 + @variables V(t)=1.0 eqs = [D(V) ~ V] discrete_events = [1.0 => [V ~ 1.0]] rxs = [ @@ -36,17 +36,17 @@ let @test length(ModelingToolkitBase.discrete_events(osys)) == 1 oprob = ODEProblem(osys, [osys.A => 0.0], (0.0, 20.0)) sol = solve(oprob, Tsit5()) - @test 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 (a production/degradation system, but both reactions stop at `t=2.5`). - @discretes α(t) = 5.0 β(t) = 1.0 - @species V(t) = 0.0 + @discretes α(t)=5.0 β(t)=1.0 + @species V(t)=0.0 rxs = [ Reaction(α, nothing, [V]), - Reaction(β, [V], nothing), + Reaction(β, [V], nothing) ] continuous_events = SymbolicContinuousCallback([V ~ 2.5] => [α ~ 0, β ~ 0]; discrete_parameters = [α, β]) @named rs = ReactionSystem(rxs, t; continuous_events) @@ -68,10 +68,10 @@ end let # Creates model. @parameters p d α::Int64 = 1 - @species X(t) A(t) = 2 [description = "A species"] a(t) = 3 + @species X(t) A(t) = 2 [description="A species"] a(t) = 3 rxs = [ Reaction(p, nothing, [X]), - Reaction(d, [X], nothing), + Reaction(d, [X], nothing) ] continuous_events = [α ~ t] => [A ~ Pre(A + a)] discrete_events = [2.0 => [A ~ Pre(α + a)]] @@ -134,7 +134,7 @@ let @species X(t) rxs = [ Reaction(p, nothing, [X]), - Reaction(d, [X], nothing), + Reaction(d, [X], nothing) ] ce = [X ~ 1.0] => [X ~ 0.5] de = [2.0] => [p ~ 1.0] @@ -153,11 +153,11 @@ let @species X(t) rxs = [ Reaction(p, nothing, [X]), - Reaction(d, [X], nothing), + Reaction(d, [X], nothing) ] # Declares various misformatted events . - @test_broken false # Some miss-formatted events don't yield errors, but should (https://github.com/SciML/ModelingToolkit.jl/issues/4167). These are commented out. + @test_broken false # Some miss-formatted events don't yield errors, but should (https://github.com/SciML/ModelingToolkit.jl/issues/4167). These are commented out. continuous_events_bad = [ X ~ 1.0 => [X ~ 0.5], # Scalar condition. [X ~ 1.0] => X ~ 0.5, # Scalar affect. @@ -170,7 +170,7 @@ let [2.0] => p ~ 1.0, # Scalar affect. #[2.0] => (p ~ 1.0, ), # Tuple affect. # Should not work, potentially bad for performance as compared to vectors (https://github.com/SciML/ModelingToolkit.jl/issues/4167). [X > 2.0] => [p ~ 1.0], # Vector conditions. - (1.0, 2.0) => [p ~ 1.0], # Tuple condition. + (1.0, 2.0) => [p ~ 1.0] # Tuple condition. ] # Checks that errors are produced. @@ -192,7 +192,7 @@ end # Tests event affecting non-species components. let rn_dsl = @reaction_network rn begin - @parameters thres = 7.0 dY_up + @parameters thres=7.0 dY_up @continuous_events begin [t ~ 2.5] => [p => p + 0.2] [X ~ thres, Y ~ X] => [X => X - 0.5, Z => Z + 0.1] @@ -204,21 +204,21 @@ let end (p, dX), 0 <--> X - (1.1 * p, dY), 0 <--> Y + (1.1*p, dY), 0 <--> Y d, Z --> 0 end # Creates model programmatically. t = default_t() @species X(t) Y(t) Z(t) - @parameters thres = 7.0 dY_up d + @parameters thres=7.0 dY_up d @discretes p(t) dX(t) dY(t) rxs = [ Reaction(p, nothing, [X], nothing, [1]), Reaction(dX, [X], nothing, [1], nothing), - Reaction(1.1 * p, nothing, [Y], nothing, [1]), + Reaction(1.1*p, nothing, [Y], nothing, [1]), Reaction(dY, [Y], nothing, [1], nothing), - Reaction(d, [Z], nothing, [1], nothing), + Reaction(d, [Z], nothing, [1], nothing) ] continuous_events = [ SymbolicContinuousCallback([t ~ 2.5] => [p ~ Pre(p) + 0.2]; discrete_parameters = [p]) @@ -305,7 +305,7 @@ let # Tuple affect (discrete events). @test_throws Exception @eval @reaction_network begin @species X(t) - @discrete_events 1.0 => (X => X + 1,) + @discrete_events 1.0 => (X => X + 1, ) end # Equation condition (discrete events). @@ -323,17 +323,17 @@ end let # Creates model with all types of events. The `e` parameters track whether events are triggered. rn = @reaction_network begin - @discretes e1(t) = 0 e2(t) = 0 e3(t) = 0 e4(t) = 0 + @discretes e1(t)=0 e2(t)=0 e3(t)=0 e4(t)=0 @continuous_events begin [X ~ 1000.0] => [e1 => 1] end @discrete_events begin [1.0] => [e2 => 1] 1.0 => [e3 => 1] - (Y > 1000.0) & (e4 == 0) => [e4 => 1] + (Y > 1000.0) & (e4==0) => [e4 => 1] end - (p, d), 0 <--> X - (p, d), 0 <--> Y + (p,d), 0 <--> X + (p,d), 0 <--> Y end # Simulates the model for conditions where it *definitely* will cross `X = 1000.0` @@ -354,13 +354,13 @@ end let # Creates model with all types of events. The `e` parameters track whether events are triggered. rn = @reaction_network begin - @discretes e1(t) = 0 e2(t) = 0 e3(t) = 0 + @discretes e1(t)=0 e2(t)=0 e3(t)=0 @discrete_events begin [1.0] => [e1 => 1] 1.0 => [e2 => 1] - (X > 1000.0) & (e3 == 0) => [e3 => 1] + (X > 1000.0) & (e3==0) => [e3 => 1] end - (p, d), 0 <--> X + (p,d), 0 <--> X end # Simulates the model for conditions where it *definitely* will cross `X = 1000.0` @@ -384,8 +384,8 @@ let rn = @reaction_network begin @default_noise_scaling 0.0 @parameters add::Int64 - (p, d), 0 <--> X - (p, d), 0 <--> Y + (p,d), 0 <--> X + (p,d), 0 <--> Y end rn_events = @reaction_network begin @default_noise_scaling 0.0 @@ -398,8 +398,8 @@ let 20.0 => [X => X + add] (Y < X) => [Y => Y + add] end - (p, d), 0 <--> X - (p, d), 0 <--> Y + (p,d), 0 <--> X + (p,d), 0 <--> Y end rn_dics_events = @reaction_network begin @parameters add::Int64 @@ -408,8 +408,8 @@ let 20.0 => [X => X + add] (Y < X) => [Y => Y + add] end - (p, d), 0 <--> X - (p, d), 0 <--> Y + (p,d), 0 <--> X + (p,d), 0 <--> Y end # Sets simulation inputs. @@ -419,9 +419,9 @@ let # Create callbacks cb_cont = ContinuousCallback((u, t, int) -> (int[:X] - 90.0), int -> (int[:X] += 10.0)) - cb_disc_1 = PresetTimeCallback([5.0, 10.0], int -> (int[:X] += int.ps[:add]; int[:Y] += int.ps[:add])) + cb_disc_1 = PresetTimeCallback([5.0, 10.0], int -> (int[:X] += int.ps[:add]; int[:Y] += int.ps[:add];)) cb_disc_2 = PresetTimeCallback(20.0:20.0:tspan[end], int -> (int[:X] += int.ps[:add])) - cb_disc_3 = DiscreteCallback((u, t, i) -> i[:Y] < i[:X], int -> (int[:Y] += int.ps[:add])) + cb_disc_3 = DiscreteCallback((u,t,i) -> i[:Y] < i[:X], int -> (int[:Y] += int.ps[:add])) callback = CallbackSet(cb_cont, cb_disc_1, cb_disc_2, cb_disc_3) # Checks for ODE simulations. @@ -456,7 +456,7 @@ let @species X(t) A(t) a(t) rxs = [ Reaction(p, nothing, [X]), - Reaction(d, [X], nothing), + Reaction(d, [X], nothing) ] continuous_events = [α ~ t] => [A ~ A + a] @named rs = ReactionSystem(rxs, t; continuous_events) @@ -606,19 +606,15 @@ end # tstops field is needed to ensure the solver steps to exactly t_switch. # Note: SDE/Jump/Hybrid solvers also support SymbolicTstops (tested separately below). let - @variables V(t) = 10.0 - @parameters t_switch = 3.0 + @variables V(t)=10.0 + @parameters t_switch=3.0 D = default_time_deriv() eqs = [D(V) ~ -V] # Generic symbolic condition — NOT a numeric PresetTimeCallback. # The tstop forces the solver to step to exactly t_switch so the equality holds. discrete_events = [(t == t_switch) => [V ~ 100.0]] - rs = complete( - ReactionSystem( - eqs, t; discrete_events, - tstops = [t_switch], name = :rs - ) - ) + rs = complete(ReactionSystem(eqs, t; discrete_events, + tstops = [t_switch], name = :rs)) osys = complete(ode_model(rs)) oprob = ODEProblem(osys, [], (0.0, 8.0)) @@ -626,7 +622,7 @@ let # Without the tstop the solver would likely step over t_switch=3.0; # with it, V is reset to 100.0 at exactly that time. - @test sol(3.0 + 0.01, idxs = V) ≈ 100.0 atol = 1.0 + @test sol(3.0 + 0.01, idxs = V) ≈ 100.0 atol=1.0 # Verify the symbolic tstop was forwarded to the System. @test issetequal(ModelingToolkitBase.get_tstops(osys), [t_switch]) @@ -635,7 +631,7 @@ end # Tests that symbolic tstops are forwarded through sde_model to the converted System. let rn = @reaction_network begin - @parameters t_event = 3.0 + @parameters t_event=3.0 @tstops begin t_event 2 * t_event @@ -643,16 +639,14 @@ let (10.0, 0.01), 0 <--> X end ssys = sde_model(rn) - @test issetequal( - ModelingToolkitBase.get_tstops(ssys), - ModelingToolkitBase.get_tstops(rn) - ) + @test issetequal(ModelingToolkitBase.get_tstops(ssys), + ModelingToolkitBase.get_tstops(rn)) end # Tests that symbolic tstops are forwarded through jump_model to the converted System. let rn = @reaction_network begin - @parameters t_event = 2.0 + @parameters t_event=2.0 @tstops begin t_event 2 * t_event @@ -660,32 +654,28 @@ let (10.0, 0.01), 0 <--> X end jsys = jump_model(rn) - @test issetequal( - ModelingToolkitBase.get_tstops(jsys), - ModelingToolkitBase.get_tstops(rn) - ) + @test issetequal(ModelingToolkitBase.get_tstops(jsys), + ModelingToolkitBase.get_tstops(rn)) end # Tests that symbolic tstops are forwarded through hybrid_model to the converted System. let rn = @reaction_network begin - @parameters t_event = 4.0 + @parameters t_event=4.0 @tstops t_event (10.0, 0.01), 0 <--> X 1.0, X --> 0, [physical_scale = Catalyst.PhysicalScale.Jump] end hsys = hybrid_model(rn; default_scale = Catalyst.PhysicalScale.ODE) - @test issetequal( - ModelingToolkitBase.get_tstops(hsys), - ModelingToolkitBase.get_tstops(rn) - ) + @test issetequal(ModelingToolkitBase.get_tstops(hsys), + ModelingToolkitBase.get_tstops(rn)) end # Integration test: solve JumpProblem with symbolic tstops and a discrete event. # The discrete event at t_event adds a large number of molecules, verifiable in the solution. let rn = @reaction_network begin - @parameters t_event = 3.0 + @parameters t_event=3.0 @tstops t_event @discrete_events (t == t_event) => [X => X + 10000] (10.0, 0.01), 0 <--> X @@ -703,7 +693,7 @@ end # Integration test: solve SDEProblem with symbolic tstops and a discrete event. let rn = @reaction_network begin - @parameters t_event = 3.0 + @parameters t_event=3.0 @tstops t_event @discrete_events (t == t_event) => [X => X + 10000.0] (10.0, 0.01), 0 <--> X @@ -721,7 +711,7 @@ end # Uses ODE-scale production/degradation + a Jump-scale degradation to ensure a true hybrid. let rn = @reaction_network begin - @parameters t_event = 3.0 + @parameters t_event=3.0 @tstops t_event @discrete_events (t == t_event) => [X => X + 10000.0] 10.0, 0 --> X, [physical_scale = Catalyst.PhysicalScale.ODE] @@ -748,17 +738,13 @@ let # Species in tstops (5-arg constructor). @test_throws ArgumentError ReactionSystem(rxs, t, [X], [k]; tstops = [X], name = :rs) # Variable in tstops (5-arg constructor). - @test_throws ArgumentError ReactionSystem( - [rxs; D(V) ~ -V], t, [X, V], [k]; - tstops = [V], name = :rs - ) + @test_throws ArgumentError ReactionSystem([rxs; D(V) ~ -V], t, [X, V], [k]; + tstops = [V], name = :rs) # Species in tstops (short-form constructor). @test_throws ArgumentError ReactionSystem(rxs, t; tstops = [X], name = :rs) # Variable in tstops (short-form constructor). - @test_throws ArgumentError ReactionSystem( - [rxs; D(V) ~ -V], t; - tstops = [V], name = :rs - ) + @test_throws ArgumentError ReactionSystem([rxs; D(V) ~ -V], t; + tstops = [V], name = :rs) # Species/variable in tstops via the DSL. @test_throws ArgumentError @eval @reaction_network begin @tstops X diff --git a/test/reactionsystem_core/functional_parameters.jl b/test/reactionsystem_core/functional_parameters.jl index b99f5a0dec..8099df248e 100644 --- a/test/reactionsystem_core/functional_parameters.jl +++ b/test/reactionsystem_core/functional_parameters.jl @@ -1,3 +1,4 @@ + # Fetch packages. using Catalyst, DataInterpolations, JumpProcesses, OrdinaryDiffEqDefault, OrdinaryDiffEqTsit5, StochasticDiffEq, Test @@ -13,17 +14,17 @@ let # Defines an input process (modified sinus wave). tend = 5.0 ts = collect(0.0:0.0001:tend) - A = 2.0; f = 1.6; ϕ = 0.5 - spline = LinearInterpolation(A .* (sin.(2π * f .* ts .- ϕ) .+ 1) / 2, ts) + A = 2.0; f = 1.6; ϕ = 0.5; + spline = LinearInterpolation(A .* (sin.(2π * f .* ts .- ϕ) .+ 1) /2, ts) @parameters (pIn::typeof(spline))(..) # Defines a `ReactionSystem` without the input parameter. @species X(t) Y1(t) Y2(t) @parameters p d k1 k2 rxs_base = [ - Reaction(A * (sin(2π * f * t .- ϕ) + 1) / 2, [], [X]) + Reaction(A * (sin(2π * f * t .- ϕ) + 1) /2, [], [X]) Reaction(d, [X], []) - Reaction(k1 * X, [Y1], [Y2]) + Reaction(k1*X, [Y1], [Y2]) Reaction(k2, [Y2], [Y1]) ] @named rs_base = ReactionSystem(rxs_base, t) @@ -33,7 +34,7 @@ let rxs_pIn = [ Reaction(pIn(t), [], [X]) Reaction(d, [X], []) - Reaction(k1 * X, [Y1], [Y2]) + Reaction(k1*X, [Y1], [Y2]) Reaction(k2, [Y2], [Y1]) ] @named rs_pIn = ReactionSystem(rxs_pIn, t) @@ -41,8 +42,8 @@ let # Defines a `ReactionSystem` with the input parameter (DSL). rs_pIn_dsl = @reaction_network rs_pIn begin - ($pIn(t), d), 0 <--> X - (k1 * X, k2), Y1 <--> Y2 + ($pIn(t),d), 0 <--> X + (k1*X,k2), Y1 <--> Y2 end # Checks that the two model declarations are equivalent. @@ -55,13 +56,13 @@ let oprob_base = ODEProblem(rs_base, u0, tend, ps_base) oprob_prog = ODEProblem(rs_pIn, u0, tend, ps) oprob_dsl = ODEProblem(rs_pIn_dsl, u0, tend, ps) - sol_base = solve(oprob_base; saveat = 0.1, abstol = 1.0e-8, reltol = 1.0e-8) - sol_prog = solve(oprob_prog; saveat = 0.1, abstol = 1.0e-8, reltol = 1.0e-8) - sol_dsl = solve(oprob_dsl; saveat = 0.1, abstol = 1.0e-8, reltol = 1.0e-8) + sol_base = solve(oprob_base; saveat = 0.1, abstol = 1e-8, reltol = 1e-8) + sol_prog = solve(oprob_prog; saveat = 0.1, abstol = 1e-8, reltol = 1e-8) + sol_dsl = solve(oprob_dsl; saveat = 0.1, abstol = 1e-8, reltol = 1e-8) # Checks that simulations are identical. - @test sol_base[X] ≈ sol_prog[X] atol = 1.0e-8 rtol = 1.0e-8 - @test sol_base[X] ≈ sol_dsl[X] atol = 1.0e-8 rtol = 1.0e-8 + @test sol_base[X] ≈ sol_prog[X] atol = 1e-8 rtol = 1e-8 + @test sol_base[X] ≈ sol_dsl[X] atol = 1e-8 rtol = 1e-8 end # Checks a simple model for different problem types. @@ -76,18 +77,14 @@ let # Defines the `ReactionSystem` with/without the temporal parameter. @species X(t) @parameters d - @named rs_base = ReactionSystem( - [ - Reaction((10 + t) / (2 + t), [], [X]), - Reaction(d, [X], []), - ] - ) - @named rs_pIn = ReactionSystem( - [ - Reaction(pIn(t), [], [X]), - Reaction(d, [X], []), - ] - ) + @named rs_base = ReactionSystem([ + Reaction((10 + t) / (2 + t), [], [X]), + Reaction(d, [X], []) + ]) + @named rs_pIn = ReactionSystem([ + Reaction(pIn(t), [], [X]), + Reaction(d, [X], []) + ]) rs_base = complete(rs_base) rs_pIn = complete(rs_pIn) @@ -97,8 +94,8 @@ let ps = [ps_base; pIn => spline] # Checks ODE simulations. - ode_sol_base = solve(ODEProblem(rs_base, u0, tend, ps_base); reltol = 1.0e-8, abstol = 1.0e-8, saveat = 0.1) - ode_sol_pIn = solve(ODEProblem(rs_pIn, u0, tend, ps); reltol = 1.0e-8, abstol = 1.0e-8, saveat = 0.1) + ode_sol_base = solve(ODEProblem(rs_base, u0, tend, ps_base); reltol = 1e-8, abstol = 1e-8, saveat = 0.1) + ode_sol_pIn = solve(ODEProblem(rs_pIn, u0, tend, ps); reltol = 1e-8, abstol = 1e-8, saveat = 0.1) @test ode_sol_base ≈ ode_sol_pIn # Checks SDE simulations. @@ -106,14 +103,14 @@ let esde_prob_pIn = EnsembleProblem(SDEProblem(rs_pIn, u0, tend, ps)) sde_sol_mean_base = EnsembleAnalysis.timeseries_point_mean(solve(esde_prob_base, ImplicitEM(); trajectories = 1000), 1.0:10.0).u sde_sol_mean_pIn = EnsembleAnalysis.timeseries_point_mean(solve(esde_prob_pIn, ImplicitEM(); trajectories = 1000), 1.0:10.0).u - @test sde_sol_mean_base ≈ sde_sol_mean_pIn rtol = 1.0e-1 atol = 1.0e-1 + @test sde_sol_mean_base ≈ sde_sol_mean_pIn rtol = 1e-1 atol = 1e-1 # Checks Jump simulations (uses Tsit5 because model has time-dependent rates -> VariableRateJumps). ejmp_prob_base = EnsembleProblem(JumpProblem(rs_base, u0, (0.0, tend), ps_base)) ejmp_prob_pIn = EnsembleProblem(JumpProblem(rs_pIn, u0, (0.0, tend), ps)) jmp_sol_mean_base = EnsembleAnalysis.timeseries_point_mean(solve(ejmp_prob_base, Tsit5(); trajectories = 1000), 1.0:10.0).u jmp_sol_mean_pIn = EnsembleAnalysis.timeseries_point_mean(solve(ejmp_prob_pIn, Tsit5(); trajectories = 1000), 1.0:10.0).u - @test jmp_sol_mean_base ≈ jmp_sol_mean_pIn rtol = 1.0e-1 atol = 1.0e-1 + @test jmp_sol_mean_base ≈ jmp_sol_mean_pIn rtol = 1e-1 atol = 1e-1 end # Checks correctness for non-time functions. @@ -122,16 +119,16 @@ end let # Declares the functional parameter. Is = collect(0.0:0.0001:200.0) - spline1d = LinearInterpolation(100.0 * Is ./ (100.0 .+ Is), Is) + spline1d = LinearInterpolation(100.0*Is ./ (100.0 .+ Is), Is) @parameters (i_rate::typeof(spline1d))(..) # Declares the models. sir = @reaction_network begin - k1 * 100 * I / (100 + I), S --> I + k1*100*I/(100 + I), S --> I k2, I --> R end sir_funcp = @reaction_network rs begin - k1 * $i_rate(I), S --> I + k1*$i_rate(I), S --> I k2, I --> R end @@ -142,14 +139,14 @@ let oprob_funcp = ODEProblem(sir_funcp, u0, 200.0, [ps; i_rate => spline1d]) sol = solve(oprob) sol_funcp = solve(oprob_funcp) - @test sol.u ≈ sol_funcp.u atol = 1.0e-6 rtol = 1.0e-6 + @test sol.u ≈ sol_funcp.u atol = 1e-6 rtol = 1e-6 end ### Other tests ### -# Tests that a functional parameter can be interpolated as the function only, as a function of a +# Tests that a functional parameter can be interpolated as the function only, as a function of a # symbolic variable, or interpolated with the functional parameter and argument separately. -let +let # Prepares the functional parameter. ts = collect(0.0:0.1:1.0) spline = LinearInterpolation(log.(ts), ts) diff --git a/test/reactionsystem_core/higher_order_reactions.jl b/test/reactionsystem_core/higher_order_reactions.jl index e113bf08f0..070ce7c961 100644 --- a/test/reactionsystem_core/higher_order_reactions.jl +++ b/test/reactionsystem_core/higher_order_reactions.jl @@ -34,12 +34,12 @@ let r3 * X3 * X4^2 / factorial(2), X3 + 2X4 ⟾ 3X5 + 3X6 r4 * X2 * X5^3 * X6^3 / (factorial(3) * factorial(3)), 3X5 + 3X6 ⟾ 3X5 + 2X7 + 4X8 r5 * X5^3 * X7^2 * X8^4 / (factorial(3) * factorial(2) * factorial(4)), - 3X5 + 2X7 + 4X8 ⟾ 10X9 + 3X5 + 2X7 + 4X8 ⟾ 10X9 r6 * X9^10 / factorial(10), 10X9 ⟾ X10 d * X10^2 / factorial(2), 2X10 ⟾ ∅ end - for factor in [1.0e-1, 1.0e0, 1.0e1, 1.0e2] + for factor in [1e-1, 1e0, 1e1, 1e2] u0 = rnd_u0(base_higher_order_network, rng; factor) ps = rnd_ps(base_higher_order_network, rng; factor) t = rand(rng) @@ -75,18 +75,16 @@ let 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) + 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!] - ) + higher_order_network_alt2 = ConstantRateJump.([rate1, rate2, rate3, rate4, rate5, rate6, rate7, rate8], + [affect1!, affect2!, affect3!, affect4!, affect5!, affect6!, affect7!, affect8!]) # Prepares JumpProblem via Catalyst. u0_base = rnd_u0_Int64(base_higher_order_network, rng) @@ -108,6 +106,6 @@ let sol_alt2 = solve(jprob_alt2, SSAStepper(); seed, saveat = 1.0) # Checks that species means in the simulations are similar - @test mean(sol_base[:X10]) ≈ mean(sol_alt1[:X10]) atol = 1.0e-1 rtol = 1.0e-1 - @test mean(sol_alt1[:X10]) ≈ mean(sol_alt2[10, :]) atol = 1.0e-1 rtol = 1.0e-1 + @test mean(sol_base[:X10]) ≈ mean(sol_alt1[:X10]) atol = 1e-1 rtol = 1e-1 + @test mean(sol_alt1[:X10]) ≈ mean(sol_alt2[10,:]) atol = 1e-1 rtol = 1e-1 end diff --git a/test/reactionsystem_core/parameter_type_designation.jl b/test/reactionsystem_core/parameter_type_designation.jl index 6782a91edc..b82d0d4a8f 100644 --- a/test/reactionsystem_core/parameter_type_designation.jl +++ b/test/reactionsystem_core/parameter_type_designation.jl @@ -31,7 +31,7 @@ begin Reaction(d2, [X2], nothing), Reaction(d3, [X3], nothing), Reaction(d4, [X4], nothing), - Reaction(d5, [X5], nothing), + Reaction(d5, [X5], nothing) ] @named rs = ReactionSystem(rxs) rs = complete(rs) @@ -39,9 +39,9 @@ begin # Declares initial condition and potential parameter sets. u0 = [X1 => 0.1, X2 => 0.2, X3 => 0.3, X4 => 0.4, X5 => 0.5] p_alts = [ - [p1 => 1.0, d1 => 1.0, p2 => 1.2, p3 => 2, p4 => 0.5, d4 => 1 // 2, p5 => 3 // 2, d5 => 1.5], - (p1 => 1.0, d1 => 1.0, p2 => 1.2, p3 => 2, p4 => 0.5, d4 => 1 // 2, p5 => 3 // 2, d5 => 1.5), - Dict([p1 => 1.0, d1 => 1.0, p2 => 1.2, p3 => 2, p4 => 0.5, d4 => 1 // 2, p5 => 3 // 2, d5 => 1.5]), + [p1 => 1.0, d1 => 1.0, p2 => 1.2, p3 => 2, p4 => 0.5, d4 => 1//2, p5 => 3//2, d5 => 1.5], + (p1 => 1.0, d1 => 1.0, p2 => 1.2, p3 => 2, p4 => 0.5, d4 => 1//2, p5 => 3//2, d5 => 1.5), + Dict([p1 => 1.0, d1 => 1.0, p2 => 1.2, p3 => 2, p4 => 0.5, d4 => 1//2, p5 => 3//2, d5 => 1.5]) ] end @@ -62,7 +62,7 @@ end # Tests that simulations with differentially typed variables yields correct results. let for p in p_alts - oprob = ODEProblem(rs, u0, (0.0, 1000.0), p; abstol = 1.0e-10, reltol = 1.0e-10) + oprob = ODEProblem(rs, u0, (0.0, 1000.0), p; abstol = 1e-10, reltol = 1e-10) sol = solve(oprob, Tsit5()) @test all(sol.u[end] .≈ 1.0) end @@ -108,8 +108,8 @@ let @test unwrap(mtk_struct.ps[p3]) == 2 @test unwrap(mtk_struct.ps[d3]) == 2 @test unwrap(mtk_struct.ps[p4]) == Float32(0.5) - @test unwrap(mtk_struct.ps[d4]) == 1 // 2 - @test unwrap(mtk_struct.ps[p5]) == 3 // 2 + @test unwrap(mtk_struct.ps[d4]) == 1//2 + @test unwrap(mtk_struct.ps[p5]) == 3//2 @test unwrap(mtk_struct.ps[d5]) == Float32(1.5) end diff --git a/test/reactionsystem_core/reaction.jl b/test/reactionsystem_core/reaction.jl index b5ffa2c22e..0c936625cd 100644 --- a/test/reactionsystem_core/reaction.jl +++ b/test/reactionsystem_core/reaction.jl @@ -15,33 +15,33 @@ t = default_t() # Checks that the `Reaction`s have the correct type, and the correct net stoichiometries are generated. let # Declare symbolic variables. - @parameters k n1 n2::Int32 x [isconstantspecies = true] + @parameters k n1 n2::Int32 x [isconstantspecies=true] @species X(t) Y(t) Z(t) @variables A(t) # Tries for different types of rates (should not matter). - for rate in (k, k * A, 2, 3.0, 4 // 3) + for rate in (k, k*A, 2, 3.0, 4//3) # Creates `Reaction`s. rx1 = Reaction(rate, [X], []) rx2 = Reaction(rate, [x], [Y], [1.5], [1]) rx3 = Reaction(rate, [x, X], [], [n1 + n2, n2], []) - rx4 = Reaction(rate, [X, Y], [X, Y, Z], [2 // 3, 3], [1 // 3, 1, 2]) + rx4 = Reaction(rate, [X, Y], [X, Y, Z], [2//3, 3], [1//3, 1, 2]) rx5 = Reaction(rate, [X, Y], [X, Y, Z], [2, 3], [1, n1, n2]) rx6 = Reaction(rate, [X], [x], [n1], [1]) # Check `Reaction` types. - @test rx1 isa Reaction{SymbolicT, Int64} - @test rx2 isa Reaction{SymbolicT, Float64} - @test rx3 isa Reaction{SymbolicT, Any} - @test rx4 isa Reaction{SymbolicT, Rational{Int64}} - @test rx5 isa Reaction{SymbolicT, Any} - @test rx6 isa Reaction{SymbolicT, Any} + @test rx1 isa Reaction{SymbolicT,Int64} + @test rx2 isa Reaction{SymbolicT,Float64} + @test rx3 isa Reaction{SymbolicT,Any} + @test rx4 isa Reaction{SymbolicT,Rational{Int64}} + @test rx5 isa Reaction{SymbolicT,Any} + @test rx6 isa Reaction{SymbolicT,Any} # Check `Reaction` net stoichiometries. issetequal(rx1.netstoich, [X => -1]) issetequal(rx2.netstoich, [x => -1.5, Y => 1.0]) issetequal(rx3.netstoich, [x => -n1 - n2, X => -n2]) - issetequal(rx4.netstoich, [X => -1 // 3, Y => -2 // 1, Z => 2 // 1]) + issetequal(rx4.netstoich, [X => -1//3, Y => -2//1, Z => 2//1]) issetequal(rx5.netstoich, [X => -1, Y => n1 - 3, Z => n2]) issetequal(rx6.netstoich, [X => -n1, x => 1]) end @@ -55,13 +55,13 @@ let @variables A(t) # Tests that the three-argument constructor generates correct result. - @test Reaction(k * A, [X], [Y, Z]) == Reaction(k * A, [X], [Y, Z], [1], [1, 1]) + @test Reaction(k*A, [X], [Y, Z]) == Reaction(k*A, [X], [Y, Z], [1], [1, 1]) # Tests that `[]` and `nothing` can be used interchangeably. - @test Reaction(k * A, [X, Z], nothing) == Reaction(k * A, [X, Z], []) - @test Reaction(k * A, nothing, [Y, Z]) == Reaction(k * A, [], [Y, Z]) - @test Reaction(k * A, [X, Z], nothing, [n1 + n2, 2], nothing) == Reaction(k * A, [X, Z], [], [n1 + n2, 2], []) - @test Reaction(k * A, nothing, [Y, Z], nothing, [n1 + n2, 2]) == Reaction(k * A, [], [Y, Z], [], [n1 + n2, 2]) + @test Reaction(k*A, [X, Z], nothing) == Reaction(k*A, [X, Z], []) + @test Reaction(k*A, nothing, [Y, Z]) == Reaction(k*A, [], [Y, Z]) + @test Reaction(k*A, [X, Z], nothing, [n1 + n2, 2], nothing) == Reaction(k*A, [X, Z], [], [n1 + n2, 2], []) + @test Reaction(k*A, nothing, [Y, Z], nothing, [n1 + n2, 2]) == Reaction(k*A, [], [Y, Z], [], [n1 + n2, 2]) end # Tests that various incorrect inputs yields errors. @@ -72,23 +72,23 @@ let @variables A(t) # Neither substrates nor products. - @test_throws ArgumentError Reaction(k * A, [], []) + @test_throws ArgumentError Reaction(k*A, [], []) # Substrate vector not of equal length to substrate stoichiometry vector. - @test_throws ArgumentError Reaction(k * A, [X, X, Z], [], [1, 2], []) + @test_throws ArgumentError Reaction(k*A, [X, X, Z], [], [1, 2], []) # Product vector not of equal length to product stoichiometry vector. - @test_throws ArgumentError Reaction(k * A, [], [X, X, Z], [], [1, 2]) + @test_throws ArgumentError Reaction(k*A, [], [X, X, Z], [], [1, 2]) # Repeated substrates. - @test_throws ArgumentError Reaction(k * A, [X, X, Z], []) + @test_throws ArgumentError Reaction(k*A, [X, X, Z], []) # Repeated products. - @test_throws ArgumentError Reaction(k * A, [], [Y, Z, Z]) + @test_throws ArgumentError Reaction(k*A, [], [Y, Z, Z]) # Non-valid reactants (parameter or variable). - @test_throws ArgumentError Reaction(k * A, [], [A]) - @test_throws ArgumentError Reaction(k * A, [], [k]) + @test_throws ArgumentError Reaction(k*A, [], [A]) + @test_throws ArgumentError Reaction(k*A, [], [k]) end @@ -135,7 +135,7 @@ let @species X(t) X2(t) metadata = [:noise_scaling => 0.0] - r = Reaction(k, [X], [X2], [2], [1]; metadata = metadata) + r = Reaction(k, [X], [X2], [2], [1]; metadata=metadata) @test Catalyst.getmetadata_dict(r) == [:noise_scaling => 0.0] @test hasmetadata(r, :noise_scaling) @@ -148,7 +148,7 @@ let @test getmetadata(r, :test_metadata) == 1111 metadata_repeated = [:noise_scaling => 0.0, :noise_scaling => 1.0, :metadata_entry => "unused"] - @test_throws Exception Reaction(k, [X], [X2], [2], [1]; metadata = metadata_repeated) + @test_throws Exception Reaction(k, [X], [X2], [2], [1]; metadata=metadata_repeated) end # Tests accessors for system without metadata. @@ -156,12 +156,12 @@ let @parameters k @species X(t) X2(t) - metadata = Pair{Symbol, Any}[] + metadata = Pair{Symbol,Any}[] r1 = Reaction(k, [X], [X2], [2], [1]) - r2 = Reaction(k, [X], [X2], [2], [1]; metadata = metadata) + r2 = Reaction(k, [X], [X2], [2], [1]; metadata=metadata) @test isequal(r1, r2) - @test Catalyst.getmetadata_dict(r1) == Pair{Symbol, Any}[] + @test Catalyst.getmetadata_dict(r1) == Pair{Symbol,Any}[] @test !hasmetadata(r1, :md) end @@ -172,16 +172,16 @@ let @parameters k @species X(t) X2(t) - metadata = Pair{Symbol, Any}[] + metadata = Pair{Symbol,Any}[] push!(metadata, :md_1 => 1.0) push!(metadata, :md_2 => false) push!(metadata, :md_3 => "Hello world") push!(metadata, :md_4 => :sym) - push!(metadata, :md_5 => X + X2^k - 1) + push!(metadata, :md_5 => X + X2^k -1) push!(metadata, :md_6 => (0.1, 2.0)) - r = Reaction(k, [X], [X2], [2], [1]; metadata = metadata) + r = Reaction(k, [X], [X2], [2], [1]; metadata=metadata) - @test Catalyst.getmetadata_dict(r) isa Vector{Pair{Symbol, Any}} + @test Catalyst.getmetadata_dict(r) isa Vector{Pair{Symbol,Any}} @test hasmetadata(r, :md_1) @test hasmetadata(r, :md_2) @test hasmetadata(r, :md_3) @@ -194,7 +194,7 @@ let @test isequal(getmetadata(r, :md_2), false) @test isequal(getmetadata(r, :md_3), "Hello world") @test isequal(getmetadata(r, :md_4), :sym) - @test isequal(getmetadata(r, :md_5), X + X2^k - 1) + @test isequal(getmetadata(r, :md_5), X + X2^k -1) @test isequal(getmetadata(r, :md_6), (0.1, 2.0)) end @@ -204,7 +204,7 @@ let @species X(t) X2(t) r1 = Reaction(k, [X], [X2], [2], [1]) - r2 = Reaction(k, [X], [X2], [2], [1]; metadata = [:noise_scaling => η]) + r2 = Reaction(k, [X], [X2], [2], [1]; metadata=[:noise_scaling => η]) @test !Catalyst.hasnoisescaling(r1) @test Catalyst.hasnoisescaling(r2) @@ -219,7 +219,7 @@ let @species X(t) X2(t) r1 = Reaction(k, [X], [X2], [2], [1]) - r2 = Reaction(k, [X], [X2], [2], [1]; metadata = [:description => "A reaction"]) + r2 = Reaction(k, [X], [X2], [2], [1]; metadata=[:description => "A reaction"]) @test !Catalyst.hasdescription(r1) @test Catalyst.hasdescription(r2) @@ -234,7 +234,7 @@ let @species X(t) X2(t) r1 = Reaction(k, [X], [X2], [2], [1]) - r2 = Reaction(k, [X], [X2], [2], [1]; metadata = [:misc => ('M', :M)]) + r2 = Reaction(k, [X], [X2], [2], [1]; metadata=[:misc => ('M', :M)]) @test !Catalyst.hasmisc(r1) @test Catalyst.hasmisc(r2) @@ -246,10 +246,10 @@ end let t = default_t() @variables E(t) F(t) - @species A(t) B(t) C(t) D(t) + @species A(t) B(t) C(t) D(t) @parameters k1, k2, η - rx = Reaction(k1 * E, [A, B], [C], [k2 * D, 3], [F], metadata = [:noise_scaling => η]) + rx = Reaction(k1*E, [A, B], [C], [k2*D, 3], [F], metadata = [:noise_scaling => η]) us = OrderedSet{Symbolics.SymbolicT}() ps = OrderedSet{Symbolics.SymbolicT}() @test ModelingToolkitBase.eqtype_supports_collect_vars(rx) == true @@ -271,10 +271,8 @@ let rx2 = Reaction(k1, [A], [B], [2], [1]) @test has_physical_scale(rx2) == false - rx3 = Reaction( - k1, [A], [B], [2], [1]; - metadata = [:physical_scale => PhysicalScale.Jump, :noise_scaling => 0.1] - ) + rx3 = Reaction(k1, [A], [B], [2], [1]; + metadata = [:physical_scale => PhysicalScale.Jump, :noise_scaling => 0.1]) @test has_physical_scale(rx3) - @test get_physical_scale(rx3) == PhysicalScale.Jump -end + @test get_physical_scale(rx3) == PhysicalScale.Jump +end \ No newline at end of file diff --git a/test/reactionsystem_core/reactionsystem.jl b/test/reactionsystem_core/reactionsystem.jl index c97eb9c022..d1136f37f0 100644 --- a/test/reactionsystem_core/reactionsystem.jl +++ b/test/reactionsystem_core/reactionsystem.jl @@ -20,8 +20,7 @@ include("../test_functions.jl") # Create the network. @parameters k[1:20] @species A(t) B(t) C(t) D(t) -rxs = [ - Reaction(k[1], nothing, [A]), # 0 -> A +rxs = [Reaction(k[1], nothing, [A]), # 0 -> A Reaction(k[2], [B], nothing), # B -> 0 Reaction(k[3], [A], [C]), # A -> C Reaction(k[4], [C], [A, B]), # C -> A + B @@ -56,18 +55,18 @@ function oderhs(u, kv, t) k = kv[1] du = zeros(eltype(u), 4) du[1] = k[1] - k[3] * A + k[4] * C + 2 * k[5] * C - k[6] * A * B + k[7] * B^2 / 2 - - k[9] * A * B - k[10] * A^2 - k[11] * A^2 / 2 - k[12] * A * B^3 * C^4 / 144 - - 3 * k[13] * A^3 * B / 6 + 2 * k[14] - k[15] * A / (2 + A) - k[16] - - k[19] * t * A + k[9] * A * B - k[10] * A^2 - k[11] * A^2 / 2 - k[12] * A * B^3 * C^4 / 144 - + 3 * k[13] * A^3 * B / 6 + 2 * k[14] - k[15] * A / (2 + A) - k[16] - + k[19] * t * A du[2] = -k[2] * B + k[4] * C - k[6] * A * B - k[7] * B^2 - k[8] * A * B - k[9] * A * B + - k[11] * A^2 / 2 - 3 * k[12] * A * B^3 * C^4 / 144 - k[13] * A^3 * B / 6 + - k[16] + 2 * k[18] * B + k[19] * t * A - 2 * k[20] * t * A * B^2 * C + k[11] * A^2 / 2 - 3 * k[12] * A * B^3 * C^4 / 144 - k[13] * A^3 * B / 6 + + k[16] + 2 * k[18] * B + k[19] * t * A - 2 * k[20] * t * A * B^2 * C du[3] = k[3] * A - k[4] * C - k[5] * C + k[6] * A * B + k[8] * A * B + k[9] * A * B + - k[10] * A^2 / 2 - 2 * k[12] * A * B^3 * C^4 / 144 - - 2 * k[17] * A * exp(B) * C^2 / 2 - k[20] * t * A * B^2 * C + k[10] * A^2 / 2 - 2 * k[12] * A * B^3 * C^4 / 144 - + 2 * k[17] * A * exp(B) * C^2 / 2 - k[20] * t * A * B^2 * C du[4] = k[9] * A * B + k[10] * A^2 / 2 + 3 * k[12] * A * B^3 * C^4 / 144 + - k[17] * A * exp(B) * C^2 / 2 + 2 * k[20] * t * A * B^2 * C - return du + k[17] * A * exp(B) * C^2 / 2 + 2 * k[20] * t * A * B^2 * C + du end # SDE noise coefs. @@ -80,28 +79,26 @@ function sdenoise(u, kv, t) G = zeros(eltype(u), length(k), length(u)) z = zero(eltype(u)) - G = [ - sqrt(k[1]) z z z; - z -sqrt(k[2] * B) z z; - -sqrt(k[3] * A) z sqrt(k[3] * A) z; - sqrt(k[4] * C) sqrt(k[4] * C) -sqrt(k[4] * C) z; - 2 * sqrt(k[5] * C) z -sqrt(k[5] * C) z; - -sqrt(k[6] * A * B) -sqrt(k[6] * A * B) sqrt(k[6] * A * B) z; - sqrt(k[7] * B^2 / 2) -2 * sqrt(k[7] * B^2 / 2) z z; - z -sqrt(k[8] * A * B) sqrt(k[8] * A * B) z; - -sqrt(k[9] * A * B) -sqrt(k[9] * A * B) sqrt(k[9] * A * B) sqrt(k[9] * A * B); - -2 * sqrt(k[10] * A^2 / 2) z sqrt(k[10] * A^2 / 2) sqrt(k[10] * A^2 / 2); - -sqrt(k[11] * A^2 / 2) sqrt(k[11] * A^2 / 2) z z; - -sqrt(k[12] * A * B^3 * C^4 / 144) -3 * sqrt(k[12] * A * B^3 * C^4 / 144) -2 * sqrt(k[12] * A * B^3 * C^4 / 144) 3 * sqrt(k[12] * A * B^3 * C^4 / 144); - -3 * sqrt(k[13] * A^3 * B / 6) -sqrt(k[13] * A^3 * B / 6) z z; - 2 * sqrt(k[14]) z z z; - -sqrt(k[15] * A / (2 + A)) z z z; - -sqrt(k[16]) sqrt(k[16]) z z; - z z -2 * sqrt(k[17] * A * exp(B) * C^2 / 2) sqrt(k[17] * A * exp(B) * C^2 / 2); - z 2 * sqrt(k[18] * B) z z; - -sqrt(k[19] * t * A) sqrt(k[19] * t * A) z z; - z -2 * sqrt(k[20] * t * A * B^2 * C) -sqrt(k[20] * t * A * B^2 * C) +2 * sqrt(k[20] * t * A * B^2 * C) - ]' + G = [sqrt(k[1]) z z z; + z -sqrt(k[2] * B) z z; + -sqrt(k[3] * A) z sqrt(k[3] * A) z; + sqrt(k[4] * C) sqrt(k[4] * C) -sqrt(k[4] * C) z; + 2*sqrt(k[5] * C) z -sqrt(k[5] * C) z; + -sqrt(k[6] * A * B) -sqrt(k[6] * A * B) sqrt(k[6] * A * B) z; + sqrt(k[7] * B^2 / 2) -2*sqrt(k[7] * B^2 / 2) z z; + z -sqrt(k[8] * A * B) sqrt(k[8] * A * B) z; + -sqrt(k[9] * A * B) -sqrt(k[9] * A * B) sqrt(k[9] * A * B) sqrt(k[9] * A * B); + -2*sqrt(k[10] * A^2 / 2) z sqrt(k[10] * A^2 / 2) sqrt(k[10] * A^2 / 2); + -sqrt(k[11] * A^2 / 2) sqrt(k[11] * A^2 / 2) z z; + -sqrt(k[12] * A * B^3 * C^4 / 144) -3*sqrt(k[12] * A * B^3 * C^4 / 144) -2*sqrt(k[12] * A * B^3 * C^4 / 144) 3*sqrt(k[12] * A * B^3 * C^4 / 144); + -3*sqrt(k[13] * A^3 * B / 6) -sqrt(k[13] * A^3 * B / 6) z z; + 2*sqrt(k[14]) z z z; + -sqrt(k[15] * A / (2 + A)) z z z; + -sqrt(k[16]) sqrt(k[16]) z z; + z z -2*sqrt(k[17] * A * exp(B) * C^2 / 2) sqrt(k[17] * A * exp(B) * C^2 / 2); + z 2*sqrt(k[18] * B) z z; + -sqrt(k[19] * t * A) sqrt(k[19] * t * A) z z; + z -2*sqrt(k[20] * t * A * B^2 * C) -sqrt(k[20] * t * A * B^2 * C) +2*sqrt(k[20] * t * A * B^2 * C)]' return G end @@ -123,7 +120,7 @@ let def_p = [k => kvals] def_u0 = [A => 0.5, B => 1.0, C => 1.5, D => 2.0] defs = merge(Dict(def_p), Dict(def_u0)) - defs_typed = convert(Dict{Symbolics.SymbolicT, Symbolics.SymbolicT}, defs) + defs_typed = convert(Dict{Symbolics.SymbolicT,Symbolics.SymbolicT}, defs) @named rs = ReactionSystem(rxs, t, [A, B, C, D], [k]; initial_conditions = defs) rs = complete(rs) @@ -169,8 +166,7 @@ end # Test with jump System. let @species A(t) B(t) C(t) D(t) E(t) F(t) - rxs = [ - Reaction(k[1], nothing, [A]), # 0 -> A + rxs = [Reaction(k[1], nothing, [A]), # 0 -> A Reaction(k[2], [B], nothing), # B -> 0 Reaction(k[3], [A], [C]), # A -> C Reaction(k[4], [C], [A, B]), # C -> A + B @@ -207,10 +203,8 @@ let u0 = rand(rng, 2:10, 6) u0map = unknowns(js) .=> u0 ttt = rand(rng) - jumps = Vector{Union{ConstantRateJump, MassActionJump, VariableRateJump}}( - undef, - length(rxs) - ) + jumps = Vector{Union{ConstantRateJump, MassActionJump, VariableRateJump}}(undef, + length(rxs)) let jumps[1] = MassActionJump(p[1], Vector{Pair{Int, Int}}(), [1 => 1]) @@ -224,38 +218,24 @@ let jumps[9] = MassActionJump(p[9], [1 => 1, 2 => 1], [1 => -1, 2 => -1, 3 => 1, 4 => 1]) jumps[10] = MassActionJump(p[10], [1 => 2], [1 => -2, 3 => 1, 4 => 1]) jumps[11] = MassActionJump(p[11], [1 => 2], [1 => -1, 2 => 1]) - jumps[12] = MassActionJump( - p[12], [1 => 1, 2 => 3, 3 => 4], - [1 => -1, 2 => -3, 3 => -2, 4 => 3] - ) + jumps[12] = MassActionJump(p[12], [1 => 1, 2 => 3, 3 => 4], + [1 => -1, 2 => -3, 3 => -2, 4 => 3]) jumps[13] = MassActionJump(p[13], [1 => 3, 2 => 1], [1 => -3, 2 => -1]) jumps[14] = MassActionJump(p[14], Vector{Pair{Int, Int}}(), [1 => 2]) - jumps[15] = ConstantRateJump( - (u, p, t) -> p[15] * u[1] / (2 + u[1]), - integrator -> (integrator.u[1] -= 1) - ) - jumps[16] = ConstantRateJump( - (u, p, t) -> p[16], - integrator -> (integrator.u[1] -= 1; integrator.u[2] += 1) - ) - jumps[17] = ConstantRateJump( - (u, p, t) -> p[17] * u[1] * exp(u[2]) * binomial(u[3], 2), - integrator -> (integrator.u[3] -= 2; integrator.u[4] += 1) - ) - jumps[18] = ConstantRateJump( - (u, p, t) -> p[18] * u[2], - integrator -> (integrator.u[2] += 2) - ) - - jumps[19] = VariableRateJump( - (u, p, t) -> p[19] * u[4] * t, - integrator -> (integrator.u[4] -= 1; integrator.u[5] += 1) - ) - jumps[20] = VariableRateJump( - (u, p, t) -> p[20] * t * u[1] * binomial(u[4], 2) * u[5], - integrator -> (integrator.u[4] -= 2; integrator.u[5] -= 1; integrator.u[6] += 2) - ) + jumps[15] = ConstantRateJump((u, p, t) -> p[15] * u[1] / (2 + u[1]), + integrator -> (integrator.u[1] -= 1)) + jumps[16] = ConstantRateJump((u, p, t) -> p[16], + integrator -> (integrator.u[1] -= 1; integrator.u[2] += 1)) + jumps[17] = ConstantRateJump((u, p, t) -> p[17] * u[1] * exp(u[2]) * binomial(u[3], 2), + integrator -> (integrator.u[3] -= 2; integrator.u[4] += 1)) + jumps[18] = ConstantRateJump((u, p, t) -> p[18] * u[2], + integrator -> (integrator.u[2] += 2)) + + jumps[19] = VariableRateJump((u, p, t) -> p[19] * u[4] * t, + integrator -> (integrator.u[4] -= 1; integrator.u[5] += 1)) + jumps[20] = VariableRateJump((u, p, t) -> p[20] * t * u[1] * binomial(u[4], 2) * u[5], + integrator -> (integrator.u[4] -= 2; integrator.u[5] -= 1; integrator.u[6] += 2)) unknownoid = Dict(unknown => i for (i, unknown) in enumerate(unknowns(js))) jprob = JumpProblem(js, merge(Dict(u0map), Dict(pmap)), (0.0, 1.0)) @@ -263,10 +243,8 @@ let jspmapper = MT.JumpSysMajParamMapper(js, mtkpars) symbolic_majs = MassActionJump[MT.jumps(js)[i] for i in midxs] symmaj = MT.assemble_maj(symbolic_majs, unknownoid, jspmapper) - maj = MassActionJump( - symmaj.param_mapper(mtkpars), symmaj.reactant_stoch, symmaj.net_stoch, - symmaj.param_mapper, scale_rates = false - ) + maj = MassActionJump(symmaj.param_mapper(mtkpars), 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] @@ -317,9 +295,9 @@ let rs_dsl = @reaction_network rs begin @parameters p[1:2] k d1 d2 @species (X(t))[1:2] Y1(t) Y2(t) - (p[1], p[2]), 0 --> (X[1], X[2]) - k, (X[1], X[2]) --> (Y1, Y2) - (d1, d2), (Y1, Y2) --> 0 + (p[1],p[2]), 0 --> (X[1],X[2]) + k, (X[1],X[2]) --> (Y1,Y2) + (d1,d2), (Y1,Y2) --> 0 end # Checks equivalence. @@ -331,12 +309,12 @@ let [X[1] => 2.0, X[2] => 5.0, Y1 => 0.2, Y2 => 0.5], [rs_dsl.X => [2.0, 5.0], rs_dsl.Y1 => 0.2, rs_dsl.Y2 => 0.5], [rs_dsl.X[1] => 2.0, X[2] => 5.0, rs_dsl.Y1 => 0.2, rs_dsl.Y2 => 0.5], - [:X => [2.0, 5.0], :Y1 => 0.2, :Y2 => 0.5], + [:X => [2.0, 5.0], :Y1 => 0.2, :Y2 => 0.5] ] ps_alts = [ [p => [1.0, 10.0], d1 => 5.0, d2 => 4.0, k => 2.0], [rs_dsl.p => [1.0, 10.0], rs_dsl.d1 => 5.0, rs_dsl.d2 => 4.0, rs_dsl.k => 2.0], - [:p => [1.0, 10.0], :d1 => 5.0, :d2 => 4.0, :k => 2.0], + [:p => [1.0, 10.0], :d1 => 5.0, :d2 => 4.0, :k => 2.0] ] # Loops through all inputs and check that the correct steady state is reached @@ -344,11 +322,11 @@ let # Technically only one model needs to be check. However, "equivalent" models in MTK can still # have slight differences, so checking for both here to be certain. for rs in [rs_prog, rs_dsl] - oprob = ODEProblem(rs, u0_alts[1], (0.0, 10000.0), ps_alts[1]) + oprob = ODEProblem(rs, u0_alts[1], (0.0, 10000.), ps_alts[1]) for rs in [rs_prog, rs_dsl], u0 in u0_alts, p in ps_alts oprob_remade = remake(oprob; u0, p) - sol = solve(oprob_remade, Vern7(); abstol = 1.0e-8, reltol = 1.0e-8) - @test sol[[X[1], X[2], Y1, Y2]][end] ≈ [0.5, 5.0, 0.2, 2.5] + sol = solve(oprob_remade, Vern7(); abstol = 1e-8, reltol = 1e-8) + @test sol[[X[1], X[2], Y1 ,Y2]][end] ≈ [0.5, 5.0, 0.2, 2.5] end end end @@ -360,7 +338,7 @@ let # Creates a `ReactionSystem`. @parameters x @parameters p d - @species S(t, x) + @species S(t,x) rxs = [ Reaction(p, [], [S]), Reaction(d, [S], []), @@ -385,7 +363,7 @@ let # Creates a reaction system with subsystems. sub_rxs = [ Reaction(k1, [X1], []), - Reaction(k2, [X2], []), + Reaction(k2, [X2], []) ] @named sub_rs = ReactionSystem(sub_rxs, t) sub_eqs = [ @@ -396,7 +374,7 @@ let @named sub_osys = ReactionSystem(sub_eqs, t) rxs = [ Reaction(k2, [X2], []), - Reaction(k3, [X3], []), + Reaction(k3, [X3], []) ] @named rs = ReactionSystem(rxs, t; systems = [sub_rs, sub_osys]) @@ -444,7 +422,7 @@ function f!(du, u, p, t) du[2] = -k1 * C * D + k2 * C * E du[3] = k1 * C * D - k2 * C * E du[4] = -C - return nothing + nothing end function fs!(du, u, p, t) A = p[1] @@ -457,7 +435,7 @@ function fs!(du, u, p, t) du[1] = k1 * A - k2 * B du[2] = -k1 * C * D + k2 * C * E du[3] = k1 * C * D - k2 * C * E - return nothing + nothing end function gs!(dg, u, p, t) A = p[1] @@ -474,7 +452,7 @@ function gs!(dg, u, p, t) dg[2, 4] = sqrt(k2 * C * E) dg[3, 3] = -dg[2, 3] dg[3, 4] = -dg[2, 4] - return nothing + nothing end # Tests for BC and constant species. @@ -482,13 +460,11 @@ let @parameters k1 k2 A [isconstantspecies = true] @species B(t) C(t) [isbcspecies = true] D(t) E(t) Dt = default_time_deriv() - eqs = [ - (@reaction k1, $A --> B), + eqs = [(@reaction k1, $A --> B), (@reaction k2, B --> $A), (@reaction k1, $C + D --> E + $C), Dt(C) ~ -C, - (@reaction k2, E + $C --> $C + D) - ] + (@reaction k2, E + $C --> $C + D)] @named rs = ReactionSystem(eqs, t) rs = complete(rs) @test all(eq -> eq isa Reaction, MT.get_eqs(rs)[1:4]) @@ -509,8 +485,8 @@ let ofun = ODEFunction(f!; sys = MT.SymbolCache(syms)) oprob2 = ODEProblem(ofun, u0, tspan, p) saveat = tspan[2] / 50 - abstol = 1.0e-10 - reltol = 1.0e-10 + abstol = 1e-10 + reltol = 1e-10 sol1 = solve(oprob1, Tsit5(); saveat, abstol, reltol) sol2 = solve(oprob2, Tsit5(); saveat, abstol, reltol) for i in eachindex(sts) @@ -520,13 +496,11 @@ let # Test sde systems. # BC species require a constraint equation to define their dynamics in SDE Systems. let - eqs = [ - (@reaction k1, $A --> B), + eqs = [(@reaction k1, $A --> B), (@reaction k2, B --> $A), (@reaction k1, $C + D --> E + $C), (@reaction k2, E + $C --> $C + D), - Dt(C) ~ 0, - ] # Constraint equation for BC species (constant in time) + Dt(C) ~ 0] # Constraint equation for BC species (constant in time) @named rs = ReactionSystem(eqs, t) rs = complete(rs) ssys = complete(sde_model(rs)) @@ -549,14 +523,12 @@ let let # Test jump systems. - rxs = [ - (@reaction k1, $A --> B), + rxs = [(@reaction k1, $A --> B), (@reaction k2, B --> $A), (@reaction k1, $C + D --> E + $C), (@reaction k2, $C + E --> $C + D), (@reaction k1 * t, $A + $C --> B + $C), - (@reaction k1 * B, 2 * $A + $C --> $C + B) - ] + (@reaction k1 * B, 2 * $A + $C --> $C + B)] @named rs = ReactionSystem(rxs, t) rs = complete(rs) jsys = complete(jump_model(rs)) @@ -590,18 +562,12 @@ end let @parameters k1 A [isconstantspecies = true] @species C(t) [isbcspecies = true] B1(t) B2(t) B3(t) - @named rn = ReactionSystem( - [ - (@reaction k1, $C --> B1 + $C), - (@reaction k1, $A --> B2), - (@reaction 10 * k1, ∅ --> B3) - ], t - ) + @named rn = ReactionSystem([(@reaction k1, $C --> B1 + $C), + (@reaction k1, $A --> B2), + (@reaction 10 * k1, ∅ --> B3)], t) rn = complete(rn) - jprob = JumpProblem( - rn, [A => 10, C => 10, B1 => 0, B2 => 0, B3 => 0], (0.0, 10.0), - [k1 => 1.0]; rng, save_positions = (false, false) - ) + jprob = JumpProblem(rn, [A => 10, C => 10, B1 => 0, B2 => 0, B3 => 0], (0.0, 10.0), + [k1 => 1.0]; rng, save_positions = (false, false)) umean = zeros(4) Nsims = 40000 for i in 1:Nsims @@ -609,8 +575,8 @@ let umean += sol(10.0, idxs = [B1, B2, B3, C]) end umean /= Nsims - @test isapprox(umean[1], umean[2]; rtol = 1.0e-2) - @test isapprox(umean[1], umean[3]; rtol = 1.0e-2) + @test isapprox(umean[1], umean[2]; rtol = 1e-2) + @test isapprox(umean[1], umean[3]; rtol = 1e-2) @test umean[4] == 10 end @@ -619,9 +585,9 @@ end # Test various species related checker functions. let # Creates species and parameters. - @species X(t) Y(t) [isbcspecies = true] - @parameters x y [isconstantspecies = true] - @discretes xt(t) yt(t) [isconstantspecies = true] + @species X(t) Y(t) [isbcspecies=true] + @parameters x y [isconstantspecies=true] + @discretes xt(t) yt(t) [isconstantspecies=true] # Tests properties. @test !isspecies(x) @@ -650,7 +616,7 @@ end # Tests various erroneous `ReactionSystem` creations. let # Prepare model inputs. - @parameters k1 k2 x [isconstantspecies = true] Γ + @parameters k1 k2 x [isconstantspecies=true] Γ @species X1(t) X2(t) @variables V(t) @@ -674,13 +640,13 @@ end let # Conversion of non-autonomous `ReactionSystem` to nonlinear `System`. rs = @reaction_network begin - (p / (1 + t), d), 0 <--> X + (p/(1+t),d), 0 <--> X end @test_throws Exception ss_ode_model(rs) # Conversion of non-complete system to various system types. nc = @network_component begin - (p, d), 0 <--> X + (p,d), 0 <--> X end @test_throws Exception ode_model(nc) @test_throws Exception sde_model(nc) @@ -713,7 +679,7 @@ end ### Specialised ReactionSystem Fields ### -# Checks that correct bindings and (default) initial conditions are created and stored in various ways and conversions. +# Checks that correct bindings and (default) iniital conditions are created and stored in various ways and conversions. let # Declare time differential. D = default_time_deriv() @@ -724,16 +690,16 @@ let @parameters ψ1 ψ2 ψ3 ψ4 ψ5 @parameters χ1 χ2 χ3 χ4 χ5 χ6 χ7 χ8 @parameters a1 a2 a3 a4 - @parameters b1 = 1.0 b2 = 2.0 b3 = 3.0 b4 = 4.0 - @parameters c1 = θ1 c2 = θ1 + θ2 c3 = log(1 + θ3) c4 = θ4 * θ5^2 + @parameters b1=1.0 b2=2.0 b3=3.0 b4=4.0 + @parameters c1=θ1 c2=θ1+θ2 c3=log(1+θ3) c4=θ4 * θ5^2 @parameters d1 d2 d3 d4 @species X1(t) X2(t) X3(t) X4(t) - @species Y1(t) = 0.1 Y2(t) = 0.2 Y3(t) = 0.3 Y4(t) = 0.4 - @species Z1(t) = ϕ1 Z2(t) = ϕ1 + ϕ2 Z3(t) = log(1 + ϕ3) Z4(t) = ϕ5 * ϕ4^2 + @species Y1(t)=0.1 Y2(t)=0.2 Y3(t)=0.3 Y4(t)=0.4 + @species Z1(t)=ϕ1 Z2(t)=ϕ1+ϕ2 Z3(t)=log(1+ϕ3) Z4(t)=ϕ5 * ϕ4^2 @species U1(t) U2(t) U3(t) U4(t) @variables K1(t) K2(t) K3(t) K4(t) - @variables L1(t) = 10.0 L2(t) = 20.0 L3(t) = 30.0 L4(t) = 40.0 - @variables M1(t) = ψ1 M2(t) = ψ1 + ψ2 M3(t) = log(1 + ψ3) M4(t) = ψ4 * ψ5^2 + @variables L1(t)=10.0 L2(t)=20.0 L3(t)=30.0 L4(t)=40.0 + @variables M1(t)=ψ1 M2(t)=ψ1+ψ2 M3(t)=log(1+ψ3) M4(t)=ψ4 * ψ5^2 @variables N1(t) N2(t) N3(t) N4(t) ps = [θ1, θ2, θ3, θ4, θ5, ϕ1, ϕ2, ϕ3, ϕ4, ϕ5, ψ1, ψ2, ψ3, ψ4, ψ5, χ1, χ2, χ3, χ4, χ5, χ6, χ7, χ8, a1, a2, a3, a4, b1, b2, b3, b4, c1, c2, c3, c4, d1, d2, d3, d4] us = [X1, X2, X3, X4, Y1, Y2, Y3, Y4, Z1, Z2, Z3, Z4, U1, U2, U3, U4, K1, K2, K3, K4, L1, L2, L3, L4, M1, M2, M3, M4, N1, N2, N3, N4] @@ -752,10 +718,10 @@ let D(K2) ~ a4 - K2, D(K3) ~ d1 - K3, D(K4) ~ d2 - K4, - L1^2 + X1 ~ b4^2 + K1^2, - L2^2 + X2 ~ c4^2 + K2^2, - L3^2 + X3 ~ 1^2 + K3^2, - L4^2 + X4 ~ 1^2 + K4^2, + L1^2 + X1 ~ b4 ^ 2 + K1^2, + L2^2 + X2 ~ c4 ^ 2 + K2^2, + L3^2 + X3 ~ 1 ^ 2 + K3^2, + L4^2 + X4 ~ 1 ^ 2 + K4^2, D(M1) ~ d3 - M1, D(M2) ~ d4 - M2, D(M3) ~ 1 - M3, @@ -768,12 +734,12 @@ let initial_conditions = [ d1 => 100.0, d2 => χ1, U1 => 200.0, U2 => χ2^3, - N1 => 300.0, N2 => χ3 + log(χ4 + 1), + N1 => 300.0, N2 => χ3 + log(χ4+ 1), ] bindings = [ d3 => 1000.0, d4 => χ5, U3 => 2000.0, U4 => χ6^3, - N3 => 3000.0, N4 => χ7 + log(χ8 + 1), + N3 => 3000.0, N4 => χ7 + log(χ8+ 1), ] @named rs = ReactionSystem(rxs, t, us, ps; initial_conditions, bindings) rs_complete = complete(rs) @@ -784,7 +750,7 @@ let rs, rs_complete, ode_model(rs_complete), complete(ode_model(rs_complete)), mtkcompile(ode_model(rs_complete)), sde_model(rs_complete), complete(sde_model(rs_complete)), mtkcompile(sde_model(rs_complete)), - hybrid_model(rs_complete; default_scale), complete(hybrid_model(rs_complete; default_scale)), mtkcompile(hybrid_model(rs_complete; default_scale)), + hybrid_model(rs_complete; default_scale), complete(hybrid_model(rs_complete; default_scale)), mtkcompile(hybrid_model(rs_complete; default_scale)) ] # Checks that all stored bindings and initial conditions are correct. @@ -795,15 +761,15 @@ let unwrap(L1) => tosym(10.0), unwrap(L2) => tosym(20.0), unwrap(L3) => tosym(30.0), unwrap(L4) => tosym(40.0), unwrap(d1) => tosym(100.0), unwrap(d2) => unwrap(χ1), unwrap(U1) => tosym(200.0), unwrap(U2) => unwrap(χ2^3), - unwrap(N1) => tosym(300.0), unwrap(N2) => unwrap(χ3 + log(χ4 + 1)), + unwrap(N1) => tosym(300.0), unwrap(N2) => unwrap(χ3 + log(χ4+ 1)), ] binds = [ - unwrap(c1) => unwrap(θ1), unwrap(c2) => unwrap(θ1 + θ2), unwrap(c3) => unwrap(log(1 + θ3)), unwrap(c4) => unwrap(θ4 * θ5^2), - unwrap(Z1) => unwrap(ϕ1), unwrap(Z2) => unwrap(ϕ1 + ϕ2), unwrap(Z3) => unwrap(log(1 + ϕ3)), unwrap(Z4) => unwrap(ϕ5 * ϕ4^2), - unwrap(M1) => unwrap(ψ1), unwrap(M2) => unwrap(ψ1 + ψ2), unwrap(M3) => unwrap(log(1 + ψ3)), unwrap(M4) => unwrap(ψ4 * ψ5^2), + unwrap(c1) => unwrap(θ1), unwrap(c2) => unwrap(θ1 + θ2), unwrap(c3) => unwrap(log(1+θ3)), unwrap(c4) => unwrap(θ4 * θ5^2), + unwrap(Z1) => unwrap(ϕ1), unwrap(Z2) => unwrap(ϕ1+ϕ2), unwrap(Z3) => unwrap(log(1+ϕ3)), unwrap(Z4) => unwrap(ϕ5 * ϕ4^2), + unwrap(M1) => unwrap(ψ1), unwrap(M2) => unwrap(ψ1+ψ2), unwrap(M3) => unwrap(log(1+ψ3)), unwrap(M4) => unwrap(ψ4 * ψ5^2), unwrap(d3) => tosym(1000.0), unwrap(d4) => unwrap(χ5), unwrap(U3) => tosym(2000.0), unwrap(U4) => unwrap(χ6^3), - unwrap(N3) => tosym(3000.0), unwrap(N4) => unwrap(χ7 + log(χ8 + 1)), + unwrap(N3) => tosym(3000.0), unwrap(N4) => unwrap(χ7 + log(χ8+ 1)), ] for sys in all_sys @test issetequal((collect(ModelingToolkitBase.get_initial_conditions(sys))), ics) @@ -832,7 +798,7 @@ let Reaction(k1, [Y1], []), Reaction(k2, [Y2], []), Reaction(k3, [Y3], []), - Reaction(k4, [Y4], []), + Reaction(k4, [Y4], []) ] bindings = [d3 => d3_1, d4 => d4_1 + d4_2, X3 => X3_1, X4 => X4_1 + X4_2] initial_conditions = [k3 => k3_1, k4 => k4_1 + k4_2, Y3 => Y3_1, Y4 => Y4_1 + Y4_2] @@ -843,10 +809,10 @@ let d1_1 => 1.0, d2_1 => 0.5, d2_2 => 1.5, d3_1 => 3.0, d4_1 => 1.5, d4_2 => 2.5, X1_1 => 1.0, X2_1 => 0.5, X2_2 => 1.5, X3_1 => 3.0, X4_1 => 1.5, X4_2 => 2.5, k3_1 => 3.0, k4_1 => 1.5, k4_2 => 2.5, - Y3_1 => 3.0, Y4_1 => 1.5, Y4_2 => 2.5, + Y3_1 => 3.0, Y4_1 => 1.5, Y4_2 => 2.5 ] oprob = ODEProblem(rs, [], 10.0, ps) - osol = solve(oprob, Vern7(); saveat = 0.0:0.1:10.0, abstol = 1.0e-8, reltol = 1.0e-8) + osol = solve(oprob, Vern7(); saveat = 0.0:0.1:10.0, abstol = 1e-8, reltol = 1e-8) # Checks that stored parameter values and initial conditions are correct. @test oprob.ps[d1] == osol.ps[d1] == 1.0 @@ -895,20 +861,14 @@ let @parameters k1 k2 @species R(t) - rxs = [ - Reaction(k1 * S, [S, I], [I], [2, 3], [2]), - Reaction(k2 * R, [I], [R]), - ] + 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]), - k1 * S * binomial(S, 2) * binomial(I, 3) - ) + @test isequal(oderatelaw(equations(rs)[1]), + k1 * S * S^2 * I^3 / (factorial(2) * factorial(3))) + @test_skip isequal(jumpratelaw(equations(eqs)[1]), + k1 * S * binomial(S, 2) * binomial(I, 3)) dep = Set() MT.get_variables!(dep, rxs[2], Set(unknowns(rs))) dep2 = Set([R, I]) @@ -920,10 +880,8 @@ let isequal2(a, b) = isequal(simplify(a), simplify(b)) @test isequal2(jumpratelaw(rxs[1]), k1 * S * S * (S - 1) * I * (I - 1) * (I - 2) / 12) - @test isequal2( - jumpratelaw(rxs[1]; combinatoric_ratelaw = false), - k1 * S * S * (S - 1) * I * (I - 1) * (I - 2) - ) + @test isequal2(jumpratelaw(rxs[1]; combinatoric_ratelaw = false), + k1 * S * S * (S - 1) * I * (I - 1) * (I - 2)) @test isequal2(oderatelaw(rxs[1]), k1 * S * S^2 * I^3 / 12) @test isequal2(oderatelaw(rxs[1]; combinatoric_ratelaw = false), k1 * S * S^2 * I^3) @@ -942,25 +900,19 @@ let # Test ConstantRateJump rate scaling. js = complete(jump_model(rs)) - @test isequal2( - MT.jumps(js)[1].rate, - k1 * S * S * (S - 1) * I * (I - 1) * (I - 2) / 12 - ) + @test isequal2(MT.jumps(js)[1].rate, + k1 * S * S * (S - 1) * I * (I - 1) * (I - 2) / 12) js = complete(jump_model(rs; combinatoric_ratelaws = false)) @test isequal2(MT.jumps(js)[1].rate, k1 * S * S * (S - 1) * I * (I - 1) * (I - 2)) js2 = complete(jump_model(rs2)) @test isequal2(MT.jumps(js2)[1].rate, k1 * S * S * (S - 1) * I * (I - 1) * (I - 2)) js3 = complete(jump_model(rs2; combinatoric_ratelaws = true)) - @test isequal2( - MT.jumps(js3)[1].rate, - k1 * S * S * (S - 1) * I * (I - 1) * (I - 2) / 12 - ) + @test isequal2(MT.jumps(js3)[1].rate, + k1 * S * S * (S - 1) * I * (I - 1) * (I - 2) / 12) # Test MassActionJump rate scaling. - rxs = [ - Reaction(k1, [S, I], [I], [2, 3], [2]), - Reaction(k2, [I], [R]), - ] + rxs = [Reaction(k1, [S, I], [I], [2, 3], [2]), + Reaction(k2, [I], [R])] @named rs = ReactionSystem(rxs, t, [S, I, R], [k1, k2]) rs = complete(rs) js = complete(jump_model(rs)) @@ -1054,10 +1006,10 @@ let @test issetequal(parameters(rs), [k, b]) end -# Test parametric initial conditions. +# Test parameteric initial conditions. let @parameters d X0 - @species X(t) = X0 + @species X(t)=X0 rx = Reaction(d, [X], nothing, [1], nothing) @named rs = ReactionSystem([rx], t) rs = complete(rs) @@ -1145,9 +1097,7 @@ end # Additional unsorted tests. let - rn = @reaction_network begin - k, X --> 0 - end + rn = @reaction_network begin k, X --> 0 end isspecies(species(rn)[1]) @test Catalyst.has_species(rn) @test Catalyst.has_rxs(rn) @@ -1191,7 +1141,7 @@ let @test ModelingToolkitBase.getmetadata(complete(ss_ode_model(complete(rs1))), MiscSystemData, nothing) == nothing @test ModelingToolkitBase.getmetadata(complete(ss_ode_model(complete(rs2))), MiscSystemData, nothing) == π - # Check metadata for `ReactionSystem`s where metadata has been updated + # Check metadata for `ReactionSystem`s where metadata has been udpated rs1 = ModelingToolkitBase.setmetadata(rs1, MiscSystemData, "Metadata") rs2 = ModelingToolkitBase.setmetadata(rs2, MiscSystemData, ones(2, 3)) @test ModelingToolkitBase.getmetadata(rs1, MiscSystemData, nothing) == "Metadata" @@ -1216,10 +1166,8 @@ let @test Catalyst.get_parameter_map(rs_plain) === nothing # System with metadata via constructor. - @named rs = ReactionSystem( - [Reaction(k1, [X], [Y])], t; - metadata = [Catalyst.U0Map => u0map, Catalyst.ParameterMap => pmap] - ) + @named rs = ReactionSystem([Reaction(k1, [X], [Y])], t; + metadata = [Catalyst.U0Map => u0map, Catalyst.ParameterMap => pmap]) @test Catalyst.has_u0_map(rs) @test Catalyst.has_parameter_map(rs) @test isequal(Catalyst.get_u0_map(rs), u0map) @@ -1244,10 +1192,8 @@ let # Preservation through flatten (hierarchical system). @named sub = ReactionSystem([Reaction(k2, [Y], [X])], t) - @named parent_rs = ReactionSystem( - [Reaction(k1, [X], [Y])], t; - systems = [sub], metadata = [Catalyst.U0Map => u0map, Catalyst.ParameterMap => pmap] - ) + @named parent_rs = ReactionSystem([Reaction(k1, [X], [Y])], t; + systems = [sub], metadata = [Catalyst.U0Map => u0map, Catalyst.ParameterMap => pmap]) flat = Catalyst.flatten(parent_rs) @test isequal(Catalyst.get_u0_map(flat), u0map) @test isequal(Catalyst.get_parameter_map(flat), pmap) @@ -1283,7 +1229,7 @@ end # there are several places in the code where the `reactionsystem_uptodate` function is called, here # the code might need adaptation to take the updated reaction system into account. let - @test_nowarn Catalyst.reactionsystem_uptodate_check() # Will fix this once most things are actually working. + @test_nowarn Catalyst.reactionsystem_uptodate_check() # Will fix this once most things are actually workin. end # Test that functions using the incidence matrix properly cache it @@ -1298,31 +1244,31 @@ let @test isempty(nps.incidencemat) == true img = incidencematgraph(rn) - @test size(nps.incidencemat) == (3, 3) + @test size(nps.incidencemat) == (3,3) Catalyst.reset!(nps) lcs = linkageclasses(rn) - @test size(nps.incidencemat) == (3, 3) + @test size(nps.incidencemat) == (3,3) Catalyst.reset!(nps) sns = subnetworks(rn) - @test size(nps.incidencemat) == (3, 3) + @test size(nps.incidencemat) == (3,3) Catalyst.reset!(nps) δ = deficiency(rn) - @test size(nps.incidencemat) == (3, 3) + @test size(nps.incidencemat) == (3,3) Catalyst.reset!(nps) δ_l = linkagedeficiencies(rn) - @test size(nps.incidencemat) == (3, 3) + @test size(nps.incidencemat) == (3,3) Catalyst.reset!(nps) rev = isreversible(rn) - @test size(nps.incidencemat) == (3, 3) + @test size(nps.incidencemat) == (3,3) Catalyst.reset!(nps) weakrev = isweaklyreversible(rn, sns) - @test size(nps.incidencemat) == (3, 3) + @test size(nps.incidencemat) == (3,3) end ########## tests related to hybrid systems ########## @@ -1342,12 +1288,10 @@ let @parameters λ k @variables V(t) @species A(t) B(t) C(t) - rxs = [ - Reaction(k * V, [], [A]), Reaction(λ * A, [B], nothing), - Reaction(k, [A, B], nothing), Reaction(λ, [C], [A]), - ] - eqs = [D(V) ~ λ * V * C] - cevents = [[V ~ 2.0] => [V ~ V / 2, A ~ A / 2]] + rxs = [Reaction(k*V, [], [A]), Reaction(λ*A, [B], nothing), + Reaction(k, [A, B], nothing), Reaction(λ, [C], [A])] + eqs = [D(V) ~ λ*V*C] + cevents = [[V ~ 2.0] => [V ~ V/2, A ~ A/2]] @named rs = ReactionSystem(vcat(rxs, eqs), t; continuous_events = cevents) rs = complete(rs) @test_throws ErrorException jump_model(rs) @@ -1360,12 +1304,10 @@ let @variables V(t) @species A(t) B(t) C(t) metadata = [:physical_scale => PhysicalScale.ODE] - rxs = [ - Reaction(k * V, [], [A]), Reaction(λ * A, [B], nothing; metadata), - Reaction(k, [A, B], nothing), Reaction(λ, [C], [A]), - ] - eqs = [D(V) ~ λ * V * C] - cevents = [[V ~ 2.0] => [V ~ V / 2, A ~ A / 2]] + rxs = [Reaction(k*V, [], [A]), Reaction(λ*A, [B], nothing; metadata), + Reaction(k, [A, B], nothing), Reaction(λ, [C], [A])] + eqs = [D(V) ~ λ*V*C] + cevents = [[V ~ 2.0] => [V ~ V/2, A ~ A/2]] @named rs = ReactionSystem(vcat(rxs, eqs), t; continuous_events = cevents) rs = complete(rs) sys = complete(hybrid_model(rs; default_scale = PhysicalScale.Jump)) @@ -1375,7 +1317,7 @@ let @test isempty(constantratejumps(sys)) @test length(variableratejumps(sys)) == 2 @test length(odeeqs(sys)) == 4 - odes = union(eqs, [D(A) ~ 0, D(B) ~ -λ * A * B, D(C) ~ 0]) + odes = union(eqs, [D(A) ~ 0, D(B) ~ -λ*A*B, D(C) ~ 0]) @test issetequal(odes, odeeqs(sys)) @test length(continuous_events(sys)) == 1 end @@ -1388,12 +1330,10 @@ let @species A(t) B(t) C(t) md1 = [:physical_scale => PhysicalScale.ODE] md2 = [:physical_scale => PhysicalScale.VariableRateJump] - rxs = [ - Reaction(k * V, [], [A]), Reaction(λ * A, [B], nothing; metadata = md1), - Reaction(k, [A, B], nothing), Reaction(λ, [C], [A]; metadata = md2), - ] - eqs = [D(V) ~ λ * V * C] - cevents = [[V ~ 2.0] => [V ~ V / 2, A ~ A / 2]] + rxs = [Reaction(k*V, [], [A]), Reaction(λ*A, [B], nothing; metadata = md1), + Reaction(k, [A, B], nothing), Reaction(λ, [C], [A]; metadata = md2)] + eqs = [D(V) ~ λ*V*C] + cevents = [[V ~ 2.0] => [V ~ V/2, A ~ A/2]] @named rs = ReactionSystem(vcat(rxs, eqs), t; continuous_events = cevents) rs = complete(rs) sys = complete(hybrid_model(rs; default_scale = PhysicalScale.Jump)) @@ -1403,7 +1343,7 @@ let @test isempty(constantratejumps(sys)) @test length(variableratejumps(sys)) == 3 @test length(odeeqs(sys)) == 4 - odes = union(eqs, [D(A) ~ 0, D(B) ~ -λ * A * B, D(C) ~ 0]) + odes = union(eqs, [D(A) ~ 0, D(B) ~ -λ*A*B, D(C) ~ 0]) @test issetequal(odes, odeeqs(sys)) @test length(continuous_events(sys)) == 1 end @@ -1428,7 +1368,7 @@ let # Define events continuous_events = [[X ~ 0] => [X ~ -X]] - discrete_events = (X == 1) => [V ~ V / 2] + discrete_events = (X == 1) => [V ~ V/2] # Define metadata metadata = [MiscSystemData => "Comprehensive test system"] @@ -1446,19 +1386,15 @@ let @named sub_rs = ReactionSystem([sub_rx], t) # Create the first reaction system - @named rs1 = ReactionSystem( - [rx1, rx2, rx3, rx4, eq], t; + @named rs1 = ReactionSystem([rx1, rx2, rx3, rx4, eq], t; continuous_events, discrete_events, - metadata, observed = obs, initial_conditions = defs, systems = [sub_rs] - ) + metadata, observed = obs, initial_conditions = defs, systems = [sub_rs]) rs1 = complete(rs1) # Create the second reaction system with the same components - rs2 = ReactionSystem( - [rx1, rx2, rx3, rx4, eq], t; + rs2 = ReactionSystem([rx1, rx2, rx3, rx4, eq], t; continuous_events, discrete_events, - metadata, observed = obs, initial_conditions = defs, systems = [sub_rs], name = :rs1 - ) + metadata, observed = obs, initial_conditions = defs, systems = [sub_rs], name = :rs1) rs2 = complete(rs2) # Check equivalence diff --git a/test/reactionsystem_core/symbolic_stoichiometry.jl b/test/reactionsystem_core/symbolic_stoichiometry.jl index 2e5795e843..beef92db5b 100644 --- a/test/reactionsystem_core/symbolic_stoichiometry.jl +++ b/test/reactionsystem_core/symbolic_stoichiometry.jl @@ -42,16 +42,16 @@ let rs1 = complete(ReactionSystem(rxs1, t; name = :rs)) rxs2 = [ - @reaction p, 0 --> $n1 * X - @reaction k, n2 * X --> n3 * Y + @reaction p, 0 --> $n1*X + @reaction k, n2*X --> n3*Y @reaction $d, Y --> 0 ] rs2 = complete(ReactionSystem(rxs2, t; name = :rs)) rs3 = @reaction_network rs begin @parameters d::Float64 n1::Int64 - p, 0 --> n1 * X - k, n2 * X --> n3 * Y + p, 0 --> n1*X + k, n2*X --> n3*Y d, Y --> 0 end @@ -78,8 +78,8 @@ begin g = k + α * C rs = @reaction_network rs begin @parameters k α::Int64 - t * k, 2 * (α^2) * A --> $g * B - 1.0, α * A + 2 * B --> k * C + α * D + t*k, 2*(α^2)*A --> $g*B + 1.0, α*A + 2*B --> k*C + α*D end end @@ -87,8 +87,8 @@ end let # With combinatoric ratelaws. function oderhs(u, p, t) - k, α = p - A, B, C, D = u + k,α = p + A,B,C,D = u n = 2 * α^2 rl = t * k / factorial(n) * A^n rl2 = A^α * B^2 / (2 * factorial(α)) @@ -104,8 +104,8 @@ let # Without combinatoric ratelaws. function oderhs_no_crl(u, p, t) - k, α = p - A, B, C, D = u + k,α = p + A,B,C,D = u n = 2 * α^2 rl = t * k * A^n rl2 = A^α * B^2 @@ -124,38 +124,34 @@ end let # With combinatoric ratelaws. function sdenoise(u, p, t) - k, α = p - A, B, C, D = u + k,α = p + A,B,C,D = u n = 2 * α^2 rl = sqrt(t * k / factorial(n) * A^n) rl2 = sqrt(A^α * B^2 / (2 * factorial(α))) - du = zeros(4, 2) - du = [ - -n * rl (-α * rl2); - (k + α * C) * rl (-2 * rl2); - 0.0 k * rl2; - 0.0 α * rl2 - ] + du = zeros(4,2) + du = [-n*rl (-α*rl2); + (k + α * C)*rl (-2*rl2); + 0.0 k*rl2; + 0.0 α*rl2] return du end @test g_eval(rs, u0_1, ps_1, τ) ≈ sdenoise(u0_2, ps_2, τ) # Without combinatoric ratelaws. function sdenoise_no_crl(u, p, t) - k, α = p - A, B, C, D = u + k,α = p + A,B,C,D = u n = 2 * α^2 rl = sqrt(t * k * A^n) rl2 = sqrt(A^α * B^2) - du = zeros(4, 2) - du = [ - -n * rl (-α * rl2); - (k + α * C) * rl (-2 * rl2); - 0.0 k * rl2; - 0.0 α * rl2 - ] + du = zeros(4,2) + du = [-n*rl (-α*rl2); + (k + α * C)*rl (-2*rl2); + 0.0 k*rl2; + 0.0 α*rl2] return du end @test g_eval(rs, u0_1, ps_1, τ; combinatoric_ratelaws = false) ≈ sdenoise_no_crl(u0_2, ps_2, τ) @@ -167,19 +163,19 @@ let function r1(u, p, t) k, α = p A = u[1] - return t * k * binomial(A, 2 * α^2) + t * k * binomial(A, 2 * α^2) end function affect1!(integrator) k, α = integrator.p C = integrator.u[3] integrator.u[1] -= 2 * α^2 integrator.u[2] += (k + α * C) - return nothing + nothing end function r2(u, p, t) k, α = p - A, B = u[1:2] - return binomial(Int64(A), Int64(α)) * B * (B - 1) / 2 + A,B = u[1:2] + binomial(Int64(A), Int64(α)) * B * (B - 1) / 2 end function affect2!(integrator) k, α = integrator.p @@ -187,7 +183,7 @@ let integrator.u[2] -= 2 integrator.u[3] += k integrator.u[4] += α - return nothing + nothing end jumps = [VariableRateJump(r1, affect1!), VariableRateJump(r2, affect2!)] @@ -222,7 +218,7 @@ let rs = @reaction_network begin @parameters k @variables V(t) - k, X --> V * Y + k, X --> V*Y end # The jump System should have explicit affects (no equations after mtkcompile) @@ -249,19 +245,19 @@ let # parameter vectors as the non-reference ones. rs_int = @reaction_network begin @parameters n::Int64 - (k1, k2), n * X1 <--> X2 + (k1, k2), n*X1 <--> X2 end rs_dec = @reaction_network begin @parameters n::Float64 - (k1, k2), n * X1 <--> X2 + (k1, k2), n*X1 <--> X2 end rs_ref_int = @reaction_network begin @parameters n::Int64 - (k1, k2), 3 * X1 <--> X2 + (k1, k2), 3*X1 <--> X2 end rs_ref_dec = @reaction_network begin @parameters n::Float64 - (k1, k2), 2.5 * X1 <--> X2 + (k1, k2), 2.5*X1 <--> X2 end # Set simulation settings. Initial conditions are design to start, more or less, at @@ -290,21 +286,21 @@ let sprob_int_ref = SDEProblem(rs_ref_int, u0_dec, tspan_stoch, ps_int) ssol_int = solve(sprob_int, ImplicitEM(); seed) ssol_int_ref = solve(sprob_int_ref, ImplicitEM(); seed) - @test mean(ssol_int[:X1]) ≈ mean(ssol_int_ref[:X1]) atol = 2 * 1.0e0 + @test mean(ssol_int[:X1]) ≈ mean(ssol_int_ref[:X1]) atol = 2*1e0 # Test SDE simulations with decimal coefficients. sprob_dec = SDEProblem(rs_dec, u0_dec, tspan_stoch, ps_dec; combinatoric_ratelaws = false) sprob_dec_ref = SDEProblem(rs_ref_dec, u0_dec, tspan_stoch, ps_dec; combinatoric_ratelaws = false) ssol_dec = solve(sprob_dec, ImplicitEM(); seed) ssol_dec_ref = solve(sprob_dec_ref, ImplicitEM(); seed) - @test mean(ssol_dec[:X1]) ≈ mean(ssol_dec_ref[:X1]) atol = 2 * 1.0e0 + @test mean(ssol_dec[:X1]) ≈ mean(ssol_dec_ref[:X1]) atol = 2*1e0 # Test Jump simulations with integer coefficients. jprob_int = JumpProblem(rs_int, u0_int, tspan_stoch, ps_int; rng, save_positions = (false, false)) jprob_int_ref = JumpProblem(rs_ref_int, u0_int, tspan_stoch, ps_int; rng, save_positions = (false, false)) jsol_int = solve(jprob_int, SSAStepper(); seed, saveat = 1.0) jsol_int_ref = solve(jprob_int_ref, SSAStepper(); seed, saveat = 1.0) - @test mean(jsol_int[:X1]) ≈ mean(jsol_int_ref[:X1]) atol = 1.0e-2 rtol = 1.0e-2 + @test mean(jsol_int[:X1]) ≈ mean(jsol_int_ref[:X1]) atol = 1e-2 rtol = 1e-2 end # Check that jump simulations (implemented with and without symbolic stoichiometries) yield simulations @@ -314,16 +310,16 @@ let # Creates the models. sir = @reaction_network begin @parameters n::Int64 k::Int64 - i, S + n * I --> k * I - r, n * I --> n * R + i, S + n*I --> k*I + r, n*I --> n*R end sir_ref = @reaction_network begin i, S + I --> 2I r, I --> R end - ps = [:i => 1.0e-4, :r => 1.0e-2, :n => 1.0, :k => 2.0] - ps_ref = [:i => 1.0e-4, :r => 1.0e-2] + ps = [:i => 1e-4, :r => 1e-2, :n => 1.0, :k => 2.0] + ps_ref = [:i => 1e-4, :r => 1e-2] tspan = (0.0, 250.0) # tspan[2] is selected so that it is in the middle of the outbreak peak. u0 = [:S => 999.0, :I => 1.0, :R => 0.0] @test issetequal(unknowns(sir), unknowns(sir_ref)) @@ -344,6 +340,6 @@ let sols_ref = solve(eprob_ref, SSAStepper(); trajectories = 10000) end_vals = [[sol[s][end] for sol in sols.u] for s in species(sir)] end_vals_ref = [[sol[s][end] for sol in sols_ref.u] for s in species(sir_ref)] - @test mean.(end_vals_ref) ≈ mean.(end_vals) atol = 1.0e-1 rtol = 1.0e-1 - @test var.(end_vals_ref) ≈ var.(end_vals) atol = 1.0e-1 rtol = 1.0e-1 + @test mean.(end_vals_ref) ≈ mean.(end_vals) atol=1e-1 rtol = 1e-1 + @test var.(end_vals_ref) ≈ var.(end_vals) atol=1e-1 rtol = 1e-1 end diff --git a/test/runtests.jl b/test/runtests.jl index 0060df0d5d..6a02c8a964 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,153 +9,77 @@ const GROUP = get(ENV, "GROUP", "All") function activate_extensions_env() Pkg.activate("extensions") Pkg.develop(PackageSpec(path = dirname(@__DIR__))) - return Pkg.instantiate() + Pkg.instantiate() end ### Run Tests ### @time begin if GROUP == "All" || GROUP == "Modeling" # Tests the `ReactionSystem` structure and its properties. - @time @safetestset "Reaction Structure" begin - include("reactionsystem_core/reaction.jl") - end - @time @safetestset "ReactionSystem Structure" begin - include("reactionsystem_core/reactionsystem.jl") - end - @time @safetestset "Higher Order Reactions" begin - include("reactionsystem_core/higher_order_reactions.jl") - end - @time @safetestset "Symbolic Stoichiometry" begin - include("reactionsystem_core/symbolic_stoichiometry.jl") - end - @time @safetestset "Parameter Type Designation" begin - include("reactionsystem_core/parameter_type_designation.jl") - end - @time @safetestset "Custom CRN Functions" begin - include("reactionsystem_core/custom_crn_functions.jl") - end - @time @safetestset "Coupled CRN/Equation Systems" begin - include("reactionsystem_core/coupled_equation_crn_systems.jl") - end - @time @safetestset "Events" begin - include("reactionsystem_core/events.jl") - end - @time @safetestset "Functional Parameters" begin - include("reactionsystem_core/functional_parameters.jl") - end + @time @safetestset "Reaction Structure" begin include("reactionsystem_core/reaction.jl") end + @time @safetestset "ReactionSystem Structure" begin include("reactionsystem_core/reactionsystem.jl") end + @time @safetestset "Higher Order Reactions" begin include("reactionsystem_core/higher_order_reactions.jl") end + @time @safetestset "Symbolic Stoichiometry" begin include("reactionsystem_core/symbolic_stoichiometry.jl") end + @time @safetestset "Parameter Type Designation" begin include("reactionsystem_core/parameter_type_designation.jl") end + @time @safetestset "Custom CRN Functions" begin include("reactionsystem_core/custom_crn_functions.jl") end + @time @safetestset "Coupled CRN/Equation Systems" begin include("reactionsystem_core/coupled_equation_crn_systems.jl") end + @time @safetestset "Events" begin include("reactionsystem_core/events.jl") end + @time @safetestset "Functional Parameters" begin include("reactionsystem_core/functional_parameters.jl") end # Tests model creation via the @reaction_network DSL. - @time @safetestset "DSL Basic Model Construction" begin - include("dsl/dsl_basic_model_construction.jl") - end - @time @safetestset "DSL Advanced Model Construction" begin - include("dsl/dsl_advanced_model_construction.jl") - end - @time @safetestset "DSL Options" begin - include("dsl/dsl_options.jl") - end + @time @safetestset "DSL Basic Model Construction" begin include("dsl/dsl_basic_model_construction.jl") end + @time @safetestset "DSL Advanced Model Construction" begin include("dsl/dsl_advanced_model_construction.jl") end + @time @safetestset "DSL Options" begin include("dsl/dsl_options.jl") end # Tests compositional and hierarchical modelling. - @time @safetestset "ReactionSystem Components Based Creation" begin - include("compositional_modelling/component_based_model_creation.jl") - end # hierarchical modelling broken due to https://github.com/SciML/ModelingToolkit.jl/pull/4101 - @time @safetestset "Brownians, Jumps, and Poissonians Composition" begin - include("compositional_modelling/brownians_and_jumps_composition.jl") - end + @time @safetestset "ReactionSystem Components Based Creation" begin include("compositional_modelling/component_based_model_creation.jl") end # hierarchical modelling broken due to https://github.com/SciML/ModelingToolkit.jl/pull/4101 + @time @safetestset "Brownians, Jumps, and Poissonians Composition" begin include("compositional_modelling/brownians_and_jumps_composition.jl") end # Tests various miscellaneous features. - @time @safetestset "API" begin - include("miscellaneous_tests/api.jl") - end - @time @safetestset "Units" begin - include("miscellaneous_tests/units.jl") - end # `_validate` currently no longer available, awaiting advice. - @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 "API" begin include("miscellaneous_tests/api.jl") end + @time @safetestset "Units" begin include("miscellaneous_tests/units.jl") end # `_validate` currently no longer avaiable, awaiting advice. + @time @safetestset "Compound Species" begin include("miscellaneous_tests/compound_macro.jl") end + @time @safetestset "Reaction Balancing" begin include("miscellaneous_tests/reaction_balancing.jl") end # Tests reaction network analysis features. - @time @safetestset "Conservation Laws" begin - include("network_analysis/conservation_laws.jl") - end # Multiple issues. https://github.com/SciML/ModelingToolkit.jl/issues/4102 required to start debugging. - @time @safetestset "Network Properties" begin - include("network_analysis/network_properties.jl") - end - @time @safetestset "CRN Theory" begin - include("network_analysis/crn_theory.jl") - end + @time @safetestset "Conservation Laws" begin include("network_analysis/conservation_laws.jl") end # Multiple issues. https://github.com/SciML/ModelingToolkit.jl/issues/4102 required to start debugging. + @time @safetestset "Network Properties" begin include("network_analysis/network_properties.jl") end + @time @safetestset "CRN Theory" begin include("network_analysis/crn_theory.jl") end end if GROUP == "All" || GROUP == "Simulation" # Tests ODE, SDE, jump simulations, nonlinear solving, and steady state simulations. - @time @safetestset "ODE System Simulations" begin - include("simulation_and_solving/simulate_ODEs.jl") - end - @time @safetestset "Automatic Jacobian Construction" begin - include("simulation_and_solving/jacobian_construction.jl") - end - @time @safetestset "SDE System Simulations" begin - include("simulation_and_solving/simulate_SDEs.jl") - end - @time @safetestset "Jump System Simulations" begin - include("simulation_and_solving/simulate_jumps.jl") - end - @time @safetestset "Nonlinear and SteadyState System Solving" begin - include("simulation_and_solving/solve_nonlinear.jl") - end + @time @safetestset "ODE System Simulations" begin include("simulation_and_solving/simulate_ODEs.jl") end + @time @safetestset "Automatic Jacobian Construction" begin include("simulation_and_solving/jacobian_construction.jl") end + @time @safetestset "SDE System Simulations" begin include("simulation_and_solving/simulate_SDEs.jl") end + @time @safetestset "Jump System Simulations" begin include("simulation_and_solving/simulate_jumps.jl") end + @time @safetestset "Nonlinear and SteadyState System Solving" begin include("simulation_and_solving/solve_nonlinear.jl") end # Tests upstream SciML and DiffEq stuff. - @time @safetestset "MTK Structure Indexing" begin - include("upstream/mtk_structure_indexing.jl") - end - @time @safetestset "MTK Problem Inputs" begin - include("upstream/mtk_problem_inputs.jl") - end # Required to fix lots of these: https://github.com/SciML/ModelingToolkit.jl/issues/4098 + @time @safetestset "MTK Structure Indexing" begin include("upstream/mtk_structure_indexing.jl") end + @time @safetestset "MTK Problem Inputs" begin include("upstream/mtk_problem_inputs.jl") end # Required to fix lots of these: https://github.com/SciML/ModelingToolkit.jl/issues/4098 end if GROUP == "All" || GROUP == "Hybrid" - @time @safetestset "ReactionSystem Hybrid Solvers" begin - include("simulation_and_solving/hybrid_models.jl") - end + @time @safetestset "ReactionSystem Hybrid Solvers" begin include("simulation_and_solving/hybrid_models.jl") end end if GROUP == "All" || GROUP == "Misc" - @time @safetestset "ReactionSystem Serialisation" begin - include("miscellaneous_tests/reactionsystem_serialisation.jl") - end - @time @safetestset "Explicit Imports" begin - include("miscellaneous_tests/explicit_imports.jl") - end + @time @safetestset "ReactionSystem Serialisation" begin include("miscellaneous_tests/reactionsystem_serialisation.jl") end + @time @safetestset "Explicit Imports" begin include("miscellaneous_tests/explicit_imports.jl") end # BROKEN #@time @safetestset "Latexify" begin include("visualisation/latexify.jl") end # https://github.com/SciML/Catalyst.jl/issues/1352 end if GROUP == "All" || GROUP == "Spatial" # Tests spatial modelling and simulations. - @time @safetestset "PDE Systems Simulations" begin - include("spatial_modelling/simulate_PDEs.jl") - end - @time @safetestset "Spatial Reactions" begin - include("spatial_modelling/spatial_reactions.jl") - end - @time @safetestset "Discrete Space Reaction Systems" begin - include("spatial_modelling/dspace_reaction_systems.jl") - end - @time @safetestset "Spatial Discrete Space Variants" begin - include("spatial_modelling/dspace_reaction_systems_space_types.jl") - end - @time @safetestset "ODE Discrete Space Systems Simulations" begin - include("spatial_modelling/dspace_reaction_systems_ODEs.jl") - end - @time @safetestset "Jump Discrete Space Systems Simulations" begin - include("spatial_modelling/dspace_reaction_systems_jumps.jl") - end - @time @safetestset "Discrete Space Simulation Structure Interfacing" begin - include("spatial_modelling/dspace_simulation_struct_interfacing.jl") - end + @time @safetestset "PDE Systems Simulations" begin include("spatial_modelling/simulate_PDEs.jl") end + @time @safetestset "Spatial Reactions" begin include("spatial_modelling/spatial_reactions.jl") end + @time @safetestset "Discrete Space Reaction Systems" begin include("spatial_modelling/dspace_reaction_systems.jl") end + @time @safetestset "Spatial Discrete Space Variants" begin include("spatial_modelling/dspace_reaction_systems_space_types.jl") end + @time @safetestset "ODE Discrete Space Systems Simulations" begin include("spatial_modelling/dspace_reaction_systems_ODEs.jl") end + @time @safetestset "Jump Discrete Space Systems Simulations" begin include("spatial_modelling/dspace_reaction_systems_jumps.jl") end + @time @safetestset "Discrete Space Simulation Structure Interfacing" begin include("spatial_modelling/dspace_simulation_struct_interfacing.jl") end end # Tests extensions. diff --git a/test/runtests_extensions.jl b/test/runtests_extensions.jl index 087f7fa7f6..78d0362e1a 100644 --- a/test/runtests_extensions.jl +++ b/test/runtests_extensions.jl @@ -8,24 +8,12 @@ using SafeTestsets, Test # runs code in a separate module where outer constants aren't visible. @time begin - @time @safetestset "Graph visualization" begin - include(joinpath(@__DIR__, "extensions", "graphmakie.jl")) - end - @time @safetestset "BifurcationKit Extension" begin - include(joinpath(@__DIR__, "extensions", "bifurcation_kit.jl")) - end - @time @safetestset "HomotopyContinuation Extension" begin - include(joinpath(@__DIR__, "extensions", "homotopy_continuation.jl")) - end - @time @safetestset "Structural Identifiability Extension" begin - include(joinpath(@__DIR__, "extensions", "structural_identifiability.jl")) - end - @time @safetestset "Steady State Stability Computations" begin - include(joinpath(@__DIR__, "extensions", "stability_computation.jl")) - end + @time @safetestset "Graph visualization" begin include(joinpath(@__DIR__, "extensions", "graphmakie.jl")) end + @time @safetestset "BifurcationKit Extension" begin include(joinpath(@__DIR__, "extensions", "bifurcation_kit.jl")) end + @time @safetestset "HomotopyContinuation Extension" begin include(joinpath(@__DIR__, "extensions", "homotopy_continuation.jl")) end + @time @safetestset "Structural Identifiability Extension" begin include(joinpath(@__DIR__, "extensions", "structural_identifiability.jl")) end + @time @safetestset "Steady State Stability Computations" begin include(joinpath(@__DIR__, "extensions", "stability_computation.jl")) end # Test spatial plotting, using CairoMakie and GraphMakie - @time @safetestset "Discrete Space Simulation Plotting" begin - include(joinpath(@__DIR__, "extensions", "dspace_simulation_plotting.jl")) - end + @time @safetestset "Discrete Space Simulation Plotting" begin include(joinpath(@__DIR__, "extensions", "dspace_simulation_plotting.jl")) end end diff --git a/test/simulation_and_solving/hybrid_models.jl b/test/simulation_and_solving/hybrid_models.jl index bd863cdff7..9f598ad3c2 100644 --- a/test/simulation_and_solving/hybrid_models.jl +++ b/test/simulation_and_solving/hybrid_models.jl @@ -12,9 +12,9 @@ seed = rand(rng, 1:100) let # Time-dependent rate -> VariableRateJump (needs ODE solver) rn = @reaction_network begin - k * (1 + sin(t)), 0 --> A + k*(1 + sin(t)), 0 --> A end - jprob = JumpProblem(rn, [:A => 0], (0.0, 10.0), [:k => 0.5]; rng) + jprob = JumpProblem(rn, [:A => 0], (0.0, 10.0), [:k => .5]; rng) sol = solve(jprob, Tsit5()) @test sol(10.0; idxs = :A) > 0 @@ -22,21 +22,21 @@ let rn = @reaction_network begin k, 0 --> A end - jprob = JumpProblem(rn, [:A => 0], (0.0, 10.0), [:k => 0.5]; rng) + jprob = JumpProblem(rn, [:A => 0], (0.0, 10.0), [:k => .5]; rng) sol = solve(jprob, SSAStepper()) @test sol(10.0; idxs = :A) > 0 # Hybrid model with ODE equations and events - requires HybridProblem rn = @reaction_network begin @parameters λ - k * V, 0 --> A - @equations D(V) ~ λ * V + k*V, 0 --> A + @equations D(V) ~ λ*V @continuous_events begin - [V ~ 2.0] => [V => V / 2, A => A / 2] + [V ~ 2.0] => [V => V/2, A => A/2] end end # JumpProblem no longer supports ODE equations - use HybridProblem instead - jprob = HybridProblem(rn, [:A => 0, :V => 1.0], (0.0, 10.0), [:k => 1.0, :λ => 0.4]; rng) + jprob = HybridProblem(rn, [:A => 0, :V => 1.0], (0.0, 10.0), [:k => 1.0, :λ => .4]; rng) sol = solve(jprob, Tsit5()) end @@ -75,7 +75,7 @@ let function Xf(t, p) local α, β, X₀, Y₀ = p return (α / β) + (α^2 / β^2) + α * (Y₀ - α / β) * t * exp(-β * t) + - (X₀ - α / β - α^2 / β^2) * exp(-β * t) + (X₀ - α / β - α^2 / β^2) * exp(-β * t) end Xact = [Xf(t, p) for t in times] Yact = [Yf(t, p) for t in times] @@ -122,16 +122,16 @@ let 1, X → ∅ end rn_jump = @reaction_network begin - (p, d), 0 <--> V - (k1, k2), V + W <--> VW + (p,d), 0 <--> V + (k1,k2), V + W <--> VW end rn_hybrid = @reaction_network begin A, ∅ → X, [physical_scale = PhysicalScale.ODE] 1, 2X + Y → 3X, [physical_scale = PhysicalScale.ODE] B, X → Y, [physical_scale = PhysicalScale.ODE] 1, X → ∅, [physical_scale = PhysicalScale.ODE] - (p, d), 0 <--> V - (k1, k2), V + W <--> VW + (p,d), 0 <--> V + (k1,k2), V + W <--> VW end # Sets simulation conditions and creates problems corresponding to the different models. @@ -147,13 +147,13 @@ let hybrid_prob = HybridProblem(rn_hybrid, u0_hybrid, tspan, ps_hybrid; save_positions = (false, false), rng) # Performs simulations. Checks that ODE parts are identical. Check that jump parts have similar statistics. - ode_sol = solve(ode_prob, Tsit5(); saveat = 1.0, abstol = 1.0e-10, reltol = 1.0e-10) + ode_sol = solve(ode_prob, Tsit5(); saveat = 1.0, abstol = 1e-10, reltol = 1e-10) jump_sol = solve(jump_prob, SSAStepper(); saveat = 1.0) - hybrid_sol = solve(hybrid_prob, Tsit5(); saveat = 1.0, abstol = 1.0e-10, reltol = 1.0e-10) - @test ode_sol[:Y] ≈ hybrid_sol[:Y] atol = 1.0e-4 rtol = 1.0e-4 - @test mean(jump_sol[:V]) ≈ mean(hybrid_sol[:V]) atol = 1.0e-1 rtol = 1.0e-1 - @test mean(jump_sol[:W]) ≈ mean(hybrid_sol[:W]) atol = 1.0e-1 rtol = 1.0e-1 - @test mean(jump_sol[:VW]) ≈ mean(hybrid_sol[:VW]) atol = 1.0e-1 rtol = 1.0e-1 + hybrid_sol = solve(hybrid_prob, Tsit5(); saveat = 1.0, abstol = 1e-10, reltol = 1e-10) + @test ode_sol[:Y] ≈ hybrid_sol[:Y] atol = 1e-4 rtol = 1e-4 + @test mean(jump_sol[:V]) ≈ mean(hybrid_sol[:V]) atol = 1e-1 rtol = 1e-1 + @test mean(jump_sol[:W]) ≈ mean(hybrid_sol[:W]) atol = 1e-1 rtol = 1e-1 + @test mean(jump_sol[:VW]) ≈ mean(hybrid_sol[:VW]) atol = 1e-1 rtol = 1e-1 end ### Other Tests ### @@ -166,9 +166,9 @@ let @species X(t) = 1.0 @parameters p = 1.0 p, 0 --> X, [physical_scale = PhysicalScale.ODE] - (kB, kD), 2X <--> X2 + (kB,kD), 2X <--> X2 k, X2 --> Y2, [physical_scale = PhysicalScale.ODE] - (kB, kD), 2Y <--> Y2 + (kB,kD), 2Y <--> Y2 d, Y --> 0, [physical_scale = PhysicalScale.ODE] end u0 = [:X2 => 0.0, :Y => 0.0, :Y2 => 0.0] @@ -286,9 +286,9 @@ let @discrete_events [1.0] => [Z1 => Z1 + 1.0] @continuous_events [Y ~ 1.0] => [Y => 5.0] @equations Δ(V) ~ Z1 + X^2 - V - (p, d), 0 <--> X + (p,d), 0 <--> X d, Y --> 0, [physical_scale = PhysicalScale.ODE] - (k * X, k * Y), Z1 <--> Z2, ([physical_scale = PhysicalScale.ODE], [physical_scale = PhysicalScale.Jump]) + (k*X, k*Y), Z1 <--> Z2, ([physical_scale = PhysicalScale.ODE], [physical_scale = PhysicalScale.Jump]) end # Simulates the model. @@ -305,7 +305,7 @@ let @test sol[:V][1] == 1.5 @test sol[:Ztot] ≈ sol[rn.Z1 + rn.Z2] @test minimum(sol[:Y]) ≈ 1.0 - @test maximum(sol[:Y]) ≈ 5.0 atol = 1.0e-1 rtol = 1.0e-1 + @test maximum(sol[:Y]) ≈ 5.0 atol = 1e-1 rtol = 1e-1 @test all(isequal([rn.τ], Symbolics.arguments(Symbolics.value(u))) for u in unknowns(rn)) @test sol(1.0 - eps(); idxs = :Z1) + 1 ≈ sol(1.0 + eps(); idxs = :Z1) end @@ -624,7 +624,7 @@ let sys_hybrid_sde = hybrid_model(rn; default_scale = PhysicalScale.SDE) @test length(equations(sys_sde)) == length(equations(sys_hybrid_sde)) @test length(ModelingToolkitBase.get_brownians(sys_sde)) == - length(ModelingToolkitBase.get_brownians(sys_hybrid_sde)) + length(ModelingToolkitBase.get_brownians(sys_hybrid_sde)) # sde_model with use_legacy_noise=true (default) should use noise_eqs matrix, not brownians. sys_sde_legacy = sde_model(rn; use_legacy_noise = true) @@ -635,7 +635,7 @@ let sys_jump = jump_model(rn) sys_hybrid_jump = hybrid_model(rn; default_scale = PhysicalScale.Jump) @test length(ModelingToolkitBase.get_jumps(sys_jump)) == - length(ModelingToolkitBase.get_jumps(sys_hybrid_jump)) + length(ModelingToolkitBase.get_jumps(sys_hybrid_jump)) end # Tests that the Brownian noise matrix extracted by mtkcompile matches assemble_diffusion. @@ -653,10 +653,8 @@ let # Build the old-style noise matrix via assemble_diffusion for comparison. flatrs = Catalyst.flatten(rn) ists, ispcs = Catalyst.get_indep_sts(flatrs, false) - noise_matrix_old = Catalyst.assemble_diffusion( - flatrs, ists, ispcs; - combinatoric_ratelaws = true, remove_conserved = false, expand_catalyst_funs = true - ) + noise_matrix_old = Catalyst.assemble_diffusion(flatrs, ists, ispcs; + combinatoric_ratelaws = true, remove_conserved = false, expand_catalyst_funs = true) # Both should be 2×2 matrices. Verify they are symbolically equivalent. @test size(noise_matrix_brownian) == size(noise_matrix_old) @@ -696,10 +694,8 @@ let k2, P --> S end - @test_throws ArgumentError hybrid_model( - rn; - default_scale = PhysicalScale.Jump, remove_conserved = true - ) + @test_throws ArgumentError hybrid_model(rn; + default_scale = PhysicalScale.Jump, remove_conserved = true) end # Tests that events pass through to the hybrid system. @@ -810,26 +806,20 @@ let end # Pure ODE → ODEProblem - prob_ode = HybridProblem( - rn, [:S => 100.0, :P => 0.0], (0.0, 1.0), [:k1 => 1.0, :k2 => 0.5]; - default_scale = PhysicalScale.ODE - ) + prob_ode = HybridProblem(rn, [:S => 100.0, :P => 0.0], (0.0, 1.0), [:k1 => 1.0, :k2 => 0.5]; + default_scale = PhysicalScale.ODE) @test prob_ode isa ODEProblem sol = solve(prob_ode, Tsit5()) @test SciMLBase.successful_retcode(sol) # Pure SDE → SDEProblem - prob_sde = HybridProblem( - rn, [:S => 100.0, :P => 0.0], (0.0, 1.0), [:k1 => 1.0, :k2 => 0.5]; - default_scale = PhysicalScale.SDE - ) + prob_sde = HybridProblem(rn, [:S => 100.0, :P => 0.0], (0.0, 1.0), [:k1 => 1.0, :k2 => 0.5]; + default_scale = PhysicalScale.SDE) @test prob_sde isa SDEProblem # Pure Jump → JumpProblem - prob_jump = HybridProblem( - rn, [:S => 100, :P => 0], (0.0, 1.0), [:k1 => 1.0, :k2 => 0.5]; - default_scale = PhysicalScale.Jump - ) + prob_jump = HybridProblem(rn, [:S => 100, :P => 0], (0.0, 1.0), [:k1 => 1.0, :k2 => 0.5]; + default_scale = PhysicalScale.Jump) @test prob_jump isa JumpProcesses.JumpProblem sol = solve(prob_jump, SSAStepper()) @test SciMLBase.successful_retcode(sol) @@ -883,10 +873,8 @@ let k3, S --> 0, [physical_scale = PhysicalScale.Jump] end - prob = HybridProblem( - rn, [:S => 100.0, :P => 0.0], (0.0, 1.0), - [:k1 => 1.0, :k2 => 0.5, :k3 => 0.1] - ) + prob = HybridProblem(rn, [:S => 100.0, :P => 0.0], (0.0, 1.0), + [:k1 => 1.0, :k2 => 0.5, :k3 => 0.1]) @test prob isa JumpProcesses.JumpProblem @test prob.prob isa SciMLBase.SDEProblem @@ -913,10 +901,8 @@ let k1, 0 --> X, [physical_scale = PhysicalScale.SDE] k2, X --> 0, [physical_scale = PhysicalScale.Jump] end - cat_prob = HybridProblem( - rn, [:X => X0], tspan, [:k1 => k1, :k2 => k2]; - save_positions = (false, false), rng - ) + cat_prob = HybridProblem(rn, [:X => X0], tspan, [:k1 => k1, :k2 => k2]; + save_positions = (false, false), rng) # --- Manually coded version using JumpProcesses + StochasticDiffEq --- f_manual!(du, u, p, t) = (du[1] = p[1]) # drift: k1 @@ -968,10 +954,8 @@ let k2, P --> 0, [physical_scale = PhysicalScale.Jump] end - prob = HybridProblem( - rn, [:S => 1.0, :P => P0], tspan, [:k1 => k1, :k2 => k2]; - save_positions = (false, false), rng - ) + prob = HybridProblem(rn, [:S => 1.0, :P => P0], tspan, [:k1 => k1, :k2 => k2]; + save_positions = (false, false), rng) # Run simulations and collect values at all time points Pv = zeros(length(times)) @@ -1018,11 +1002,9 @@ let k4, A --> 0, [physical_scale = PhysicalScale.Jump] k5, D --> 0, [physical_scale = PhysicalScale.Jump] end - cat_prob = HybridProblem( - rn, [:A => A0, :B => B0, :C => C0, :D => D0], tspan, + cat_prob = HybridProblem(rn, [:A => A0, :B => B0, :C => C0, :D => D0], tspan, [:k1 => k1, :k2 => k2, :k3 => k3, :k4 => k4, :k5 => k5]; - save_positions = (false, false), rng - ) + save_positions = (false, false), rng) # --- Manually coded version --- # Species order: [A, B, C, D] @@ -1031,13 +1013,13 @@ let du[1] = p[1] # k1 (A production drift) du[2] = 0.0 du[3] = 0.0 - return du[4] = 0.0 + du[4] = 0.0 end function g_manual!(du, u, p, t) du[1] = sqrt(p[1]) # sqrt(k1) (A production diffusion) du[2] = 0.0 du[3] = 0.0 - return du[4] = 0.0 + du[4] = 0.0 end # Jump 1: A + B -> C at rate k2*A*B @@ -1045,7 +1027,7 @@ let function affect1!(integrator) integrator.u[1] -= 1 # A -= 1 integrator.u[2] -= 1 # B -= 1 - return integrator.u[3] += 1 # C += 1 + integrator.u[3] += 1 # C += 1 end jump1 = VariableRateJump(rate1, affect1!; save_positions = (false, false)) @@ -1054,7 +1036,7 @@ let function affect2!(integrator) integrator.u[3] -= 1 # C -= 1 integrator.u[2] += 1 # B += 1 - return integrator.u[4] += 1 # D += 1 + integrator.u[4] += 1 # D += 1 end jump2 = VariableRateJump(rate2, affect2!; save_positions = (false, false)) @@ -1068,14 +1050,10 @@ let affect4!(integrator) = (integrator.u[4] -= 1) jump4 = VariableRateJump(rate4, affect4!; save_positions = (false, false)) - sde_prob = SciMLBase.SDEProblem( - f_manual!, g_manual!, [A0, B0, C0, D0], tspan, - [k1, k2, k3, k4, k5] - ) - manual_prob = JumpProblem( - sde_prob, Direct(), jump1, jump2, jump3, jump4; rng, - save_positions = (false, false) - ) + sde_prob = SciMLBase.SDEProblem(f_manual!, g_manual!, [A0, B0, C0, D0], tspan, + [k1, k2, k3, k4, k5]) + manual_prob = JumpProblem(sde_prob, Direct(), jump1, jump2, jump3, jump4; rng, + save_positions = (false, false)) # Run simulations and collect values cat_A = zeros(N, length(times)) @@ -1248,10 +1226,8 @@ let n_trials = 500 # Create problem once; the RNG state advances across solves giving different trajectories. - prob = HybridProblem( - rn, [:X => 0.0], (0.0, T), [:λ => λ_val]; - save_positions = (false, false), rng = StableRNG(12345) - ) + prob = HybridProblem(rn, [:X => 0.0], (0.0, T), [:λ => λ_val]; + save_positions = (false, false), rng = StableRNG(12345)) # Pure poissonian system: D(X) ~ dN becomes D(X) ~ 0 after extraction, # mtkcompile reduces to DiscreteProblem → use SSAStepper. final_vals = [solve(prob, SSAStepper(); saveat = T)[X, end] for _ in 1:n_trials] @@ -1284,11 +1260,9 @@ let n_trials = 500 # Create problem once; the RNG state advances across solves. - prob = HybridProblem( - rn, [:S => 10.0, :X => 0.0], (0.0, T), + prob = HybridProblem(rn, [:S => 10.0, :X => 0.0], (0.0, T), [:λ => λ_val, :k => 1.0, :d => 0.1]; - save_positions = (false, false), rng = StableRNG(12345) - ) + save_positions = (false, false), rng = StableRNG(12345)) @test prob isa JumpProblem # Has ODE equations (reactions + D(X) ~ 0 after extraction), so needs ODE solver. @@ -1323,11 +1297,9 @@ let n_trials = 500 # Create problem once; the RNG state advances across solves. - prob = HybridProblem( - rn, [:X => 0.0], (0.0, T), + prob = HybridProblem(rn, [:X => 0.0], (0.0, T), [:λ => λ_val, :σ => σ_val]; - save_positions = (false, false), rng = StableRNG(12345) - ) + save_positions = (false, false), rng = StableRNG(12345)) @test prob isa JumpProblem final_vals = [solve(prob, SRIW1(); saveat = T)[X, end] for _ in 1:n_trials] @@ -1360,10 +1332,8 @@ let # State-dependent rate → VariableRateJump → needs ODE solver. # Create problem once; the RNG state advances across solves. - prob = HybridProblem( - rn, [:X => X0], (0.0, T), [:k => k_val]; - save_positions = (false, false), rng = StableRNG(12345) - ) + prob = HybridProblem(rn, [:X => X0], (0.0, T), [:k => k_val]; + save_positions = (false, false), rng = StableRNG(12345)) @test prob isa JumpProblem final_vals = [solve(prob, Tsit5(); saveat = T)[X, end] for _ in 1:n_trials] @@ -1393,10 +1363,8 @@ let n_trials = 500 # Create problem once; the RNG state advances across solves. - prob = HybridProblem( - rn, [:X => 0.0], (0.0, T), [:λ => λ_val]; - save_positions = (false, false), rng = StableRNG(12345) - ) + prob = HybridProblem(rn, [:X => 0.0], (0.0, T), [:λ => λ_val]; + save_positions = (false, false), rng = StableRNG(12345)) @test prob isa JumpProblem # Pure poissonian: D(X) ~ dN becomes D(X) ~ 0 → DiscreteProblem → SSAStepper. diff --git a/test/simulation_and_solving/jacobian_construction.jl b/test/simulation_and_solving/jacobian_construction.jl index 072fb4f129..dab99eeb2d 100644 --- a/test/simulation_and_solving/jacobian_construction.jl +++ b/test/simulation_and_solving/jacobian_construction.jl @@ -34,15 +34,13 @@ let end @unpack X, Y, XY, p1, p2, p3 = jacobian_network_2 - for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2], repeat in 1:10 + for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2], repeat in 1:10 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 - ] + 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 @@ -60,15 +58,13 @@ let end @unpack A, B, C, k1, k2, k3, k4, k5, k6, k7, k8 = jacobian_network_3 - for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2], repeat in 1:10 + for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2], repeat in 1:10 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 - ] + 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 @@ -77,13 +73,13 @@ end let # Creates model (vaguely messy model without conserved quantities). rn = @reaction_network begin - (p, d), 0 <--> (X, Y) - (k1, k2), X + Y <--> XY - (k1, k2), X + 2Y <--> XY2 - (k1, k2), XY + XY2 <--> X2Y3 - d, (XY2, X2Y3) --> 0 - mm(X2Y3, v, K), 0 --> Z - (k3, k4), 3Z <--> Z3 + (p,d), 0 <--> (X,Y) + (k1,k2), X + Y <--> XY + (k1,k2), X + 2Y <--> XY2 + (k1,k2), XY + XY2 <--> X2Y3 + d, (XY2,X2Y3) --> 0 + mm(X2Y3,v,K), 0 --> Z + (k3,k4), 3Z <--> Z3 1.0, X3 --> 0 end @@ -107,8 +103,8 @@ let return J end @test eval_jac(oprob_jac, false) == eval_jac(sprob_jac, false) == eval_jac(nlprob_jac, false) - @test eval_jac(oprob_sjac, true) ≈ eval_jac(sprob_sjac, true) atol = 1.0e-14 rtol = 1.0e-14 - @test eval_jac(oprob_sjac, true) ≈ eval_jac(nlprob_sjac, true) atol = 1.0e-14 rtol = 1.0e-14 + @test eval_jac(oprob_sjac, true) ≈ eval_jac(sprob_sjac, true) atol = 1e-14 rtol = 1e-14 + @test eval_jac(oprob_sjac, true) ≈ eval_jac(nlprob_sjac, true) atol = 1e-14 rtol = 1e-14 end end @@ -119,16 +115,16 @@ let # Creates model (vaguely messy model without conserved quantities). # Model includes a time-dependent reaction. rn = @reaction_network begin - (p, d), 0 <--> (X, Y, Z) + (p,d), 0 <--> (X,Y,Z) k1, X + Y --> XY k2, X + 2Z --> XZ2 - k3, Y3 + X2 --> Y3Z2 + k3, Y3 +X2 --> Y3Z2 k4, X + Y + Z --> XYZ k5, XZ2 + Y3Z2 --> XY3Z4 k6, XYZ + XYZ --> X2Y2Z2 d, (XY3Z4, X2Y2Z2) --> 0 X + Y, V --> 0 - k7 / (1 + t), 2V --> V2 + k7/(1 + t), 2V --> V2 Z, V2 --> 0 end @@ -136,15 +132,15 @@ let for factor in [0.1, 1.0, 10.0] # Creates randomised species and parameter values. Generates jacobians (dense/sparse). u0 = rnd_u0(rn, rng; factor) - t_val = factor * rand() + t_val = factor*rand() ps = rnd_ps(rn, rng; factor) jac = jac_eval(rn, u0, ps, t_val; sparse = false) jac_sparse = jac_eval(rn, u0, ps, t_val; sparse = true) # Check correctness (both by converting to sparse jac to dense, and through multiplication with other matrix). # Approx is due to https://github.com/SciML/ModelingToolkit.jl/issues/3554. - @test Matrix(jac_sparse) ≈ jac atol = 1.0e-14 rtol = 1.0e-14 - mat = factor * rand(rng, length(u0), length(u0)) + @test Matrix(jac_sparse) ≈ jac atol = 1e-14 rtol = 1e-14 + mat = factor*rand(rng, length(u0), length(u0)) @test jac_sparse * mat ≈ jac * mat end end @@ -153,13 +149,13 @@ end let # Creates model (vaguely messy model without conserved quantities). rn = @reaction_network begin - (v0 + mm(X, v, K), d), 0 <--> X + 2Y - (k1, k2), X + Y <--> XY - (k1, k2), X + Y2 <--> XY2 - (k3, k4), XY + XY2 <--> X2Y3 - 1.0, (XY, XY2, X2Y3) --> 0 - mm(X2Y3, v, K), 0 --> Z - (k3 * X, k4 * Y), 3Z <--> Z3 + (v0 + mm(X,v,K),d), 0 <--> X + 2Y + (k1,k2), X + Y <--> XY + (k1,k2), X + Y2 <--> XY2 + (k3,k4), XY + XY2 <--> X2Y3 + 1.0, (XY,XY2,X2Y3) --> 0 + mm(X2Y3,v,K), 0 --> Z + (k3*X,k4*Y), 3Z <--> Z3 d, Z --> 0 end @@ -172,9 +168,9 @@ let oprob_js = ODEProblem(rn, u0, 1.0, ps; jac = true, sparse = true) # Simulates system with implicit solver. Checks that all solutions are identical. - sol = solve(oprob, Rosenbrock23(), saveat = 0.1, abstol = 1.0e-8, reltol = 1.0e-8) - sol_j = solve(oprob_j, Rosenbrock23(), saveat = 0.1, abstol = 1.0e-8, reltol = 1.0e-8) - sol_s = solve(oprob_s, Rosenbrock23(), saveat = 0.1, abstol = 1.0e-8, reltol = 1.0e-8) - sol_js = solve(oprob_js, Rosenbrock23(), saveat = 0.1, abstol = 1.0e-8, reltol = 1.0e-8) + sol = solve(oprob, Rosenbrock23(), saveat = 0.1, abstol = 1e-8, reltol = 1e-8) + sol_j = solve(oprob_j, Rosenbrock23(), saveat = 0.1, abstol = 1e-8, reltol = 1e-8) + sol_s = solve(oprob_s, Rosenbrock23(), saveat = 0.1, abstol = 1e-8, reltol = 1e-8) + sol_js = solve(oprob_js, Rosenbrock23(), saveat = 0.1, abstol = 1e-8, reltol = 1e-8) @test sol ≈ sol_j ≈ sol_s ≈ sol_js end diff --git a/test/simulation_and_solving/simulate_ODEs.jl b/test/simulation_and_solving/simulate_ODEs.jl index 260f5d78cb..e12cac10a2 100644 --- a/test/simulation_and_solving/simulate_ODEs.jl +++ b/test/simulation_and_solving/simulate_ODEs.jl @@ -15,17 +15,17 @@ include("../test_networks.jl") # Exponential decay, should be identical to the (known) analytical solution. let - exponential_decay = @reaction_network begin + exponential_decay = @reaction_network begin d, X → ∅ end - for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1, 1.0e2] - u0 = rnd_u0(exponential_decay, rng; factor) + for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2] + 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, Vern7(), saveat = t_stops, abstol = 1.0e-10, reltol = 1.0e-10) + 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 @@ -40,8 +40,8 @@ let (k7, k8), ∅ ↔ X8 end - for factor in [1.0e-1, 1.0e0, 1.0e1] - u0 = rnd_u0(known_equilibrium, rng; factor) + 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, Rosenbrock23()) @@ -60,14 +60,14 @@ let manual_networks = [] u0_syms = [] ps_syms = [] - + function real_functions_1(du, u, p, t) X1, X2, X3 = u p1, p2, p3, k1, k2, k3, k4, d1, d2, d3 = p du[1] = p1 + k1 * X2 - k2 * X1 * X3^2 / factorial(2) - k3 * X1 + k4 * X3 - d1 * X1 du[2] = p2 - k1 * X2 + k2 * X1 * X3^2 / factorial(2) - d2 * X2 - return du[3] = p3 + 2 * k1 * X2 - 2 * k2 * X1 * X3^2 / factorial(2) + k3 * X1 - k4 * X3 - - d3 * X3 + du[3] = p3 + 2 * k1 * X2 - 2 * k2 * X1 * X3^2 / factorial(2) + k3 * X1 - k4 * X3 - + d3 * X3 end push!(catalyst_networks, reaction_networks_standard[1]) push!(manual_networks, real_functions_1) @@ -78,7 +78,7 @@ let X1, X2 = u v1, K1, v2, K2, d = p du[1] = v1 * K1 / (K1 + X2) - d * X1 * X2 - return du[2] = v2 * X1 / (K2 + X1) - d * X1 * X2 + du[2] = v2 * X1 / (K2 + X1) - d * X1 * X2 end push!(catalyst_networks, reaction_networks_standard[2]) push!(manual_networks, real_functions_2) @@ -90,7 +90,7 @@ let v1, v2, v3, K1, K2, K3, n1, n2, n3, d1, d2, d3 = p du[1] = v1 * K1^n1 / (K1^n1 + X3^n1) - d1 * X1 du[2] = v2 * K2^n2 / (K2^n2 + X1^n2) - d2 * X2 - return du[3] = v3 * K3^n3 / (K3^n3 + X2^n3) - d3 * X3 + du[3] = v3 * K3^n3 / (K3^n3 + X2^n3) - d3 * X3 end push!(catalyst_networks, reaction_networks_hill[2]) push!(manual_networks, real_functions_3) @@ -102,7 +102,7 @@ let k1, k2, k3, k4, k5, k6 = p du[1] = -k1 * X1 + k2 * X2 + k5 * X3 - k6 * X1 du[2] = -k3 * X2 + k4 * X3 + k1 * X1 - k2 * X2 - return du[3] = -k5 * X3 + k6 * X1 + k3 * X2 - k4 * X3 + du[3] = -k5 * X3 + k6 * X1 + k3 * X2 - k4 * X3 end push!(catalyst_networks, reaction_networks_conserved[1]) push!(manual_networks, real_functions_4) @@ -114,7 +114,7 @@ let k1, k2, k3, k4 = p du[1] = k1 - k2 * log(12 + X) * X du[2] = k2 * log(12 + X) * X - k3 * log(3 + Y) * Y - return du[3] = k3 * log(3 + Y) * Y - log(5, 6 + k4) * Z + du[3] = k3 * log(3 + Y) * Y - log(5, 6 + k4) * Z end push!(catalyst_networks, reaction_networks_weird[2]) push!(manual_networks, real_functions_5) @@ -122,7 +122,7 @@ let push!(ps_syms, [:k1, :k2, :k3, :k4]) for (rn_catalyst, rn_manual, u0_sym, ps_sym) in zip(catalyst_networks, manual_networks, u0_syms, ps_syms) - for factor in [1.0e-2, 1.0e-1, 1.0e0, 1.0e1] + for factor in [1e-2, 1e-1, 1e0, 1e1] # Set input values. u0_1 = rnd_u0(rn_catalyst, rng; factor = factor) ps_1 = rnd_ps(rn_catalyst, rng; factor = factor) @@ -135,7 +135,7 @@ let 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. + # 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()) @@ -148,7 +148,7 @@ end ### Checks Simulations Don't Error ### let for (i, rn) in enumerate(reaction_networks_all) - for factor in [1.0e-1, 1.0e0, 1.0e1] + for factor in [1e-1, 1e0, 1e1] 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]) @@ -165,13 +165,13 @@ end ### Other Tests ### # Checks that solution values have types consistent with their input types. -# Check that both float types are preserved in the solution (and problems), while integers are +# Check that both float types are preserved in the solution (and problems), while integers are # promoted to floats. # Checks that the time types are correct (`Float64` by default or possibly `Float32`). let # Create model. Checks when input type is `Float64` the produced values are also `Float64`. rn = @reaction_network begin - (k1, k2), X1 <--> X2 + (k1,k2), X1 <--> X2 end u0 = [:X1 => 1.0, :X2 => 3.0] ps = [:k1 => 2.0, :k2 => 3.0] @@ -180,7 +180,7 @@ let @test eltype(osol[:X1]) == eltype(osol[:X2]) == typeof(oprob[:X1]) == typeof(oprob[:X2]) == Float64 @test eltype(osol.t) == typeof(oprob.tspan[1]) == typeof(oprob.tspan[2]) == Float64 - # Checks that `Int64` values are promoted to `Float64`. + # Checks that `Int64` values are promoted to `Float64`. u0 = [:X1 => 1, :X2 => 3] ps = [:k1 => 2, :k2 => 3] oprob = ODEProblem(rn, u0, 1, ps) @@ -199,10 +199,10 @@ end # Tests simulating a network without parameters. let - no_param_network = @reaction_network begin - (1.5, 2), ∅ ↔ X + no_param_network = @reaction_network begin + (1.5, 2), ∅ ↔ X end - for factor in [1.0e0, 1.0e1, 1.0e2] + for factor in [1e0, 1e1, 1e2] u0 = rnd_u0(no_param_network, rng; factor) prob = ODEProblem(no_param_network, u0, (0.0, 1000.0)) sol = solve(prob, Rosenbrock23()) @@ -216,19 +216,19 @@ let 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 - return nothing + nothing end - rn = @reaction_network begin - k, 2.5 * A --> 3 * B + rn = @reaction_network begin + k, 2.5 * A --> 3 * B end 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) - + # Check equivalence. - du1 = du2 = zeros(2) + du1 = du2 = zeros(2) oprob = ODEProblem(rn, u_1, tspan, p_1; combinatoric_ratelaws = false) oprob.f(du1, oprob.u0, oprob.p, 90.0) oderhs(du2, u_2, p_2, 0.0) diff --git a/test/simulation_and_solving/simulate_SDEs.jl b/test/simulation_and_solving/simulate_SDEs.jl index 89c8bbda66..3fd300aa08 100644 --- a/test/simulation_and_solving/simulate_SDEs.jl +++ b/test/simulation_and_solving/simulate_SDEs.jl @@ -31,7 +31,7 @@ let p, k1, k2, k3, d = p du[1] = 2 * p - k1 * X1 du[2] = k1 * X1 - k2 * X2 - k3 * X2 - return du[3] = k2 * X2 + k3 * X2 - d * X3 + du[3] = k2 * X2 + k3 * X2 - d * X3 end function real_g_1(du, u, p, t) X1, X2, X3 = u @@ -50,7 +50,7 @@ let du[3, 2] = 0 du[3, 3] = sqrt(k2 * X2) du[3, 4] = sqrt(k3 * X2) - return du[3, 5] = -sqrt(d * X3) + du[3, 5] = -sqrt(d * X3) end push!(catalyst_networks, reaction_networks_standard[8]) push!(manual_networks, (f = real_f_1, g = real_g_1, nrp = zeros(3, 5))) @@ -60,13 +60,13 @@ let function real_f_2(du, u, p, t) X1, = u v, K, n, d = p - return du[1] = v / 10 + v * X1^n / (X1^n + K^n) - d * X1 + du[1] = v / 10 + v * X1^n / (X1^n + K^n) - d * X1 end function real_g_2(du, u, p, t) X1, = u v, K, n, d = p du[1, 1] = sqrt(v / 10 + v * X1^n / (X1^n + K^n)) - return du[1, 2] = -sqrt(d * X1) + du[1, 2] = -sqrt(d * X1) end push!(catalyst_networks, reaction_networks_hill[6]) push!(manual_networks, (f = real_f_2, g = real_g_2, nrp = zeros(1, 2))) @@ -82,7 +82,7 @@ let du[4] = -k3 * X3 * X4 + k4 * X5 du[5] = k3 * X3 * X4 - k4 * X5 - k5 * X5 * X6 + k6 * X7 du[6] = -k5 * X5 * X6 + k6 * X7 - return du[7] = k5 * X5 * X6 - k6 * X7 + du[7] = k5 * X5 * X6 - k6 * X7 end function real_g_3(du, u, p, t) X1, X2, X3, X4, X5, X6, X7 = u @@ -105,7 +105,7 @@ let du[6, 5] = -sqrt(k5 * X5 * X6) du[6, 6] = sqrt(k6 * X7) du[7, 5] = sqrt(k5 * X5 * X6) - return du[7, 6] = -sqrt(k6 * X7) + du[7, 6] = -sqrt(k6 * X7) end push!(catalyst_networks, reaction_networks_conserved[9]) push!(manual_networks, (f = real_f_3, g = real_g_3, nrp = zeros(7, 6))) @@ -113,7 +113,7 @@ let push!(ps_syms, [:k1, :k2, :k3, :k4, :k5, :k6]) for (rn_catalyst, rn_manual, u0_sym, ps_sym) in zip(catalyst_networks, manual_networks, u0_syms, ps_syms) - for factor in [1.0e-1, 1.0e0], repeat in 1:3 + for factor in [1e-1, 1e0], repeat in 1:3 # Set input values. u0_1 = rnd_u0(rn_catalyst, rng; factor, min = 100.0) ps_1 = rnd_ps(rn_catalyst, rng; factor, min = 0.01) @@ -167,7 +167,7 @@ let @parameters η1 η2 @default_noise_scaling η1 p, 0 --> X1 - (k1, k2), X1 ↔ X2, ([noise_scaling = η2], [noise_scaling = 2 * η2 + 1]) + (k1, k2), X1 ↔ X2, ([noise_scaling=η2],[noise_scaling=2 * η2 + 1]) d, X2 --> 0, [noise_scaling = 0.0] end @@ -185,7 +185,7 @@ 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]) + (k1, k2), X1 ↔ X2, ([noise_scaling=η1],[noise_scaling=η2]) end u0 = [:X1 => 1000.0, :X2 => 3000.0] 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]) @@ -201,7 +201,7 @@ let # 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]]) + (k1, k2), X1 ↔ X2, ([noise_scaling=η[1]],[noise_scaling=η[2]]) end @unpack k1, k2, η = noise_scaling_network_2 sprob_2_1 = SDEProblem(noise_scaling_network_2, u0, (0.0, 1000.0), [k1 => 2.0, k2 => 0.66, η => [2.0, 2.0]]) @@ -238,9 +238,9 @@ end # Tests the noise_scaling_parameters getter. let noise_scaling_network = @reaction_network begin - @parameters k1 par1 [description = "Parameter par1"] par2 η1 η2 = 0.0 [description = "Parameter η2"] η3 = 1.0 η4 - (p, d), 0 ↔ X1, ([noise_scaling = η1], [noise_scaling = η2]) - (k1, k2), X1 ↔ X2, ([noise_scaling = η3], [noise_scaling = η4]) + @parameters k1 par1 [description="Parameter par1"] par2 η1 η2=0.0 [description="Parameter η2"] η3=1.0 η4 + (p, d), 0 ↔ X1, ([noise_scaling=η1],[noise_scaling=η2]) + (k1, k2), X1 ↔ X2, ([noise_scaling=η3],[noise_scaling=η4]) end u0 = [:X1 => 500.0, :X2 => 500.0] p = [:p => 20.0, :d => 0.1, :η1 => 0.0, :η3 => 0.0, :η4 => 0.0, :k1 => 2.0, :k2 => 2.0, :par1 => 1000.0, :par2 => 1000.0] @@ -257,11 +257,11 @@ end # tests for default value used in noise scaling parameter. let noise_scaling_network = @reaction_network begin - @parameters η1 η2 = 0.1 + @parameters η1 η2=0.1 @default_noise_scaling η1 - (p, d), 0 <--> X1, ([noise_scaling = η2], [noise_scaling = η2]) - (p, d), 0 <--> X2, ([description = "Y"], [description = "Y"]) - (p, d), 0 <--> X3 + (p,d), 0 <--> X1, ([noise_scaling=η2],[noise_scaling=η2]) + (p,d), 0 <--> X2, ([description="Y"],[description="Y"]) + (p,d), 0 <--> X3 end u0 = [:X1 => 1000.0, :X2 => 1000.0, :X3 => 1000.0] @@ -280,15 +280,15 @@ end return false noise_scaling_network = @reaction_network begin @parameters η1 η2 η3 η4 - @species N1(t) N2(t) = 0.5 + @species N1(t) N2(t)=0.5 @variables N3(t) @default_noise_scaling η1 + N1 + 5.0 p, 0 --> X1 - p, 0 --> X2, [noise_scaling = 0.33η2^1.2 + N2] - p, 0 --> X3, [noise_scaling = N3 * η3] - p, 0 --> X4, [noise_scaling = exp(-η4) - 0.008] - p, 0 --> X5, [noise_scaling = 0.0] - d, (X1, X2, X3, X4, X5) --> 0, ([], [noise_scaling = 0.33η2^1.2 + N2], [noise_scaling = N3 * η3], [noise_scaling = exp(-η4) - 0.008], [noise_scaling = 0.0]) + p, 0 --> X2, [noise_scaling=0.33η2^1.2 + N2] + p, 0 --> X3, [noise_scaling=N3*η3] + p, 0 --> X4, [noise_scaling=exp(-η4) - 0.008] + p, 0 --> X5, [noise_scaling=0.0] + d, (X1, X2, X3, X4, X5) --> 0, ([], [noise_scaling=0.33η2^1.2 + N2], [noise_scaling=N3*η3], [noise_scaling=exp(-η4) - 0.008], [noise_scaling=0.0]) end u0 = [:X1 => 1000.0, :X2 => 1000.0, :X3 => 1000.0, :X4 => 1000.0, :X5 => 1000.0, :N1 => 3.0, :N3 => 0.33] @@ -320,15 +320,15 @@ let @species X(t) H(t) @variables h(t) @parameters p d η - rx1 = Reaction(p, nothing, [X]; metadata = [:noise_scaling => η * H + 1]) - rx2 = @reaction d, X --> 0, [noise_scaling = $h] + rx1 = Reaction(p, nothing, [X]; metadata = [:noise_scaling => η*H + 1]) + rx2 = @reaction d, X --> 0, [noise_scaling=$h] @named rs = ReactionSystem([rx1, rx2], t) # Checks that noise scaling has been added correctly. @test issetequal([X, H], species(rs)) @test issetequal([X, H, h], unknowns(rs)) @test issetequal([p, d, η], parameters(rs)) - @test isequal(getnoisescaling(reactions(rs)[1]), η * H + 1) + @test isequal(getnoisescaling(reactions(rs)[1]), η*H + 1) @test isequal(getnoisescaling(reactions(rs)[2]), h) end @@ -339,7 +339,7 @@ let # Creates noise scaling networks. noise_scaling_network1 = @reaction_network begin @parameters η1 - p, 0 --> X, [noise_scaling = 2.0] + p, 0 --> X, [noise_scaling=2.0] d, X --> 0 end @unpack p, d, η1, X = noise_scaling_network1 @@ -361,11 +361,11 @@ end let # Creates hierarchical model. rn1 = @network_component rn1 begin - p, 0 --> X, [noise_scaling = 2.0] + p, 0 --> X, [noise_scaling=2.0] d, X --> 0 end rn2 = @network_component rn2 begin - k1, X1 --> X2, [noise_scaling = 5.0] + k1, X1 --> X2, [noise_scaling=5.0] k2, X2 --> X1 end rn = compose(rn1, [rn2]) @@ -381,6 +381,7 @@ let end + ### Other Tests ### # Checks that solution values have types consistent with their input types. @@ -391,7 +392,7 @@ end let # Create model. Checks when input type is `Float64` the produced values are also `Float64`. rn = @reaction_network begin - (k1, k2), X1 <--> X2 + (k1,k2), X1 <--> X2 end u0 = [:X1 => 1.0, :X2 => 3.0] ps = [:k1 => 2.0, :k2 => 3.0] @@ -422,7 +423,7 @@ let no_param_network = @reaction_network begin (1.2, 5), X1 ↔ X2 end - for factor in [1.0e3, 1.0e4] + for factor in [1e3, 1e4] u0 = rnd_u0(no_param_network, rng; factor) sprob = SDEProblem(no_param_network, u0, (0.0, 1000.0)) for repeat in 1:5 @@ -510,7 +511,7 @@ end # Test D: `only_use_rate=true` reactions are unaffected by use_jump_ratelaws. let rn = @reaction_network begin - k * X^2, X => Y + k*X^2, X => Y end u0 = [:X => 50.0, :Y => 10.0] ps = [:k => 1.0] @@ -592,27 +593,23 @@ let tspan = (0.0, 20.0) # Build Catalyst SDEProblem with jump rate laws - cat_prob = SDEProblem( - rn, [:X => u0_val[1]], tspan, - [:k_prod => k_prod_val, :k_deg => k_deg_val]; use_jump_ratelaws = true - ) + cat_prob = SDEProblem(rn, [:X => u0_val[1]], tspan, + [:k_prod => k_prod_val, :k_deg => k_deg_val]; use_jump_ratelaws = true) # Hand-coded SDE with jump rate laws function handcoded_f!(du, u, p, t) X = u[1] k_prod, k_deg = p - return du[1] = k_prod - k_deg * X * (X - 1) + du[1] = k_prod - k_deg * X * (X - 1) end function handcoded_g!(du, u, p, t) X = u[1] k_prod, k_deg = p du[1, 1] = sqrt(k_prod) - return du[1, 2] = -2 * sqrt(k_deg * X * (X - 1) / 2) + du[1, 2] = -2 * sqrt(k_deg * X * (X - 1) / 2) end - hand_prob = SDEProblem( - handcoded_f!, handcoded_g!, u0_val, tspan, - [k_prod_val, k_deg_val]; noise_rate_prototype = zeros(1, 2) - ) + hand_prob = SDEProblem(handcoded_f!, handcoded_g!, u0_val, tspan, + [k_prod_val, k_deg_val]; noise_rate_prototype = zeros(1, 2)) # Run ensembles and compare statistics N = 2000 diff --git a/test/simulation_and_solving/simulate_jumps.jl b/test/simulation_and_solving/simulate_jumps.jl index 312e8ae92e..ceeaf6a490 100644 --- a/test/simulation_and_solving/simulate_jumps.jl +++ b/test/simulation_and_solving/simulate_jumps.jl @@ -53,10 +53,8 @@ let jump_1_6 = ConstantRateJump(rate_1_6, affect_1_6!) jump_1_7 = ConstantRateJump(rate_1_7, affect_1_7!) 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, - ) + 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!(catalyst_networks, reaction_networks_standard[5]) push!(manual_networks, jumps_1) push!(u0_syms, [:X1, :X2, :X3, :X4]) @@ -130,7 +128,7 @@ let # Loops through all cases, checks that identical simulations are generated with/without Catalyst. for (rn_catalyst, rn_manual, u0_sym, ps_sym, u0_1, ps_1, sp) in - zip(catalyst_networks, manual_networks, u0_syms, ps_syms, u0s, ps, sps) + zip(catalyst_networks, manual_networks, u0_syms, ps_syms, u0s, ps, sps) # Simulates the Catalyst-created model. jprob_1 = JumpProblem(rn_catalyst, u0_1, (0.0, 10000.0), ps_1; aggregator = Direct(), rng) @@ -139,7 +137,7 @@ let # simulate using auto-alg jprob_1b = JumpProblem(rn_catalyst, u0_1, (0.0, 10000.0), ps_1; rng) sol1b = solve(jprob_1; seed, saveat = 1.0) - @test mean(sol1[sp]) ≈ mean(sol1b[sp]) rtol = 1.0e-1 + @test mean(sol1[sp]) ≈ mean(sol1b[sp]) rtol = 1e-1 # Simulates the manually written model u0_2 = map_to_vec(u0_1, u0_sym) @@ -150,7 +148,7 @@ let # Checks that the means are similar (the test have been check that it holds across a large # number of simulates, even without seed). - @test mean(sol1[sp]) ≈ mean(sol2[findfirst(u0_sym .== sp), :]) rtol = 1.0e-1 + @test mean(sol1[sp]) ≈ mean(sol2[findfirst(u0_sym .== sp),:]) rtol = 1e-1 end end @@ -179,27 +177,25 @@ end # this should also help with indirectly testing dep graphs are setup ok let rn = @reaction_network gene_model begin - α * (1 + sin(t)), D --> D + P - μ * (1 + cos(t)), P --> ∅ + α*(1 + sin(t)), D --> D + P + μ*(1 + cos(t)), P --> ∅ k₊, D + P --> D⁻ k₋, D⁻ --> D + P α, D2 --> D2 + P2 μ, P2 --> P3 end - u0map = [ - rn.D => 1.0, rn.P => 0.0, rn.D⁻ => 0.0, rn.D2 => 1.0, rn.P2 => 0.0, - rn.P3 => 0.0, - ] + u0map = [rn.D => 1.0, rn.P => 0.0, rn.D⁻ => 0.0, rn.D2 => 1.0, rn.P2 => 0.0, + rn.P3 => 0.0] pmap = [rn.α => 10.0, rn.μ => 1.0, rn.k₊ => 1.0, rn.k₋ => 2.0] tspan = (0.0, 25.0) # the direct method needs no dep graphs so is good as a baseline for comparison jprobdm = JumpProblem(rn, u0map, tspan, pmap; aggregator = Direct(), save_positions = (false, false), rng) jprobsd = JumpProblem(rn, u0map, tspan, pmap; aggregator = SortingDirect(), save_positions = (false, false), rng) - @test issetequal(jprobsd.discrete_jump_aggregation.dep_gr, [[1, 2], [2]]) + @test issetequal(jprobsd.discrete_jump_aggregation.dep_gr, [[1,2],[2]]) jprobrssa = JumpProblem(rn, u0map, tspan, pmap; aggregator = RSSA(), save_positions = (false, false), rng) - @test issetequal(jprobrssa.discrete_jump_aggregation.vartojumps_map, [[], [], [], [1], [2], []]) - @test issetequal(jprobrssa.discrete_jump_aggregation.jumptovars_map, [[5], [5, 6]]) + @test issetequal(jprobrssa.discrete_jump_aggregation.vartojumps_map, [[],[],[],[1],[2],[]]) + @test issetequal(jprobrssa.discrete_jump_aggregation.jumptovars_map, [[5],[5,6]]) N = 4000 # number of simulations to run function getmean(N, prob) m1 = 0.0 @@ -219,11 +215,11 @@ let # See: https://github.com/SciML/JumpProcesses.jl/issues/545 means1 = zeros(2) means2 = zeros(2) - for (i, prob) in enumerate((jprobdm, jprobsd)) - means1[i], means2[i] = getmean(N, prob) + for (i,prob) in enumerate((jprobdm, jprobsd)) + means1[i],means2[i] = getmean(N, prob) end - @test abs(means1[1] - means1[2]) < 0.1 * means1[1] - @test abs(means2[1] - means2[2]) < 0.1 * means2[1] + @test abs(means1[1] - means1[2]) < .1 * means1[1] + @test abs(means2[1] - means2[2]) < .1 * means2[1] # RSSA simulation tests are broken - see comment above # @test_broken abs(means1[1] - means1[3]) < .1 * means1[1] # @test_broken abs(means2[1] - means2[3]) < .1 * means2[1] @@ -237,7 +233,7 @@ end let # Create model. Checks when input type is `Float64` the produced values are also `Float64`. rn = @reaction_network begin - (k1, k2), X1 <--> X2 + (k1,k2), X1 <--> X2 end u0 = [:X1 => 1.0, :X2 => 3.0] ps = [:k1 => 2.0, :k2 => 3.0] @@ -449,10 +445,8 @@ end @test jprob3.massaction_jump.scaled_rates[1] ≈ 6.0 / factorial(3) # callback + reset_aggregated_jumps! - jprob_cb = JumpProblem( - rn, u0, (0.0, 200.0), ps; - aggregator = Direct(), save_positions = (false, false) - ) + jprob_cb = JumpProblem(rn, u0, (0.0, 200.0), ps; + aggregator = Direct(), save_positions = (false, false)) condit(u, t, integrator) = t == 100.0 setk! = setp(jprob_cb, :k) function affect_cb!(integrator) @@ -501,10 +495,8 @@ end @test jprob3.massaction_jump.scaled_rates[1] ≈ 6.0 # callback + reset_aggregated_jumps! - jprob_cb = JumpProblem( - rn, u0, (0.0, 200.0), ps; - aggregator = Direct(), save_positions = (false, false) - ) + jprob_cb = JumpProblem(rn, u0, (0.0, 200.0), ps; + aggregator = Direct(), save_positions = (false, false)) condit(u, t, integrator) = t == 100.0 setk! = setp(jprob_cb, :k) function affect_cb!(integrator) @@ -549,10 +541,8 @@ end @test jprob2.massaction_jump.scaled_rates[2] ≈ 3.0 # callback changing only the higher-order rate. - jprob_cb = JumpProblem( - rn, u0, (0.0, 200.0), ps; - aggregator = Direct(), save_positions = (false, false) - ) + jprob_cb = JumpProblem(rn, u0, (0.0, 200.0), ps; + aggregator = Direct(), save_positions = (false, false)) condit(u, t, integrator) = t == 100.0 setk1! = setp(jprob_cb, :k1) function affect_cb!(integrator) @@ -651,10 +641,8 @@ end # After callback — both should still agree. for (rn, ps) in [(rn_true, [:k => k_val]), (rn_false, [:k => k_val / factorial(3)])] - jp = JumpProblem( - rn, u0, (0.0, 200.0), ps; - aggregator = Direct(), save_positions = (false, false) - ) + jp = JumpProblem(rn, u0, (0.0, 200.0), ps; + aggregator = Direct(), save_positions = (false, false)) condit(u, t, integrator) = t == 100.0 new_k_true = 24.0 new_k_val = rn === rn_true ? new_k_true : new_k_true / factorial(3) diff --git a/test/simulation_and_solving/solve_nonlinear.jl b/test/simulation_and_solving/solve_nonlinear.jl index 041c1cb596..a02e9c22a5 100644 --- a/test/simulation_and_solving/solve_nonlinear.jl +++ b/test/simulation_and_solving/solve_nonlinear.jl @@ -20,7 +20,7 @@ let steady_state_network_1 = @reaction_network begin (k1, k2), ∅ ↔ X1 (k3, k4), ∅ ↔ 3X2 - (k5, k6 * X1), ∅ ↔ X3 + (k5, k6*X1), ∅ ↔ X3 end # Creates NonlinearProblem. @@ -29,16 +29,16 @@ let nlprob = NonlinearProblem(steady_state_network_1, u0, ps) # Solves it using standard algorithm and simulation based algorithm. - sol1 = solve(nlprob; abstol = 1.0e-12, reltol = 1.0e-12) - sol2 = solve(nlprob, DynamicSS(Rosenbrock23()); abstol = 1.0e-12, reltol = 1.0e-12) + 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 sol1[:X1] ≈ nlprob.ps[:k1] / nlprob.ps[:k2] atol = 1.0e-10 - @test sol1[:X2]^3 / factorial(3) ≈ nlprob.ps[:k3] / nlprob.ps[:k4] atol = 1.0e-10 - @test sol1[:X3] ≈ (nlprob.ps[:k5] * nlprob.ps[:k2]) / (nlprob.ps[:k6] * nlprob.ps[:k1]) atol = 1.0e-10 - @test sol2[:X1] ≈ nlprob.ps[:k1] / nlprob.ps[:k2] atol = 1.0e-10 - @test sol2[:X2]^3 / factorial(3) ≈ nlprob.ps[:k3] / nlprob.ps[:k4] atol = 1.0e-10 - @test sol2[:X3] ≈ (nlprob.ps[:k5] * nlprob.ps[:k2]) / (nlprob.ps[:k6] * nlprob.ps[:k1]) atol = 1.0e-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. @@ -57,23 +57,23 @@ let nlprob = NonlinearProblem(steady_state_network_2, u0, ps) # Solves it using standard algorithm and simulation based algorithm. - sol1 = solve(nlprob; abstol = 1.0e-12, reltol = 1.0e-12) - sol2 = solve(nlprob, DynamicSS(Rosenbrock23()); abstol = 1.0e-12, reltol = 1.0e-12) + 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). nlprob_eval_1 = remake(nlprob; u0 = [:X => sol1[1]]) nlprob_eval_2 = remake(nlprob; u0 = [:X => sol2[1]]) - function nf_manual(u, p) + function nf_manual(u,p) X = u[:X] v, K, n, d = p - return v / 10 + v * X^n / (X^n + K^n) - d * X + return v/10 + v * X^n/(X^n + K^n) - d*X end # Tests solutions are correct. - @test nlprob_eval_1.f(nlprob_eval_1.u0, nlprob_eval_1.p)[1] ≈ 0.0 atol = 1.0e-10 - @test nlprob_eval_2.f(nlprob_eval_2.u0, nlprob_eval_2.p)[1] ≈ 0.0 atol = 1.0e-10 - @test nf_manual(sol1, last.(ps)) ≈ 0.0 atol = 1.0e-10 - @test nf_manual(sol2, last.(ps)) ≈ 0.0 atol = 1.0e-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. @@ -81,7 +81,7 @@ end let # Creates steady state network, unpack the parameter values. steady_state_network_3 = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X (k1, k2), 2Y <--> Y2 (k3, k4), X + Y2 <--> XY2 end @@ -89,17 +89,17 @@ let # Creates NonlinearProblem. u0 = [steady_state_network_3.X => rand(), steady_state_network_3.Y => rand() + 1.0, steady_state_network_3.Y2 => rand() + 3.0, steady_state_network_3.XY2 => 0.0] - p = [:p => rand() + 1.0, :d => 0.5, :k1 => 1.0, :k2 => 2.0, :k3 => 3.0, :k4 => 4.0] + p = [:p => rand()+1.0, :d => 0.5, :k1 => 1.0, :k2 => 2.0, :k3 => 3.0, :k4 => 4.0] nl_prob_1 = NonlinearProblem(steady_state_network_3, u0, p; remove_conserved = true, mtkcompile = true) nl_prob_2 = NonlinearProblem(steady_state_network_3, u0, p) # Solves it using standard algorithm and simulation based algorithm. - sol1 = solve(nl_prob_1; abstol = 1.0e-12, reltol = 1.0e-12) - sol2 = solve(nl_prob_2, DynamicSS(Rosenbrock23()); abstol = 1.0e-12, reltol = 1.0e-12) + 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 the ODE's drift function - @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 = 1.0e-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 = 1.0e-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 ### Other Tests ### @@ -110,7 +110,7 @@ end let # Create model. Checks when input type is `Float64` that the problem and solution types are `Float64`. rn = @reaction_network begin - (k1, k2), X1 <--> X2 + (k1,k2), X1 <--> X2 end u0 = [:X1 => 1.0, :X2 => 3.0] ps = [:k1 => 2.0, :k2 => 3.0] diff --git a/test/spatial_modelling/dspace_reaction_systems.jl b/test/spatial_modelling/dspace_reaction_systems.jl index d88070872b..648c8659eb 100644 --- a/test/spatial_modelling/dspace_reaction_systems.jl +++ b/test/spatial_modelling/dspace_reaction_systems.jl @@ -34,7 +34,7 @@ end # Test case 2. let rs = @reaction_network begin - @parameters p1 p2 [edgeparameter = true] + @parameters p1 p2 [edgeparameter=true] end @unpack p1, p2 = rs @@ -45,7 +45,7 @@ end # Test case 3. let rs = @reaction_network begin - @parameters pX pY dX [edgeparameter = true] dY + @parameters pX pY dX [edgeparameter=true] dY (pX, 1), 0 <--> X (pY, 1), 0 <--> Y end @@ -87,7 +87,7 @@ end let rs = @reaction_network begin @species W(t) - @parameters pX pY dX [edgeparameter = true] dY + @parameters pX pY dX [edgeparameter=true] dY (pX, 1), 0 <--> X (pY, 1), 0 <--> Y (pZ, 1), 0 <--> Z @@ -178,7 +178,7 @@ let @species Y(t) (p, d), 0 <--> X end - tr = @transport_reaction D * Y X + tr = @transport_reaction D*Y X for grid in grids @test_throws ErrorException DiscreteSpaceReactionSystem(rs, [tr], grid) end @@ -187,7 +187,7 @@ end # Network with edge parameter in non-spatial reaction rate. let rs = @reaction_network begin - @parameters p [edgeparameter = true] + @parameters p [edgeparameter=true] (p, d), 0 <--> X end tr = @transport_reaction D X @@ -199,7 +199,7 @@ end # Network where metadata has been added in rs (which is not seen in transport reaction). let rs = @reaction_network begin - @species X(t) [description = "Species with added metadata"] + @species X(t) [description="Species with added metadata"] (p, d), 0 <--> X end tr = @transport_reaction D X @@ -207,7 +207,7 @@ let @test_throws ErrorException DiscreteSpaceReactionSystem(rs, [tr], grid) rs = @reaction_network begin - @parameters D [description = "Parameter with added metadata"] + @parameters D [description="Parameter with added metadata"] (p, d), 0 <--> X end tr = @transport_reaction D X @@ -234,30 +234,30 @@ let # Variable unknowns. rs1 = @reaction_network begin @variables V(t) - (p, d), 0 <--> X + (p,d), 0 <--> X end @test_throws ArgumentError DiscreteSpaceReactionSystem(rs1, [tr], short_path) # Non-reaction equations. rs2 = @reaction_network begin @equations D(V) ~ X - V - (p, d), 0 <--> X + (p,d), 0 <--> X end @test_throws ArgumentError DiscreteSpaceReactionSystem(rs2, [tr], short_path) # Events. rs3 = @reaction_network begin @discrete_events [1.0] => [p => p + 1] - (p, d), 0 <--> X + (p,d), 0 <--> X end @test_throws ArgumentError DiscreteSpaceReactionSystem(rs3, [tr], short_path) # Observables (only generates a warning). rs4 = @reaction_network begin @observables X2 ~ 2X - (p, d), 0 <--> X + (p,d), 0 <--> X end - @test_logs (:warn, r"The `ReactionSystem` used as input to `DiscreteSpaceReactionSystem` contain observables. It *") match_mode = :any DiscreteSpaceReactionSystem(rs4, [tr], short_path) + @test_logs (:warn, r"The `ReactionSystem` used as input to `DiscreteSpaceReactionSystem` contain observables. It *") match_mode=:any DiscreteSpaceReactionSystem(rs4, [tr], short_path) end # Tests for hierarchical input system (should yield a warning). @@ -269,16 +269,16 @@ let @named rs1 = ReactionSystem(rxs, t) @named rs2 = ReactionSystem(rxs, t; systems = [rs1]) rs2 = complete(rs2) - @test_logs (:warn, r"The `ReactionSystem` used as input to `DiscreteSpaceReactionSystem` was originally created as a hierarchical model. While *") match_mode = :any DiscreteSpaceReactionSystem(rs2, [TransportReaction(D, X)], CartesianGrid((2, 2))) + @test_logs (:warn, r"The `ReactionSystem` used as input to `DiscreteSpaceReactionSystem` was originally created as a hierarchical model. While *") match_mode=:any DiscreteSpaceReactionSystem(rs2, [TransportReaction(D, X)], CartesianGrid((2,2))) end # Tests for non-complete input `ReactionSystem`. let tr = @transport_reaction D X rs = @network_component begin - (p, d), 0 <--> X + (p,d), 0 <--> X end - @test_throws ArgumentError DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2, 2))) + @test_throws ArgumentError DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,2))) end # Tests for array parameters/species. @@ -287,17 +287,17 @@ let rs1 = @reaction_network begin @species X(t)[1:2] Y(t) - (k1, k2), X[1] <--> X[2] + (k1,k2), X[1] <--> X[2] end - @test_throws ArgumentError DiscreteSpaceReactionSystem(rs1, [tr], CartesianGrid((2, 2))) + @test_throws ArgumentError DiscreteSpaceReactionSystem(rs1, [tr], CartesianGrid((2,2))) - rs2 = @reaction_network begin + rs2 = @reaction_network begin @species Y(t) - @parameters k[1:2, 1:2] - (k[1, 1], k[1, 2]), X11 <--> X12 - (k[2, 1], k[2, 2]), X21 <--> X22 + @parameters k[1:2,1:2] + (k[1,1],k[1,2]), X11 <--> X12 + (k[2,1],k[2,2]), X21 <--> X22 end - @test_throws ArgumentError DiscreteSpaceReactionSystem(rs2, [tr], CartesianGrid((2, 2))) + @test_throws ArgumentError DiscreteSpaceReactionSystem(rs2, [tr], CartesianGrid((2,2))) end ### Tests Grid Vertex and Edge Number Computation ### @@ -307,17 +307,15 @@ let # Function counting the values in an iterator by stepping through it. function iterator_count(iterator) count = 0 - foreach(e -> count += 1, iterator) + foreach(e -> count+=1, iterator) return count end # Cartesian and masked grid (test diagonal edges as well). - for space in [ - small_1d_cartesian_grid, small_2d_cartesian_grid, small_3d_cartesian_grid, - random_1d_masked_grid, random_2d_masked_grid, random_3d_masked_grid, - ] + for space in [small_1d_cartesian_grid, small_2d_cartesian_grid, small_3d_cartesian_grid, + random_1d_masked_grid, random_2d_masked_grid, random_3d_masked_grid] dsrs1 = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_1, space) - dsrs2 = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_1, space; diagonal_connections = true) + dsrs2 = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_1, space; diagonal_connections=true) @test num_edges(dsrs1) == iterator_count(edge_iterator(dsrs1)) @test num_edges(dsrs2) == iterator_count(edge_iterator(dsrs2)) end @@ -335,7 +333,7 @@ end let # Prepares the model and the function that determines the edge values. rn = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X end tr = @transport_reaction D X function make_edge_p_value(src_vert, dst_vert) @@ -343,11 +341,9 @@ let end # Loops through a variety of grids, checks that `make_edge_p_values` yields the correct values. - for space in [ - small_1d_cartesian_grid, small_2d_cartesian_grid, small_3d_cartesian_grid, - small_1d_masked_grid, small_2d_masked_grid, small_3d_masked_grid, - random_1d_masked_grid, random_2d_masked_grid, random_3d_masked_grid, - ] + for space in [small_1d_cartesian_grid, small_2d_cartesian_grid, small_3d_cartesian_grid, + small_1d_masked_grid, small_2d_masked_grid, small_3d_masked_grid, + random_1d_masked_grid, random_2d_masked_grid, random_3d_masked_grid] dsrs = DiscreteSpaceReactionSystem(rn, [tr], space) fspat_to_grid_idx = Catalyst.get_index_converters(dspace(dsrs), num_verts(dsrs))[1] edge_values = make_edge_p_values(dsrs, make_edge_p_value) @@ -373,39 +369,39 @@ let dsrs = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid(n)) ps = [:D => make_directed_edge_values(dsrs, (10.0, 0.0))] oprob = ODEProblem(dsrs, u0, tspan, ps) - @test isapprox(solve(oprob, Tsit5()).u[end][5], n, rtol = 1.0e-6) + @test isapprox(solve(oprob, Tsit5()).u[end][5], n, rtol=1e-6) # Checks the 2d case (both with 1d and 2d flow). - dsrs = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid((n, n))) + dsrs = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid((n,n))) ps = [:D => make_directed_edge_values(dsrs, (1.0, 0.0), (0.0, 0.0))] oprob = ODEProblem(dsrs, u0, tspan, ps) - @test all(isapprox.(solve(oprob, Tsit5()).u[end][5:5:25], n, rtol = 1.0e-6)) + @test all(isapprox.(solve(oprob, Tsit5()).u[end][5:5:25], n, rtol=1e-6)) ps = [:D => make_directed_edge_values(dsrs, (1.0, 0.0), (1.0, 0.0))] oprob = ODEProblem(dsrs, u0, tspan, ps) - @test isapprox(solve(oprob, Tsit5()).u[end][25], n^2, rtol = 1.0e-6) + @test isapprox(solve(oprob, Tsit5()).u[end][25], n^2, rtol=1e-6) # Checks the 3d case (both with 1d and 2d flow). - dsrs = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid((n, n, n))) + dsrs = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid((n,n,n))) ps = [:D => make_directed_edge_values(dsrs, (1.0, 0.0), (0.0, 0.0), (0.0, 0.0))] oprob = ODEProblem(dsrs, u0, tspan, ps) - @test all(isapprox.(solve(oprob, Tsit5()).u[end][5:5:125], n, rtol = 1.0e-6)) + @test all(isapprox.(solve(oprob, Tsit5()).u[end][5:5:125], n, rtol=1e-6)) ps = [:D => make_directed_edge_values(dsrs, (1.0, 0.0), (1.0, 0.0), (0.0, 0.0))] oprob = ODEProblem(dsrs, u0, tspan, ps) - @test all(isapprox.(solve(oprob, Tsit5()).u[end][25:25:125], n^2, rtol = 1.0e-6)) + @test all(isapprox.(solve(oprob, Tsit5()).u[end][25:25:125], n^2, rtol=1e-6)) ps = [:D => make_directed_edge_values(dsrs, (1.0, 0.0), (1.0, 0.0), (1.0, 0.0))] oprob = ODEProblem(dsrs, u0, tspan, ps) - @test isapprox(solve(oprob, Tsit5()).u[end][125], n^3, rtol = 1.0e-6) + @test isapprox(solve(oprob, Tsit5()).u[end][125], n^3, rtol=1e-6) end # Checks that erroneous input yields errors. let rn = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X end tr = @transport_reaction D X tspan = (0.0, 10000.0) @@ -413,13 +409,13 @@ let # Graph grids. dsrs = DiscreteSpaceReactionSystem(rn, [tr], path_graph(5)) - @test_throws Exception make_edge_p_values(dsrs, make_edge_p_value) + @test_throws Exception make_edge_p_values(dsrs, make_edge_p_value,) @test_throws Exception make_directed_edge_values(dsrs, (1.0, 0.0)) # Wrong dimensions to `make_directed_edge_values`. dsrs_1d = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid(5)) - dsrs_2d = DiscreteSpaceReactionSystem(rn, [tr], fill(true, 5, 5)) - dsrs_3d = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid((5, 5, 5))) + dsrs_2d = DiscreteSpaceReactionSystem(rn, [tr], fill(true,5,5)) + dsrs_3d = DiscreteSpaceReactionSystem(rn, [tr], CartesianGrid((5,5,5))) @test_throws Exception make_directed_edge_values(dsrs_1d, (1.0, 0.0), (1.0, 0.0)) @test_throws Exception make_directed_edge_values(dsrs_1d, (1.0, 0.0), (1.0, 0.0), (1.0, 0.0)) diff --git a/test/spatial_modelling/dspace_reaction_systems_ODEs.jl b/test/spatial_modelling/dspace_reaction_systems_ODEs.jl index 9772f5f0f8..c3eff6c156 100644 --- a/test/spatial_modelling/dspace_reaction_systems_ODEs.jl +++ b/test/spatial_modelling/dspace_reaction_systems_ODEs.jl @@ -55,8 +55,8 @@ let dsrs = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_2, very_small_2d_cartesian_grid) u0 = [:S => 990.0, :I => rand_v_vals(dsrs), :R => 0.0] ps_1 = [:α => 0.1, :β => 0.01, :dS => 0.01, :dI => 0.01, :dR => 0.01] - ps_2 = [:α => 1 // 10, :β => 1 // 100, :dS => 1 // 100, :dI => 1 // 100, :dR => 1 // 100] - ps_3 = [:α => 1 // 10, :β => 0.01, :dS => 0.01, :dI => 1 // 100, :dR => 0.01] + ps_2 = [:α => 1//10, :β => 1//100, :dS => 1//100, :dI => 1//100, :dR => 1//100] + ps_3 = [:α => 1//10, :β => 0.01, :dS => 0.01, :dI => 1//100, :dR => 0.01] sol_base = solve(ODEProblem(dsrs, u0, (0.0, 100.0), ps_1), Rosenbrock23(); saveat = 0.1) for ps in [ps_1, ps_2, ps_3] for jac in [true, false], sparse in [true, false] @@ -72,19 +72,19 @@ let rs = @reaction_network begin pX, 0 --> X d, X --> 0 - pY * X, 0 --> Y + pY*X, 0 --> Y d, Y --> 0 end tr = @transport_reaction D X space = path_graph(3) - dsrs = DiscreteSpaceReactionSystem(rs, [tr], space) + dsrs = DiscreteSpaceReactionSystem(rs, [tr], space); - D_vals = spzeros(3, 3) - D_vals[1, 2] = 0.2; D_vals[2, 1] = 0.2 - D_vals[2, 3] = 0.3; D_vals[3, 2] = 0.3 + D_vals = spzeros(3,3) + D_vals[1,2] = 0.2; D_vals[2,1] = 0.2; + D_vals[2,3] = 0.3; D_vals[3,2] = 0.3; u0 = [:X => [1.0, 2.0, 3.0], :Y => 1.0] ps = [:pX => [2.0, 2.5, 3.0], :d => 0.1, :pY => 0.5, :D => D_vals] - oprob = ODEProblem(dsrs, u0, (0.0, 0.0), ps; jac = true, sparse = true) + oprob = ODEProblem(dsrs, u0, (0.0, 0.0), ps; jac=true, sparse=true) # Creates manual f and jac functions. function f_manual!(du, u, p, t) @@ -93,14 +93,14 @@ let pX1, pX2, pX3 = pX pY, = pY d, = d - D1 = D_vals[1, 2]; D2 = D_vals[2, 1] - D3 = D_vals[2, 3]; D4 = D_vals[3, 2] - du[1] = pX1 - d * X1 - D1 * X1 + D2 * X2 - du[2] = pY * X1 - d * Y1 - du[3] = pX2 - d * X2 + D1 * X1 - (D2 + D3) * X2 + D4 * X3 - du[4] = pY * X2 - d * Y2 - du[5] = pX3 - d * X3 + D3 * X2 - D4 * X3 - return du[6] = pY * X3 - d * Y3 + D1 = D_vals[1,2]; D2 = D_vals[2,1]; + D3 = D_vals[2,3]; D4 = D_vals[3,2]; + du[1] = pX1 - d*X1 - D1*X1 + D2*X2 + du[2] = pY*X1 - d*Y1 + du[3] = pX2 - d*X2 + D1*X1 - (D2+D3)*X2 + D4*X3 + du[4] = pY*X2 - d*Y2 + du[5] = pX3 - d*X3 + D3*X2 - D4*X3 + du[6] = pY*X3 - d*Y3 end function jac_manual!(J, u, p, t) X1, Y1, X2, Y2, X3, Y3 = u @@ -108,30 +108,30 @@ let pX1, pX2, pX3 = pX pY, = pY d, = d - D1 = D_vals[1, 2]; D2 = D_vals[2, 1] - D3 = D_vals[2, 3]; D4 = D_vals[3, 2] + D1 = D_vals[1,2]; D2 = D_vals[2,1]; + D3 = D_vals[2,3]; D4 = D_vals[3,2]; J .= 0.0 - J[1, 1] = - d - D1 - J[1, 2] = 0 - J[2, 1] = pY - J[2, 2] = - d - - J[3, 3] = - d - D2 - D3 - J[3, 4] = 0 - J[4, 3] = pY - J[4, 4] = - d - - J[5, 5] = - d - D4 - J[5, 6] = 0 - J[6, 5] = pY - J[6, 6] = - d - - J[1, 3] = D1 - J[3, 1] = D2 - J[3, 5] = D3 - return J[5, 3] = D4 + J[1,1] = - d - D1 + J[1,2] = 0 + J[2,1] = pY + J[2,2] = - d + + J[3,3] = - d - D2 - D3 + J[3,4] = 0 + J[4,3] = pY + J[4,4] = - d + + J[5,5] = - d - D4 + J[5,6] = 0 + J[6,5] = pY + J[6,6] = - d + + J[1,3] = D1 + J[3,1] = D2 + J[3,5] = D3 + J[5,3] = D4 end # Sets test input values. @@ -159,7 +159,7 @@ let u0 = [ :X => 1.0 .+ rand_v_vals(dsrs), :Y => 2.0 * rand_v_vals(dsrs), - :XY => 0.5, + :XY => 0.5 ] oprob = ODEProblem(dsrs, u0, (0.0, 1000.0), binding_p; tstops = 0.1:0.1:1000.0) ss = solve(oprob, Tsit5()).u[end] @@ -180,10 +180,10 @@ let oprob_jac = ODEProblem(dsrs, u0, (0.0, 5.0), [pV; pE]; jac = true, sparse = false) oprob_sparse_jac = ODEProblem(dsrs, u0, (0.0, 5.0), [pV; pE]; jac = true, sparse = true) - ss = solve(oprob, Rosenbrock23(); abstol = 1.0e-10, reltol = 1.0e-10).u[end] - @test all(isapprox.(ss, solve(oprob_sparse, Rosenbrock23(); abstol = 1.0e-10, reltol = 1.0e-10).u[end]; rtol = 0.0001)) - @test all(isapprox.(ss, solve(oprob_jac, Rosenbrock23(); abstol = 1.0e-10, reltol = 1.0e-10).u[end]; rtol = 0.0001)) - @test all(isapprox.(ss, solve(oprob_sparse_jac, Rosenbrock23(); abstol = 1.0e-10, reltol = 1.0e-10).u[end]; rtol = 0.0001)) + ss = solve(oprob, Rosenbrock23(); abstol = 1e-10, reltol = 1e-10).u[end] + @test all(isapprox.(ss, solve(oprob_sparse, Rosenbrock23(); abstol = 1e-10, reltol = 1e-10).u[end]; rtol = 0.0001)) + @test all(isapprox.(ss, solve(oprob_jac, Rosenbrock23(); abstol = 1e-10, reltol = 1e-10).u[end]; rtol = 0.0001)) + @test all(isapprox.(ss, solve(oprob_sparse_jac, Rosenbrock23(); abstol = 1e-10, reltol = 1e-10).u[end]; rtol = 0.0001)) end # Compares Catalyst-generated to hand-written one for the Brusselator for a line of cells. @@ -201,7 +201,6 @@ let for i in 3:2:(length(u) - 3) du[i] += p[3] * (u[i - 2] + u[i + 2] - 2u[i]) end - return end function spatial_brusselator_jac(J, u, p, t) J .= 0 @@ -223,7 +222,6 @@ let J[i, i - 2] += p[3] J[i, i + 2] += p[3] end - return end function spatial_brusselator_jac_sparse(J, u, p, t) # Spatial @@ -244,7 +242,7 @@ let J.nzval[end - 3] = u[end - 1] * u[end] - 1 - p[end - 1] J.nzval[end - 2] = 0.5 * (u[end - 1]^2) J.nzval[end - 1] = p[2] - u[end - 1] * u[end] - return J.nzval[end] = -0.5 * (u[end - 1]^2) + J.nzval[end] = -0.5 * (u[end - 1]^2) end function make_jac_prototype(u0) jac_prototype_pre = zeros(length(u0), length(u0)) @@ -262,15 +260,13 @@ let end num_verts = 100 - u0 = 2 * rand(rng, 2 * num_verts) + u0 = 2 * rand(rng, 2*num_verts) p = [1.0, 4.0, 0.1] tspan = (0.0, 100.0) ofun_hw_dense = ODEFunction(spatial_brusselator_f; jac = spatial_brusselator_jac) - ofun_hw_sparse = ODEFunction( - spatial_brusselator_f; jac = spatial_brusselator_jac, - jac_prototype = make_jac_prototype(u0) - ) + ofun_hw_sparse = ODEFunction(spatial_brusselator_f; jac = spatial_brusselator_jac, + jac_prototype = make_jac_prototype(u0)) dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, path_graph(num_verts)) u0_map = [:X => u0[1:2:(end - 1)], :Y => u0[2:2:end]] @@ -311,7 +307,7 @@ end ### Test Grid Types ### # Tests that identical spaces (using different types of spaces) give identical results. -let +let # Declares the diffusion parameters. sigmaB_p_spat = [:DσB => 0.05, :Dw => 0.04, :Dv => 0.03] @@ -320,9 +316,9 @@ let dsrs1_masked = DiscreteSpaceReactionSystem(sigmaB_system, sigmaB_srs_2, very_small_1d_masked_grid) dsrs1_graph = DiscreteSpaceReactionSystem(sigmaB_system, sigmaB_srs_2, very_small_1d_graph_grid) - oprob1_cartesian = ODEProblem(dsrs1_cartesian, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) - oprob1_masked = ODEProblem(dsrs1_masked, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) - oprob1_graph = ODEProblem(dsrs1_graph, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) + oprob1_cartesian = ODEProblem(dsrs1_cartesian, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) + oprob1_masked = ODEProblem(dsrs1_masked, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) + oprob1_graph = ODEProblem(dsrs1_graph, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) @test solve(oprob1_cartesian, QNDF()) ≈ solve(oprob1_masked, QNDF()) ≈ solve(oprob1_graph, QNDF()) # 2d spaces. @@ -330,9 +326,9 @@ let dsrs2_masked = DiscreteSpaceReactionSystem(sigmaB_system, sigmaB_srs_2, very_small_2d_masked_grid) dsrs2_graph = DiscreteSpaceReactionSystem(sigmaB_system, sigmaB_srs_2, very_small_2d_graph_grid) - oprob2_cartesian = ODEProblem(dsrs2_cartesian, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) - oprob2_masked = ODEProblem(dsrs2_masked, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) - oprob2_graph = ODEProblem(dsrs2_graph, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) + oprob2_cartesian = ODEProblem(dsrs2_cartesian, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) + oprob2_masked = ODEProblem(dsrs2_masked, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) + oprob2_graph = ODEProblem(dsrs2_graph, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) @test solve(oprob2_cartesian, QNDF()) ≈ solve(oprob2_masked, QNDF()) ≈ solve(oprob2_graph, QNDF()) # 3d spaces. @@ -340,31 +336,31 @@ let dsrs3_masked = DiscreteSpaceReactionSystem(sigmaB_system, sigmaB_srs_2, very_small_3d_masked_grid) dsrs3_graph = DiscreteSpaceReactionSystem(sigmaB_system, sigmaB_srs_2, very_small_3d_graph_grid) - oprob3_cartesian = ODEProblem(dsrs3_cartesian, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) - oprob3_masked = ODEProblem(dsrs3_masked, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) - oprob3_graph = ODEProblem(dsrs3_graph, sigmaB_u0, (0.0, 1.0), [sigmaB_p; sigmaB_p_spat]) + oprob3_cartesian = ODEProblem(dsrs3_cartesian, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) + oprob3_masked = ODEProblem(dsrs3_masked, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) + oprob3_graph = ODEProblem(dsrs3_graph, sigmaB_u0, (0.0,1.0), [sigmaB_p; sigmaB_p_spat]) @test solve(oprob3_cartesian, QNDF()) ≈ solve(oprob3_masked, QNDF()) ≈ solve(oprob3_graph, QNDF()) end # Tests that input parameter and u0 values can be given using different types of input for 2d spaces. -# Tries both for cartesian and masked (where all vertices are `true`). +# Tries both for cartesian and masked (where all vertices are `true`). # Tries for Vector, Tuple, and Dictionary inputs. let - for space in [CartesianGrid((4, 3)), fill(true, 4, 3)] + for space in [CartesianGrid((4,3)), fill(true, 4, 3)] dsrs = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_1, space) # Initial condition values. - S_vals_vec = [100.0, 100.0, 200.0, 300.0, 200.0, 100.0, 200.0, 300.0, 300.0, 100.0, 200.0, 300.0] - S_vals_mat = [100.0 200.0 300.0; 100.0 100.0 100.0; 200.0 200.0 200.0; 300.0 300.0 300.0] + S_vals_vec = [100., 100., 200., 300., 200., 100., 200., 300., 300., 100., 200., 300.] + S_vals_mat = [100. 200. 300.; 100. 100. 100.; 200. 200. 200.; 300. 300. 300.] SIR_u0_vec = [:S => S_vals_vec, :I => 1.0, :R => 0.0] SIR_u0_mat = [:S => S_vals_mat, :I => 1.0, :R => 0.0] - + # Parameter values. β_vals_vec = [0.01, 0.01, 0.02, 0.03, 0.02, 0.01, 0.02, 0.03, 0.03, 0.01, 0.02, 0.03] β_vals_mat = [0.01 0.02 0.03; 0.01 0.01 0.01; 0.02 0.02 0.02; 0.03 0.03 0.03] SIR_p_vec = [:α => 0.1 / 1000, :β => β_vals_vec, :dS => 0.01] SIR_p_mat = [:α => 0.1 / 1000, :β => β_vals_mat, :dS => 0.01] - + oprob = ODEProblem(dsrs, SIR_u0_vec, (0.0, 10.0), SIR_p_vec) sol_base = solve(oprob, Tsit5()) for u0_base in [SIR_u0_vec, SIR_u0_mat], ps_base in [SIR_p_vec, SIR_p_mat] @@ -381,7 +377,7 @@ end let space = [true true false; true false false; true true true; false true true] dsrs = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_1, space) - + # Initial condition values. 999 is used for empty points. S_vals_vec = [100.0, 100.0, 200.0, 200.0, 200.0, 300.0, 200.0, 300.0] S_vals_mat = [100.0 200.0 999.0; 100.0 999.0 999.0; 200.0 200.0 200.0; 999.0 300.0 300.0] @@ -389,15 +385,15 @@ let SIR_u0_vec = [:S => S_vals_vec, :I => 1.0, :R => 0.0] SIR_u0_mat = [:S => S_vals_mat, :I => 1.0, :R => 0.0] SIR_u0_sparse_mat = [:S => S_vals_sparse_mat, :I => 1.0, :R => 0.0] - - # Parameter values. 9.99 is used for empty points. + + # Parameter values. 9.99 is used for empty points. β_vals_vec = [0.01, 0.01, 0.02, 0.02, 0.02, 0.03, 0.02, 0.03] β_vals_mat = [0.01 0.02 9.99; 0.01 9.99 9.99; 0.02 0.02 0.02; 9.99 0.03 0.03] β_vals_sparse_mat = sparse(β_vals_mat .* space) SIR_p_vec = [:α => 0.1 / 1000, :β => β_vals_vec, :dS => 0.01] SIR_p_mat = [:α => 0.1 / 1000, :β => β_vals_mat, :dS => 0.01] SIR_p_sparse_mat = [:α => 0.1 / 1000, :β => β_vals_sparse_mat, :dS => 0.01] - + oprob = ODEProblem(dsrs, SIR_u0_vec, (0.0, 10.0), SIR_p_vec) sol = solve(oprob, Tsit5()) for u0 in [SIR_u0_vec, SIR_u0_mat, SIR_u0_sparse_mat] @@ -429,19 +425,19 @@ let end # Tries non-trivial diffusion rates. -let - SIR_tr_S_alt = @transport_reaction dS1 + dS2 S - SIR_tr_I_alt = @transport_reaction dI1 * dI2 I - SIR_tr_R_alt = @transport_reaction log(dR1) + dR2 R +let + SIR_tr_S_alt = @transport_reaction dS1+dS2 S + SIR_tr_I_alt = @transport_reaction dI1*dI2 I + SIR_tr_R_alt = @transport_reaction log(dR1)+dR2 R SIR_srs_2_alt = [SIR_tr_S_alt, SIR_tr_I_alt, SIR_tr_R_alt] dsrs_1 = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_2, small_2d_graph_grid) dsrs_2 = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_2_alt, small_2d_graph_grid) - + u0 = [:S => 990.0, :I => 20.0 * rand_v_vals(dsrs_1), :R => 0.0] pV = [:α => 0.1 / 1000, :β => 0.01] pE_1 = [:dS => 0.01, :dI => 0.01, :dR => 0.01] pE_2 = [:dS1 => 0.003, :dS2 => 0.007, :dI1 => 2, :dI2 => 0.005, :dR1 => 1.010050167084168, :dR2 => 1.0755285551056204e-16] - + ss_1 = solve(ODEProblem(dsrs_1, u0, (0.0, 500.0), [pV; pE_1]), Tsit5()).u[end] ss_2 = solve(ODEProblem(dsrs_2, u0, (0.0, 500.0), [pV; pE_2]), Tsit5()).u[end] @test ss_1 == ss_2 @@ -451,7 +447,7 @@ end let CuH_Amination_system_alt_1 = @reaction_network begin @species Newspecies1(t) Newspecies2(t) - @parameters dCuoAc [edgeparameter = true] dLigand dSilane dStyrene dCu_ELigand + @parameters dCuoAc [edgeparameter=true] dLigand dSilane dStyrene dCu_ELigand 10.0^kp1, CuoAc + Ligand --> CuoAcLigand 10.0^kp2, CuoAcLigand + Silane --> CuHLigand + SilaneOAc 10.0^k1, CuHLigand + Styrene --> AlkylCuLigand @@ -464,7 +460,7 @@ let end @unpack dLigand, dSilane, Silane = CuH_Amination_system_alt_1 @parameters dAmine_E dNewspecies1 - @species Ligand(t) Amine_E(t) Newspecies1(t) + @species Ligand(t) Amine_E(t) Newspecies1(t) tr_alt_1_1 = TransportReaction(dLigand, Ligand) tr_alt_1_2 = TransportReaction(dSilane, Silane) tr_alt_1_3 = TransportReaction(dAmine_E, Amine_E) @@ -477,7 +473,7 @@ let CuH_Amination_system_alt_2 = @reaction_network begin @species Newspecies1(t) Newspecies2(t) - @parameters dCuoAc [edgeparameter = true] dLigand dSilane dStyrene dCu_ELigand + @parameters dCuoAc [edgeparameter=true] dLigand dSilane dStyrene dCu_ELigand 10.0^kp1, CuoAc + Ligand --> CuoAcLigand 10.0^kp2, CuoAcLigand + Silane --> CuHLigand + SilaneOAc 10.0^k1, CuHLigand + Styrene --> AlkylCuLigand @@ -488,9 +484,9 @@ let 10.0^kam, CuHLigand + Amine_E --> Amine + Cu_ELigand 10.0^kdc, CuHLigand + CuHLigand --> Decomposition end - @unpack Decomposition, dCu_ELigand, Cu_ELigand = CuH_Amination_system_alt_2 + @unpack Decomposition, dCu_ELigand, Cu_ELigand = CuH_Amination_system_alt_2 @parameters dNewspecies2 dDecomposition - @species Newspecies2(t) + @species Newspecies2(t) tr_alt_2_1 = @transport_reaction dLigand Ligand tr_alt_2_2 = @transport_reaction dSilane Silane tr_alt_2_3 = @transport_reaction dAmine_E Amine_E @@ -500,10 +496,10 @@ let tr_alt_2_7 = TransportReaction(dNewspecies2, Newspecies2) CuH_Amination_srs_alt_2 = [tr_alt_2_1, tr_alt_2_2, tr_alt_2_3, tr_alt_2_4, tr_alt_2_5, tr_alt_2_6, tr_alt_2_7] dsrs_2 = DiscreteSpaceReactionSystem(CuH_Amination_system_alt_2, CuH_Amination_srs_alt_2, small_2d_graph_grid) - + u0 = [CuH_Amination_u0; :Newspecies1 => 0.1; :Newspecies2 => 0.1] - pV = [CuH_Amination_p; :dLigand => 0.01; :dSilane => 0.01; :dCu_ELigand => 0.009; :dStyrene => -10000.0] - pE = [:dAmine_E => 0.011, :dNewspecies1 => 0.013, :dDecomposition => 0.015, :dNewspecies2 => 0.016, :dCuoAc => -10000.0] + pV = [CuH_Amination_p; :dLigand => 0.01; :dSilane => 0.01; :dCu_ELigand => 0.009; :dStyrene => -10000.0] + pE = [:dAmine_E => 0.011, :dNewspecies1 => 0.013, :dDecomposition => 0.015, :dNewspecies2 => 0.016, :dCuoAc => -10000.0] ss_1 = solve(ODEProblem(dsrs_1, u0, (0.0, 500.0), [pV; pE]), Tsit5()).u[end] ss_2 = solve(ODEProblem(dsrs_2, u0, (0.0, 500.0), [pV; pE]), Tsit5()).u[end] @@ -518,7 +514,7 @@ let dsrs_graph = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_2, complete_graph(3)) u0 = [:S => 990.0, :I => 20.0 * rand_v_vals(dsrs_digraph), :R => 0.0] pV = SIR_p - pE = [:dS => 0.1, :dI => 0.01, :dR => 0.01] + pE = [:dS => 0.10, :dI => 0.01, :dR => 0.01] oprob_digraph = ODEProblem(dsrs_digraph, u0, (0.0, 500.0), [pV; pE]) oprob_graph = ODEProblem(dsrs_graph, u0, (0.0, 500.0), [pV; pE]) @@ -559,18 +555,18 @@ let :p2 => rand_v_vals(dsrs_alt), ] oprob_alt = ODEProblem(dsrs_alt, u0_alt, (0.0, 10.0), p_alt) - ss_alt = solve(oprob_alt, Tsit5(); abstol = 1.0e-9, reltol = 1.0e-9).u[end] - + ss_alt = solve(oprob_alt, Tsit5(); abstol=1e-9, reltol=1e-9).u[end] + binding_srs_main = [TransportReaction(dX, X), TransportReaction(dXY, XY)] dsrs = DiscreteSpaceReactionSystem(binding_system, binding_srs_main, small_2d_graph_grid) u0 = u0_alt[1:3] p = p_alt[1:4] oprob = ODEProblem(dsrs, u0, (0.0, 10.0), p) - ss = solve(oprob, Tsit5(); abstol = 1.0e-9, reltol = 1.0e-9).u[end] - + ss = solve(oprob, Tsit5(); abstol=1e-9, reltol=1e-9).u[end] + i = 3 ss_alt[((i - 1) * 6 + 1):((i - 1) * 6 + 3)] ≈ ss[((i - 1) * 3 + 1):((i - 1) * 3 + 3)] - + for i in 1:25 @test ss_alt[((i - 1) * 6 + 1):((i - 1) * 6 + 3)] ≈ ss[((i - 1) * 3 + 1):((i - 1) * 3 + 3)] end @@ -583,8 +579,8 @@ let dsrs = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_2, very_small_2d_cartesian_grid) u0 = [:S => 990.0, :I => rand_v_vals(dsrs), :R => 0.0] ps_1 = [:α => 0.1, :β => 0.01, :dS => 0.01, :dI => 0.01, :dR => 0.01] - ps_2 = [:α => 1 // 10, :β => 1 // 100, :dS => 1 // 100, :dI => 1 // 100, :dR => 1 // 100] - ps_3 = [:α => 1 // 10, :β => 0.01, :dS => 0.01, :dI => 1 // 100, :dR => 0.01] + ps_2 = [:α => 1//10, :β => 1//100, :dS => 1//100, :dI => 1//100, :dR => 1//100] + ps_3 = [:α => 1//10, :β => 0.01, :dS => 0.01, :dI => 1//100, :dR => 0.01] sol_base = solve(ODEProblem(dsrs, u0, (0.0, 100.0), ps_1), Rosenbrock23(); saveat = 0.1) for ps in [ps_1, ps_2, ps_3] for jac in [true, false], sparse in [true, false] @@ -600,19 +596,19 @@ let u0_Int64 = [:X => 2, :Y => [1, 1, 1, 2]] u0_Float64 = [:X => 2.0, :Y => [1.0, 1.0, 1.0, 2.0]] u0_Int32 = [:X => Int32(2), :Y => Int32.([1, 1, 1, 2])] - u0_Any = Pair{Symbol, Any}[:X => 2.0, :Y => [1.0, 1.0, 1.0, 2.0]] + u0_Any = Pair{Symbol,Any}[:X => 2.0, :Y => [1.0, 1.0, 1.0, 2.0]] u0s = (u0_Int64, u0_Float64, u0_Int32, u0_Any) # Declare parameter versions. - dY_vals = spzeros(4, 4) - dY_vals[1, 2] = 1; dY_vals[2, 1] = 1 - dY_vals[1, 3] = 1; dY_vals[3, 1] = 1 - dY_vals[2, 4] = 1; dY_vals[4, 2] = 1 - dY_vals[3, 4] = 2; dY_vals[4, 3] = 2 + dY_vals = spzeros(4,4) + dY_vals[1,2] = 1; dY_vals[2,1] = 1; + dY_vals[1,3] = 1; dY_vals[3,1] = 1; + dY_vals[2,4] = 1; dY_vals[4,2] = 1; + dY_vals[3,4] = 2; dY_vals[4,3] = 2; p_Int64 = (:A => [1, 1, 1, 2], :B => 4, :dX => 1, :dY => Int64.(dY_vals)) p_Float64 = (:A => [1.0, 1.0, 1.0, 2.0], :B => 4.0, :dX => 1.0, :dY => Float64.(dY_vals)) p_Int32 = (:A => Int32.([1, 1, 1, 2]), :B => Int32(4), :dX => Int32(1), :dY => Int32.(dY_vals)) - p_Any = Pair{Symbol, Any}[:A => [1.0, 1.0, 1.0, 2.0], :B => 4.0, :dX => 1.0, :dY => dY_vals] + p_Any = Pair{Symbol,Any}[:A => [1.0, 1.0, 1.0, 2.0], :B => 4.0, :dX => 1.0, :dY => dY_vals] ps = (p_Int64, p_Float64, p_Int32, p_Any) # Creates a base solution to compare all solution to. @@ -626,7 +622,7 @@ let for u0 in [u0_base, Tuple(u0_base), Dict(u0_base)], p in [p_base, Dict(p_base)] oprob = ODEProblem(dsrs, u0, (0.0, 1.0), p; sparse = true, jac = true) sol = solve(oprob, QNDF(); saveat = 0.01) - @test sol.u ≈ sol_base.u atol = 1.0e-6 rtol = 1.0e-6 + @test sol.u ≈ sol_base.u atol = 1e-6 rtol = 1e-6 end end end @@ -643,7 +639,7 @@ end # Checks that various erroneous inputs to `ODEProblem` yields errors. let # Create `DiscreteSpaceReactionSystem`. - @parameters d1 d2 D [edgeparameter = true] + @parameters d1 d2 D [edgeparameter=true] @species X1(t) X2(t) rxs = [Reaction(d1, [X1], [])] @named rs = ReactionSystem(rxs, t) @@ -668,5 +664,5 @@ let @test_throws ArgumentError ODEProblem(dsrs, u0, tspan, [d1 => 1.0, D => bad_D_vals_2]) @test_throws ArgumentError ODEProblem(dsrs, u0, tspan, [d1 => 1.0]) @test_throws ArgumentError ODEProblem(dsrs, [X1 => [1.0, 2.0, 3.0]], tspan, ps) - @test_throws ArgumentError ODEProblem(dsrs, [X1 => ones(3, 1)], tspan, ps) + @test_throws ArgumentError ODEProblem(dsrs, [X1 => ones(3,1)], tspan, ps) end diff --git a/test/spatial_modelling/dspace_reaction_systems_jumps.jl b/test/spatial_modelling/dspace_reaction_systems_jumps.jl index fa655ed6c9..0dd2bd34ac 100644 --- a/test/spatial_modelling/dspace_reaction_systems_jumps.jl +++ b/test/spatial_modelling/dspace_reaction_systems_jumps.jl @@ -30,7 +30,7 @@ let ] for pV in [pV_1, pV_2, pV_3] pE_1 = [sp => 0.01 for sp in spatial_param_syms(dsrs)] - pE_2 = [sp => rand_e_vals(dsrs) / 50.0 for sp in spatial_param_syms(dsrs)] + pE_2 = [sp => rand_e_vals(dsrs)/50.0 for sp in spatial_param_syms(dsrs)] for pE in [pE_1, pE_2] isempty(spatial_param_syms(dsrs)) && (pE = Vector{Pair{Symbol, Float64}}()) jprob = JumpProblem(dsrs, u0, (0.0, 1.0), [pV; pE]) @@ -54,7 +54,7 @@ let # Prepares various u0 input types. u0_1 = [:I => 2.0, :S => 1.0, :R => 3.0] - u0_2 = [:I => fill(2.0, nv(grid)), :S => 1.0, :R => 3.0] + u0_2 = [:I => fill(2., nv(grid)), :S => 1.0, :R => 3.0] # Prepare various (compartment) parameter input types. pV_1 = [:β => 0.2, :α => 0.1] @@ -68,7 +68,7 @@ let # Checks hopping rates and u0 are correct. true_u0 = [fill(1.0, 1, 25); fill(2.0, 1, 25); fill(3.0, 1, 25)] - true_hopping_rates = cumsum.([fill(dval, length(v)) for dval in [0.01, 0.02, 0.03], v in grid.fadjlist]) + true_hopping_rates = cumsum.([fill(dval, length(v)) for dval in [0.01,0.02,0.03], v in grid.fadjlist]) true_maj_scaled_rates = [0.1, 0.2] true_maj_reactant_stoch = [[1 => 1, 2 => 1], [2 => 1]] true_maj_net_stoch = [[1 => -1, 2 => 1], [2 => -1, 3 => 1]] @@ -77,7 +77,7 @@ let jprob = JumpProblem(dsrs, u0, (0.0, 100.0), [pE; pV]) @test jprob.prob.u0 == true_u0 @test jprob.discrete_jump_aggregation.hop_rates.hop_const_cumulative_sums == true_hopping_rates - @test jprob.massaction_jump.reactant_stoch == true_maj_reactant_stoch + @test jprob.massaction_jump.reactant_stoch == true_maj_reactant_stoch @test all(issetequal(ns1, ns2) for (ns1, ns2) in zip(jprob.massaction_jump.net_stoch, true_maj_net_stoch)) end end @@ -87,7 +87,7 @@ end ### SpatialMassActionJump Testing ### # Checks that the correct structures are produced. -let +let # Network for reference: # A, ∅ → X # 1, 2X + Y → 3X @@ -104,8 +104,8 @@ let jprob = JumpProblem(dsrs, u0, tspan, ps) # Checks internal structures. - jprob.massaction_jump.uniform_rates == [1.0, 0.5, 10.0] # 0.5 is due to combinatoric /2! in (2X + Y). - jprob.massaction_jump.spatial_rates[1, :] == ps[2][2] + jprob.massaction_jump.uniform_rates == [1.0, 0.5 ,10.] # 0.5 is due to combinatoric /2! in (2X + Y). + jprob.massaction_jump.spatial_rates[1,:] == ps[2][2] # Test when new SII functions are ready, or we implement them in Catalyst. # @test isequal(to_int(getfield.(reactions(reactionsystem(dsrs)), :netstoich)), jprob.massaction_jump.net_stoch) # @test isequal(to_int(Pair.(getfield.(reactions(reactionsystem(dsrs)), :substrates),getfield.(reactions(reactionsystem(dsrs)), :substoich))), jprob.massaction_jump.net_stoch) @@ -116,24 +116,24 @@ end # Checks that heterogeneous vertex parameters work. Checks that birth-death system with different # birth rates produce different means. -let +let # Create model. birth_death_network = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X end srs = [(@transport_reaction D X)] dsrs = DiscreteSpaceReactionSystem(birth_death_network, srs, very_small_2d_graph_grid) - + # Create JumpProblem. u0 = [:X => 1] tspan = (0.0, 100.0) - ps = [:p => [0.1, 1.0, 10.0, 100.0], :d => 1.0, :D => 0.0] + ps = [:p => [0.1, 1.0, 10.0, 100.0], :d => 1.0, :D => 0.0] jprob = JumpProblem(dsrs, u0, tspan, ps) # Simulate model (a few repeats to ensure things don't succeed by change for uniform rates). # Check that higher p gives higher mean. - for i in 1:5 - sol = solve(jprob, SSAStepper(); saveat = 1.0) + for i = 1:5 + sol = solve(jprob, SSAStepper(); saveat = 1.) @test mean(getindex.(sol.u, 1)) < mean(getindex.(sol.u, 2)) < mean(getindex.(sol.u, 3)) < mean(getindex.(sol.u, 4)) end end @@ -142,7 +142,7 @@ end ### Tests taken from JumpProcesses ### # ABC Model Test -let +let # Preparations (stuff used in JumpProcesses examples ported over here, could be written directly into code). Nsims = 100 reltol = 0.05 @@ -159,7 +159,7 @@ let # Make model. rn = @reaction_network begin - (kB, kD), A + B <--> C + (kB,kD), A + B <--> C end tr_1 = @transport_reaction D A tr_2 = @transport_reaction D B @@ -168,17 +168,13 @@ let dsrs = DiscreteSpaceReactionSystem(rn, [tr_1, tr_2, tr_3], space) # Set simulation parameters and create problems. - u0 = [:A => [0, 0, 500, 0, 0], :B => [0, 0, 500, 0, 0], :C => 0] + u0 = [:A => [0,0,500,0,0], :B => [0,0,500,0,0], :C => 0] tspan = (0.0, 10.0) pV = [:kB => rates[1], :kD => rates[2]] pE = [:D => diffusivity] # NRM could be added, but doesn't work. Might need Cartesian grid. - jump_problems = [ - JumpProblem( - dsrs, u0, tspan, [pV; pE]; aggregator = alg(), - save_positions = (false, false) - ) for alg in [NSM, DirectCRDirect] - ] + jump_problems = [JumpProblem(dsrs, u0, tspan, [pV; pE]; aggregator = alg(), + save_positions = (false, false)) for alg in [NSM, DirectCRDirect]] # Run tests. function get_mean_end_state(jump_prob, Nsims) @@ -187,7 +183,7 @@ let sol = solve(jump_prob, SSAStepper()) end_state .+= sol.u[end] end - return end_state / Nsims + end_state / Nsims end for jprob in jump_problems solution = solve(jprob, SSAStepper()) @@ -198,4 +194,4 @@ let @test abs(d) < reltol * non_spatial_mean[i] end end -end +end \ No newline at end of file diff --git a/test/spatial_modelling/dspace_reaction_systems_space_types.jl b/test/spatial_modelling/dspace_reaction_systems_space_types.jl index 32ca1f8afc..e6cc8879f5 100644 --- a/test/spatial_modelling/dspace_reaction_systems_space_types.jl +++ b/test/spatial_modelling/dspace_reaction_systems_space_types.jl @@ -10,13 +10,13 @@ include("../spatial_test_networks.jl") ### Run Tests ### # Test errors when attempting to create networks with dimensions > 3. -let +let @test_throws Exception DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, CartesianGrid((5, 5, 5, 5))) @test_throws Exception DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, fill(true, 5, 5, 5, 5)) end # Checks that getter functions give the correct output. -let +let # Create DiscreteSpaceReactionSystems. cartesian_1d_dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, small_1d_cartesian_grid) cartesian_2d_dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, small_2d_cartesian_grid) @@ -54,23 +54,23 @@ let # Checks grid sizes. @test grid_size(cartesian_1d_dsrs) == (5,) - @test grid_size(cartesian_2d_dsrs) == (5, 5) - @test grid_size(cartesian_3d_dsrs) == (5, 5, 5) + @test grid_size(cartesian_2d_dsrs) == (5,5) + @test grid_size(cartesian_3d_dsrs) == (5,5,5) @test grid_size(masked_1d_dsrs) == (5,) - @test grid_size(masked_2d_dsrs) == (5, 5) - @test grid_size(masked_3d_dsrs) == (5, 5, 5) + @test grid_size(masked_2d_dsrs) == (5,5) + @test grid_size(masked_3d_dsrs) == (5,5,5) @test_throws ArgumentError grid_size(graph_dsrs) end # Checks grid dimensions for 2d and 3d grids where some dimension is equal to 1. let # Creates DiscreteSpaceReactionSystems - cartesian_2d_dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, CartesianGrid((1, 5))) - cartesian_3d_dsrs_1 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, CartesianGrid((1, 5, 5))) - cartesian_3d_dsrs_2 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, CartesianGrid((1, 1, 5))) + cartesian_2d_dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, CartesianGrid((1,5))) + cartesian_3d_dsrs_1 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, CartesianGrid((1,5,5))) + cartesian_3d_dsrs_2 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, CartesianGrid((1,1,5))) masked_2d_dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, fill(true, 1, 5)) - masked_3d_dsrs_1 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, fill(true, 1, 5, 5)) - masked_3d_dsrs_2 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, fill(true, 1, 1, 5)) + masked_3d_dsrs_1 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, fill(true, 1, 5,5)) + masked_3d_dsrs_2 = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, fill(true, 1, 1,5)) # Check grid dimensions. @test grid_dims(cartesian_2d_dsrs) == 2 @@ -83,7 +83,7 @@ end # Checks that some grids, created using different approaches, generates the same spatial structures. # Checks that some grids, created using different approaches, generates the same simulation output. -let +let # Create DiscreteSpaceReactionSystems. cartesian_grid = CartesianGrid((5, 5)) masked_grid = fill(true, 5, 5) @@ -100,8 +100,8 @@ let @test num_verts(cartesian_dsrs) == num_verts(masked_dsrs) == num_verts(graph_dsrs) @test num_edges(cartesian_dsrs) == num_edges(masked_dsrs) == num_edges(graph_dsrs) @test num_species(cartesian_dsrs) == num_species(masked_dsrs) == num_species(graph_dsrs) - @test isequal(spatial_species(cartesian_dsrs), spatial_species(masked_dsrs)) - @test isequal(spatial_species(masked_dsrs), spatial_species(graph_dsrs)) + @test isequal(spatial_species(cartesian_dsrs), spatial_species(masked_dsrs)) + @test isequal(spatial_species(masked_dsrs), spatial_species(graph_dsrs)) @test isequal(parameters(cartesian_dsrs), parameters(masked_dsrs)) @test isequal(parameters(masked_dsrs), parameters(graph_dsrs)) @test isequal(vertex_parameters(cartesian_dsrs), vertex_parameters(masked_dsrs)) @@ -124,9 +124,9 @@ let masked_oprob = ODEProblem(masked_dsrs, u0_masked, (0.0, 100.0), [pV_masked; pE]) graph_oprob = ODEProblem(graph_dsrs, u0_graph, (0.0, 100.0), [pV_graph; pE]) - cartesian_sol = solve(cartesian_oprob, FBDF(); saveat = 0.1) - masked_sol = solve(masked_oprob, FBDF(); saveat = 0.1) - graph_sol = solve(graph_oprob, FBDF(); saveat = 0.1) + cartesian_sol = solve(cartesian_oprob, FBDF(); saveat=0.1) + masked_sol = solve(masked_oprob, FBDF(); saveat=0.1) + graph_sol = solve(graph_oprob, FBDF(); saveat=0.1) @test cartesian_sol.u == masked_sol.u == graph_sol.u end @@ -136,41 +136,41 @@ let # Create DiscreteSpaceReactionSystems. masked_grid = [true true true; true false true; true true true] graph_grid = SimpleGraph(8) - add_edge!(graph_grid, 1, 2); add_edge!(graph_grid, 2, 1) - add_edge!(graph_grid, 2, 3); add_edge!(graph_grid, 3, 2) - add_edge!(graph_grid, 3, 5); add_edge!(graph_grid, 5, 3) - add_edge!(graph_grid, 5, 8); add_edge!(graph_grid, 8, 5) - add_edge!(graph_grid, 8, 7); add_edge!(graph_grid, 7, 8) - add_edge!(graph_grid, 7, 6); add_edge!(graph_grid, 6, 7) - add_edge!(graph_grid, 6, 4); add_edge!(graph_grid, 4, 6) - add_edge!(graph_grid, 4, 1); add_edge!(graph_grid, 1, 4) + add_edge!(graph_grid, 1, 2); add_edge!(graph_grid, 2, 1); + add_edge!(graph_grid, 2, 3); add_edge!(graph_grid, 3, 2); + add_edge!(graph_grid, 3, 5); add_edge!(graph_grid, 5, 3); + add_edge!(graph_grid, 5, 8); add_edge!(graph_grid, 8, 5); + add_edge!(graph_grid, 8, 7); add_edge!(graph_grid, 7, 8); + add_edge!(graph_grid, 7, 6); add_edge!(graph_grid, 6, 7); + add_edge!(graph_grid, 6, 4); add_edge!(graph_grid, 4, 6); + add_edge!(graph_grid, 4, 1); add_edge!(graph_grid, 1, 4); masked_dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, masked_grid) graph_dsrs = DiscreteSpaceReactionSystem(brusselator_system, brusselator_srs_1, graph_grid) - + # Check internal structures. @test num_verts(masked_dsrs) == num_verts(graph_dsrs) @test num_edges(masked_dsrs) == num_edges(graph_dsrs) @test issetequal(edge_iterator(masked_dsrs), edge_iterator(graph_dsrs)) - + # Checks that simulations yields the same output. - u0_masked_grid = [:X => [1.0 4.0 6.0; 2.0 0.0 7.0; 3.0 5.0 8.0], :Y => 2.0] - u0_graph_grid = [:X => [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], :Y => 2.0] - pV_masked_grid = [:A => 0.5 .+ [1.0 4.0 6.0; 2.0 0.0 7.0; 3.0 5.0 8.0], :B => 4.0] - pV_graph_grid = [:A => 0.5 .+ [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], :B => 4.0] + u0_masked_grid = [:X => [1. 4. 6.; 2. 0. 7.; 3. 5. 8.], :Y => 2.0] + u0_graph_grid = [:X => [1., 2., 3., 4., 5., 6., 7., 8.], :Y => 2.0] + pV_masked_grid = [:A => 0.5 .+ [1. 4. 6.; 2. 0. 7.; 3. 5. 8.], :B => 4.0] + pV_graph_grid = [:A => 0.5 .+ [1., 2., 3., 4., 5., 6., 7., 8.], :B => 4.0] pE = [:dX => 0.2] - + base_oprob = ODEProblem(masked_dsrs, u0_masked_grid, (0.0, 100.0), [pV_masked_grid; pE]) - base_osol = solve(base_oprob, QNDF(); saveat = 0.1, abstol = 1.0e-9, reltol = 1.0e-9) + base_osol = solve(base_oprob, QNDF(); saveat=0.1, abstol=1e-9, reltol=1e-9) for jac in [false, true], sparse in [false, true] masked_oprob = ODEProblem(masked_dsrs, u0_masked_grid, (0.0, 100.0), [pV_masked_grid; pE]; jac, sparse) graph_oprob = ODEProblem(graph_dsrs, u0_graph_grid, (0.0, 100.0), [pV_graph_grid; pE]; jac, sparse) - masked_sol = solve(masked_oprob, QNDF(); saveat = 0.1, abstol = 1.0e-9, reltol = 1.0e-9) - graph_sol = solve(graph_oprob, QNDF(); saveat = 0.1, abstol = 1.0e-9, reltol = 1.0e-9) - @test base_osol ≈ masked_sol atol = 1.0e-6 rtol = 1.0e-6 - @test base_osol ≈ graph_sol atol = 1.0e-6 rtol = 1.0e-6 - @test masked_sol ≈ graph_sol atol = 1.0e-6 rtol = 1.0e-6 + masked_sol = solve(masked_oprob, QNDF(); saveat=0.1, abstol=1e-9, reltol=1e-9) + graph_sol = solve(graph_oprob, QNDF(); saveat=0.1, abstol=1e-9, reltol=1e-9) + @test base_osol ≈ masked_sol atol = 1e-6 rtol = 1e-6 + @test base_osol ≈ graph_sol atol = 1e-6 rtol = 1e-6 + @test masked_sol ≈ graph_sol atol = 1e-6 rtol = 1e-6 end end @@ -193,19 +193,19 @@ let # Declares the grids (1d, 2d, and 3d). For each dimension, there are a 2 Cartesian grids (length 4 and 3). cart_grid_1d_1 = CartesianGrid(4) cart_grid_1d_2 = CartesianGrid(3) - cart_grid_2d_1 = CartesianGrid((4, 1)) - cart_grid_2d_2 = CartesianGrid((3, 1)) - cart_grid_3d_1 = CartesianGrid((1, 4, 1)) - cart_grid_3d_2 = CartesianGrid((1, 3, 1)) + cart_grid_2d_1 = CartesianGrid((4,1)) + cart_grid_2d_2 = CartesianGrid((3,1)) + cart_grid_3d_1 = CartesianGrid((1,4,1)) + cart_grid_3d_2 = CartesianGrid((1,3,1)) masked_grid_1d = [true, true, true, true, false, true, true, true] - masked_grid_2d = reshape(masked_grid_1d, 8, 1) - masked_grid_3d = reshape(masked_grid_1d, 1, 8, 1) + masked_grid_2d = reshape(masked_grid_1d,8,1) + masked_grid_3d = reshape(masked_grid_1d,1,8,1) - # Creates a base solution to which we will compare all simulations. + # Creaets a base solution to which we will compare all simulations. dsrs_base = DiscreteSpaceReactionSystem(SIR_system, SIR_srs_1, masked_grid_1d) oprob_base = ODEProblem(dsrs_base, [:S => S_vals, :I => I_val, :R => R_val], (0.0, 100.0), [:α => α_vals, :β => β_val, :dS => dS_val]) - sol_base = solve(oprob_base, Tsit5(); saveat = 1.0, abstol = 1.0e-9, reltol = 1.0e-9) + sol_base = solve(oprob_base, Tsit5(); saveat = 1.0, abstol = 1e-9, reltol = 1e-9) # Checks simulations for the masked grid (covering all 7 vertices, with a gap in the middle). for grid in [masked_grid_1d, masked_grid_2d, masked_grid_3d] @@ -214,14 +214,14 @@ let u0 = [:S => [S_vals[1:4]; S_vals[6:8]], :I => I_val, :R => R_val] ps = [:α => [α_vals[1:4]; α_vals[6:8]], :β => β_val, :dS => dS_val] oprob = ODEProblem(dsrs, u0, (0.0, 100.0), ps) - sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1.0e-9, reltol = 1.0e-9) + sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1e-9, reltol = 1e-9) @test sol ≈ sol_base # Checks where the values are arrays of size equal to the grid. u0 = [:S => reshape(S_vals, grid_size(dsrs)), :I => I_val, :R => R_val] ps = [:α => reshape(α_vals, grid_size(dsrs)), :β => β_val, :dS => dS_val] oprob = ODEProblem(dsrs, u0, (0.0, 100.0), ps) - sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1.0e-9, reltol = 1.0e-9) + sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1e-9, reltol = 1e-9) @test sol ≈ sol_base end @@ -232,15 +232,15 @@ let u0 = [:S => S_vals[1:4], :I => I_val, :R => R_val] ps = [:α => α_vals[1:4], :β => β_val, :dS => dS_val] oprob = ODEProblem(dsrs, u0, (0.0, 100.0), ps) - sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1.0e-9, reltol = 1.0e-9) - @test hcat(sol.u...) ≈ sol_base[1:12, :] + sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1e-9, reltol = 1e-9) + @test hcat(sol.u...) ≈ sol_base[1:12,:] # Checks where the values are arrays of size equal to the grid. u0 = [:S => reshape(S_vals[1:4], grid_size(dsrs)), :I => I_val, :R => R_val] ps = [:α => reshape(α_vals[1:4], grid_size(dsrs)), :β => β_val, :dS => dS_val] oprob = ODEProblem(dsrs, u0, (0.0, 100.0), ps) - sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1.0e-9, reltol = 1.0e-9) - @test hcat(sol.u...) ≈ sol_base[1:12, :] + sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1e-9, reltol = 1e-9) + @test hcat(sol.u...) ≈ sol_base[1:12,:] end # Checks simulations for the second Cartesian grids (covering vertices 6 to 8). @@ -250,14 +250,14 @@ let u0 = [:S => S_vals[6:8], :I => I_val, :R => R_val] ps = [:α => α_vals[6:8], :β => β_val, :dS => dS_val] oprob = ODEProblem(dsrs, u0, (0.0, 100.0), ps) - sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1.0e-9, reltol = 1.0e-9) - @test hcat(sol.u...) ≈ sol_base[13:end, :] + sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1e-9, reltol = 1e-9) + @test hcat(sol.u...) ≈ sol_base[13:end,:] # Checks where the values are arrays of size equal to the grid. u0 = [:S => reshape(S_vals[6:8], grid_size(dsrs)), :I => I_val, :R => R_val] ps = [:α => reshape(α_vals[6:8], grid_size(dsrs)), :β => β_val, :dS => dS_val] oprob = ODEProblem(dsrs, u0, (0.0, 100.0), ps) - sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1.0e-9, reltol = 1.0e-9) - @test hcat(sol.u...) ≈ sol_base[13:end, :] + sol = solve(oprob, Tsit5(); saveat = 1.0, abstol = 1e-9, reltol = 1e-9) + @test hcat(sol.u...) ≈ sol_base[13:end,:] end end diff --git a/test/spatial_modelling/dspace_simulation_struct_interfacing.jl b/test/spatial_modelling/dspace_simulation_struct_interfacing.jl index bfdccfc29a..e5dbd44745 100644 --- a/test/spatial_modelling/dspace_simulation_struct_interfacing.jl +++ b/test/spatial_modelling/dspace_simulation_struct_interfacing.jl @@ -14,7 +14,7 @@ include("../spatial_test_networks.jl") # Checks for symbol and symbolic variables input. let # Declares various types of spaces and corresponding initial values of `X`. - dspace_cartesian = CartesianGrid((2, 2, 2)) + dspace_cartesian = CartesianGrid((2,2,2)) dspace_masked = [true true; false true] dspace_graph = cycle_graph(5) val0_cartesian = fill(1.0, 2, 2, 2) @@ -35,13 +35,13 @@ let jprob = JumpProblem(dsrs, deepcopy(u0), (0.0, 1.0), ps) oint = init(deepcopy(oprob), Tsit5()) jint = init(deepcopy(jprob), SSAStepper()) - + # Check that `spat_getu` retrieves the correct values. @test spat_getu(oprob, :X, dsrs) == spat_getu(oprob, X, dsrs) == spat_getu(oprob, brusselator_system.X, dsrs) == val0 @test spat_getu(oint, :X, dsrs) == spat_getu(oint, X, dsrs) == spat_getu(oint, brusselator_system.X, dsrs) == val0 @test spat_getu(jprob, :X, dsrs) == spat_getu(jprob, X, dsrs) == spat_getu(jprob, brusselator_system.X, dsrs) == val0 @test spat_getu(jint, :X, dsrs) == spat_getu(jint, X, dsrs) == spat_getu(jint, brusselator_system.X, dsrs) == val0 - + # Updates Y and checks its content. spat_setu!(oprob, :Y, dsrs, val0) @test spat_getu(oprob, :Y, dsrs) == spat_getu(oprob, Y, dsrs) == spat_getu(oprob, brusselator_system.Y, dsrs) == val0 @@ -70,7 +70,7 @@ end # Checks for symbol and symbolic variables input. let # Declares various types of spaces and corresponding initial values of `A`. - dspace_cartesian = CartesianGrid((2, 2, 2)) + dspace_cartesian = CartesianGrid((2,2,2)) dspace_masked = [true true; false true] dspace_graph = cycle_graph(5) val0_cartesian = fill(1.0, 2, 2, 2) @@ -89,11 +89,11 @@ let ps = [:A => val0, :B => 2.0, :dX => 0.1] oprob = ODEProblem(dsrs, u0, (0.0, 1.0), deepcopy(ps)) oint = init(deepcopy(oprob), Tsit5()) - + # Check that `spat_getp` retrieves the correct values. @test spat_getp(oprob, :A, dsrs) == spat_getp(oprob, A, dsrs) == spat_getp(oprob, brusselator_system.A, dsrs) == val0 @test spat_getp(oint, :A, dsrs) == spat_getp(oint, A, dsrs) == spat_getp(oint, brusselator_system.A, dsrs) == val0 - + # Updates Y and checks its content. spat_setp!(oprob, :B, dsrs, val0) @test spat_getp(oprob, :B, dsrs) == spat_getp(oprob, B, dsrs) == spat_getp(oprob, brusselator_system.B, dsrs) == val0 @@ -137,19 +137,19 @@ end ### Simulation `spat_getu` Tests ### # Basic test. For simulations without change in system, check that the solution corresponds to known -# initial condition throughout the solution. +# initial condition throughout the solution. # Checks using both `t` sampling` and normal time step sampling. # Checks for both ODE and jump simulations. # Checks for all discrete space types. -let +let # Prepare `DiscreteSpaceReactionSystem`s. rs = @reaction_network begin - (k1, k2), X1 <--> X2 + (k1,k2), X1 <--> X2 end tr = @transport_reaction D X1 dsrs1 = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,))) - dsrs2 = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2, 3))) - dsrs3 = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2, 3, 2))) + dsrs2 = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,3))) + dsrs3 = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,3,2))) dsrs4 = DiscreteSpaceReactionSystem(rs, [tr], [true, true, false, true]) dsrs5 = DiscreteSpaceReactionSystem(rs, [tr], [true false; true true]) dsrs6 = DiscreteSpaceReactionSystem(rs, [tr], cycle_graph(4)) @@ -165,7 +165,7 @@ let ps = [:k1 => 0.0, :k2 => 0.0, :D => 0.0] # Loops through all discrete space cases and check that they are correct. - for (u0, dsrs) in zip([u0_1, u0_2, u0_3, u0_4, u0_5, u0_6], [dsrs1, dsrs2, dsrs3, dsrs4, dsrs5, dsrs6]) + for (u0,dsrs) in zip([u0_1, u0_2, u0_3, u0_4, u0_5, u0_6], [dsrs1, dsrs2, dsrs3, dsrs4, dsrs5, dsrs6]) # Simulates ODE version and checks `spat_getu` on its solution. oprob = ODEProblem(dsrs, u0, tspan, ps) osol = solve(oprob, Tsit5(), saveat = 0.5) @@ -188,7 +188,7 @@ end let # Prepare `DiscreteSpaceReactionSystem`s. rs = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X end tr = @transport_reaction D X dsrs = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,))) @@ -200,26 +200,26 @@ let oprob = ODEProblem(dsrs, u0, tspan, ps) # Simulates the ODE. Checks that the start/end points are correct. - # Check that the first vertex is monotonously increasing in values, and that the second one is + # Check that the first vertex is monotonously increasing in values, and that the second one is # monotonously decreasing. The non evenly spaced `saveat` is so that non-monotonicity is # not produced due to numeric errors. saveat = [0.0, 1.0, 5.0, 10.0, 50.0] - sol = solve(oprob, Vern7(); abstol = 1.0e-8, reltol = 1.0e-8) + sol = solve(oprob, Vern7(); abstol = 1e-8, reltol = 1e-8) vals = spat_getu(sol, :X, dsrs) @test vals[1] == [1.0, 3.0] @test vals[end] ≈ [2.0, 2.0] - for i in 1:(length(saveat) - 1) + for i = 1:(length(saveat) - 1) @test vals[i][1] < vals[i + 1][1] @test vals[i][2] > vals[i + 1][2] end end -# Checks interpolation when sampling at time point. Check that values at `t` is in between the +# Checks interpolation when sampling at time point. Check that values at `t` is in between the # sample points. Does so by checking that in simulation which is monotonously decreasing/increasing. let # Prepare `DiscreteSpaceReactionSystem`s. rs = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X end tr = @transport_reaction D X dsrs = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,))) @@ -241,7 +241,7 @@ end let # Prepare `DiscreteSpaceReactionSystem`s. rs = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X end tr = @transport_reaction D X dsrs = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,))) @@ -262,7 +262,7 @@ end let # Prepare `DiscreteSpaceReactionSystem`s. rs = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X end tr = @transport_reaction D X dsrs = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid((2,))) @@ -283,7 +283,7 @@ end let # Prepare `DiscreteSpaceReactionSystem`s. rs = @reaction_network begin - (p, d), 0 <--> X + (p,d), 0 <--> X end tr = @transport_reaction D X dsrs = DiscreteSpaceReactionSystem(rs, [tr], rand([false, true], 2, 3, 4)) @@ -307,7 +307,7 @@ let @named rs = ReactionSystem([Reaction(d, [X], [])], t) rs = complete(rs) tr = @transport_reaction D X - dsrs = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid(2)) + dsrs = DiscreteSpaceReactionSystem(rs, [tr], CartesianGrid(2,)) # Solved a corresponding ODEProblem. u0 = [:X => 1.0] @@ -332,20 +332,20 @@ let for jac in [false, true], sparse in [false, true] # Creates an initial ODEProblem. u0 = [:X => 1.0, :Y => [1.0 2.0; 3.0 4.0]] - dY_vals = spzeros(4, 4) - dY_vals[1, 2] = 0.1; dY_vals[2, 1] = 0.1 - dY_vals[1, 3] = 0.2; dY_vals[3, 1] = 0.2 - dY_vals[2, 4] = 0.3; dY_vals[4, 2] = 0.3 - dY_vals[3, 4] = 0.4; dY_vals[4, 3] = 0.4 + dY_vals = spzeros(4,4) + dY_vals[1,2] = 0.1; dY_vals[2,1] = 0.1; + dY_vals[1,3] = 0.2; dY_vals[3,1] = 0.2; + dY_vals[2,4] = 0.3; dY_vals[4,2] = 0.3; + dY_vals[3,4] = 0.4; dY_vals[4,3] = 0.4; ps = [:A => 1.0, :B => [4.0 5.0; 6.0 7.0], :dX => 0.1, :dY => dY_vals] oprob_1 = ODEProblem(dsrs, u0, (0.0, 10.0), ps; jac, sparse) # Creates an alternative version of the ODEProblem. - dX_vals = spzeros(4, 4) - dX_vals[1, 2] = 0.01; dX_vals[2, 1] = 0.01 - dX_vals[1, 3] = 0.02; dX_vals[3, 1] = 0.02 - dX_vals[2, 4] = 0.03; dX_vals[4, 2] = 0.03 - dX_vals[3, 4] = 0.04; dX_vals[4, 3] = 0.04 + dX_vals = spzeros(4,4) + dX_vals[1,2] = 0.01; dX_vals[2,1] = 0.01; + dX_vals[1,3] = 0.02; dX_vals[3,1] = 0.02; + dX_vals[2,4] = 0.03; dX_vals[4,2] = 0.03; + dX_vals[3,4] = 0.04; dX_vals[4,3] = 0.04; ps = [:A => [1.1 1.2; 1.3 1.4], :B => 5.0, :dX => dX_vals, :dY => 0.01] oprob_2 = ODEProblem(dsrs, u0, (0.0, 10.0), ps; jac, sparse) @@ -372,16 +372,16 @@ let B1 = [4.0 5.0; 6.0 7.0] A2 = [1.1 1.2; 1.3 1.4] B2 = 5.0 - dY_vals = spzeros(4, 4) - dY_vals[1, 2] = 0.1; dY_vals[2, 1] = 0.1 - dY_vals[1, 3] = 0.2; dY_vals[3, 1] = 0.2 - dY_vals[2, 4] = 0.3; dY_vals[4, 2] = 0.3 - dY_vals[3, 4] = 0.4; dY_vals[4, 3] = 0.4 - dX_vals = spzeros(4, 4) - dX_vals[1, 2] = 0.01; dX_vals[2, 1] = 0.01 - dX_vals[1, 3] = 0.02; dX_vals[3, 1] = 0.02 - dX_vals[2, 4] = 0.03; dX_vals[4, 2] = 0.03 - dX_vals[3, 4] = 0.04; dX_vals[4, 3] = 0.04 + dY_vals = spzeros(4,4) + dY_vals[1,2] = 0.1; dY_vals[2,1] = 0.1; + dY_vals[1,3] = 0.2; dY_vals[3,1] = 0.2; + dY_vals[2,4] = 0.3; dY_vals[4,2] = 0.3; + dY_vals[3,4] = 0.4; dY_vals[4,3] = 0.4; + dX_vals = spzeros(4,4) + dX_vals[1,2] = 0.01; dX_vals[2,1] = 0.01; + dX_vals[1,3] = 0.02; dX_vals[3,1] = 0.02; + dX_vals[2,4] = 0.03; dX_vals[4,2] = 0.03; + dX_vals[3,4] = 0.04; dX_vals[4,3] = 0.04; dX1 = 0.1 dY1 = dY_vals dX2 = dX_vals @@ -391,10 +391,10 @@ let # Creates simulation through two different separate simulations. oprob_1_1 = ODEProblem(dsrs, u0, (0.0, 5.0), ps_1; jac = true, sparse = true) - sol_1_1 = solve(oprob_1_1, Rosenbrock23(); saveat = 1.0, abstol = 1.0e-8, reltol = 1.0e-8) + sol_1_1 = solve(oprob_1_1, Rosenbrock23(); saveat = 1.0, abstol = 1e-8, reltol = 1e-8) u0_1_2 = [:X => sol_1_1.u[end][1:2:end], :Y => sol_1_1.u[end][2:2:end]] oprob_1_2 = ODEProblem(dsrs, u0_1_2, (0.0, 5.0), ps_2; jac = true, sparse = true) - sol_1_2 = solve(oprob_1_2, Rosenbrock23(); saveat = 1.0, abstol = 1.0e-8, reltol = 1.0e-8) + sol_1_2 = solve(oprob_1_2, Rosenbrock23(); saveat = 1.0, abstol = 1e-8, reltol = 1e-8) # Creates simulation through a single simulation with a callback oprob_2 = ODEProblem(dsrs, u0, (0.0, 10.0), ps_1; jac = true, sparse = true) @@ -404,10 +404,10 @@ let spat_setp!(integrator, :B, dsrs, B2) integrator.ps[:dX] = dX2 integrator.ps[:dY] = [dY2] - return rebuild_spat_internals!(integrator) + rebuild_spat_internals!(integrator) end callback = DiscreteCallback(condition, affect!) - sol_2 = solve(oprob_2, Rosenbrock23(); saveat = 1.0, tstops = [5.0], callback, abstol = 1.0e-8, reltol = 1.0e-8) + sol_2 = solve(oprob_2, Rosenbrock23(); saveat = 1.0, tstops = [5.0], callback, abstol = 1e-8, reltol = 1e-8) # Check that trajectories are equivalent. @test [sol_1_1.u; sol_1_2.u] ≈ sol_2.u @@ -417,10 +417,10 @@ end let # Prepare `DiscreteSpaceReactionSystem`. rs = @reaction_network begin - (k1, k2), X1 <--> X2 + (k1,k2), X1 <--> X2 end - tr = @transport_reaction D X1 - grid = CartesianGrid((2, 2)) + tr = @transport_reaction D X1 + grid = CartesianGrid((2,2)) dsrs = DiscreteSpaceReactionSystem(rs, [tr], grid) # Create problems. diff --git a/test/spatial_modelling/simulate_PDEs.jl b/test/spatial_modelling/simulate_PDEs.jl index 3bcbc605a4..9e4a1dd9b9 100644 --- a/test/spatial_modelling/simulate_PDEs.jl +++ b/test/spatial_modelling/simulate_PDEs.jl @@ -11,7 +11,7 @@ rng = StableRNG(12345) # Test function. function icfun(n, x, y, A) - return float(rand(rng, Poisson(round(n * A * 10))) / A / 10) + float(rand(rng, Poisson(round(n * A * 10))) / A / 10) end ### Run Tests ### @@ -21,15 +21,13 @@ let @parameters k[1:7] D[1:3] n0[1:3] A @parameters x y @species U(x, y, t) V(x, y, t) W(x, y, t) - rxs = [ - Reaction(k[1], [U, W], [V, W]), + rxs = [Reaction(k[1], [U, W], [V, W]), Reaction(k[2], [V], [W], [2], [1]), Reaction(k[3], [W], [V], [1], [2]), Reaction(k[4], [U], nothing), Reaction(k[5], nothing, [U]), Reaction(k[6], [V], nothing), - Reaction(k[7], nothing, [V]), - ] + Reaction(k[7], nothing, [V])] pars = vcat(MT.scalarize(k), MT.scalarize(D), MT.scalarize(n0), [A]) @named bpm = ReactionSystem(rxs, t, [U, V, W], pars; spatial_ivs = [x, y]) @@ -38,23 +36,17 @@ let @test isspatial(bpm) rxeqs = Catalyst.assemble_oderhs(bpm, unknowns(bpm), combinatoric_ratelaws = false) - eqs = Dict( - ( - U => (k[5] - k[4] * U - k[1] * U * W), - V => (2 * k[3] * W + k[1] * U * W + k[7] - k[6] * V - 2 * (V^2) * k[2]), - W => ((V^2) * k[2] - k[3] * W), - ) - ) + eqs = Dict((U => (k[5] - k[4] * U - k[1] * U * W), + V => (2 * k[3] * W + k[1] * U * W + k[7] - k[6] * V - 2 * (V^2) * k[2]), + W => ((V^2) * k[2] - k[3] * W))) @test all(isequal.((MT.unwrap(eqs[st]) for st in unknowns(bpm)), rxeqs)) @test issetequal(species(bpm), [MT.unwrap(U), MT.unwrap(V), MT.unwrap(W)]) # Test a few API functions. - ns = [ - -1 0 0 -1 1 0 0; - 1 -2 2 0 0 -1 1; - 0 1 -1 0 0 0 0 - ] + ns = [-1 0 0 -1 1 0 0; + 1 -2 2 0 0 -1 1; + 0 1 -1 0 0 0 0] @test ns == netstoichmat(bpm) bpm2 = deepcopy(bpm) @test Catalyst.isequivalent(bpm, bpm2) @@ -70,34 +62,28 @@ let evalat(u, a, b, t) = (operation(ModelingToolkitBase.unwrap(u)))(a, b, t) @register_symbolic icfun(n, x, y, A) L = 32.0 - tstop = 5.0e4 + tstop = 5e4 for (i, st) in enumerate(unknowns(bpm)) idx = smap[st] eqs[i] = ∂t(st) ~ D[idx] * Δ(st) + rxeqs[idx] - newbcs = [ - evalat(st, x, y, 0.0) ~ icfun(n0[idx], x, y, A), + newbcs = [evalat(st, x, y, 0.0) ~ icfun(n0[idx], x, y, A), evalat(st, 0.0, y, t) ~ evalat(st, L, y, t), - evalat(st, x, 0.0, t) ~ evalat(st, x, L, t), - ] + evalat(st, x, 0.0, t) ~ evalat(st, x, L, t)] append!(bcs, newbcs) end - domains = [ - x ∈ Interval(0.0, L), + domains = [x ∈ Interval(0.0, L), y ∈ Interval(0.0, L), - t ∈ Interval(0.0, tstop), - ] + t ∈ Interval(0.0, tstop)] pmap = collect(MT.initial_conditions(bpm)) @named bpmpdes = PDESystem(eqs, bcs, domains, [x, y, t], [U, V, W], pmap) - rxs = [ - Reaction(k[1] * x, [U, W], [V, W]), + rxs = [Reaction(k[1] * x, [U, W], [V, W]), Reaction(k[2] * y, [V], [W], [2], [1]), Reaction(k[3] + t, [W], [V], [1], [2]), Reaction(k[4], [U], nothing), Reaction(k[5], nothing, [U]), Reaction(k[6], [V], nothing), - Reaction(k[7], nothing, [V]), - ] + Reaction(k[7], nothing, [V])] @named bpm4 = ReactionSystem(rxs, t, [U, V, W], pars; spatial_ivs = [x, y]) @test !ismassaction(rxs[1], bpm4) @test ismassaction(rxs[1], bpm4; ivset = Set([t, y])) diff --git a/test/spatial_modelling/spatial_reactions.jl b/test/spatial_modelling/spatial_reactions.jl index a74ee7f7b5..bdda4c25a9 100644 --- a/test/spatial_modelling/spatial_reactions.jl +++ b/test/spatial_modelling/spatial_reactions.jl @@ -9,13 +9,13 @@ using Catalyst, Test # Tests TransportReaction with non-trivial rate. let rs = @reaction_network begin - @parameters dV dE [edgeparameter = true] - (p, 1), 0 <--> X + @parameters dV dE [edgeparameter=true] + (p,1), 0 <--> X end @unpack dV, dE, X = rs - tr = TransportReaction(dV * dE, X) - @test isequal(tr.rate, dV * dE) + tr = TransportReaction(dV*dE, X) + @test isequal(tr.rate, dV*dE) end # Test reactions with constants in rate. let @@ -38,7 +38,7 @@ end # Test case 1. let tr_1 = @transport_reaction dX X - tr_2 = @transport_reaction dY1 * dY2 Y + tr_2 = @transport_reaction dY1*dY2 Y # @test ModelingToolkitBase.getname.(species(tr_1)) == ModelingToolkitBase.getname.(spatial_species(tr_1)) == [:X] # species(::TransportReaction) currently not supported. # @test ModelingToolkitBase.getname.(species(tr_2)) == ModelingToolkitBase.getname.(spatial_species(tr_2)) == [:Y] @@ -61,7 +61,7 @@ let end @unpack X, Y, dX, dY1, dY2 = rs tr_1 = TransportReaction(dX, X) - tr_2 = TransportReaction(dY1 * dY2, Y) + tr_2 = TransportReaction(dY1*dY2, Y) # @test isequal(species(tr_1), [X]) # @test isequal(species(tr_1), [X]) @test issetequal(spatial_species(tr_2), [Y]) @@ -79,7 +79,7 @@ let t = default_t() @species X(t) Y(t) @parameters D1 D2 D3 - @test_throws ErrorException TransportReaction(D1 + D2 * (D3 + Y), X) + @test_throws ErrorException TransportReaction(D1 + D2*(D3 + Y), X) @test_throws ErrorException TransportReaction(Y, X) @test_throws Exception @eval @transport_reaction ∅ X end @@ -95,10 +95,10 @@ let end @unpack X, Y, Z, dX, dY1, dY2, dZ = rs rate1 = dX - rate2 = dY1 * dY2 + rate2 = dY1*dY2 species3 = Z tr_1 = TransportReaction(dX, X) - tr_2 = TransportReaction(dY1 * dY2, Y) + tr_2 = TransportReaction(dY1*dY2, Y) tr_3 = TransportReaction(dZ, Z) tr_macro_1 = @transport_reaction $dX X tr_macro_2 = @transport_reaction $(rate2) Y diff --git a/test/spatial_test_networks.jl b/test/spatial_test_networks.jl index c04c503cab..af4be2a183 100644 --- a/test/spatial_test_networks.jl +++ b/test/spatial_test_networks.jl @@ -1,7 +1,7 @@ ### Fetch packages ### using Catalyst, Graphs using Catalyst: reactionsystem, spatial_reactions, dspace, num_verts, num_edges, num_species, - spatial_species, vertex_parameters, edge_parameters, edge_iterator + spatial_species, vertex_parameters, edge_parameters, edge_iterator const MT = ModelingToolkitBase # Sets rnd number. @@ -16,7 +16,7 @@ rand_v_vals(dsrs::DiscreteSpaceReactionSystem) = rand_v_vals(dspace(dsrs)) function rand_v_vals(grid::DiGraph) return rand(rng, nv(grid)) end -function rand_v_vals(grid::Catalyst.CartesianGridRej{N, T}) where {N, T} +function rand_v_vals(grid::Catalyst.CartesianGridRej{N,T}) where {N,T} return rand(rng, grid.dims) end function rand_v_vals(grid::Array{Bool, N}) where {N} @@ -43,12 +43,12 @@ end # Gets a symbol list of spatial parameters. function spatial_param_syms(dsrs::DiscreteSpaceReactionSystem) - return MT.getname.(edge_parameters(dsrs)) + MT.getname.(edge_parameters(dsrs)) end # Converts to integer value (for JumpProcess simulations). function make_values_int(values::Vector{<:Pair}) - return [val[1] => round.(Int64, val[2]) for val in values] + [val[1] => round.(Int64, val[2]) for val in values] end make_values_int(values::Matrix{<:Number}) = round.(Int64, values) make_values_int(values::Vector{<:Number}) = round.(Int64, values) @@ -71,9 +71,7 @@ SIR_srs_1 = [SIR_tr_S] SIR_srs_2 = [SIR_tr_S, SIR_tr_I, SIR_tr_R] # Small non-stiff system. -binding_system = @reaction_network begin - (k1, k2), X + Y <--> XY -end +binding_system = @reaction_network begin (k1, k2), X + Y <--> XY end binding_tr_X = @transport_reaction dX X binding_tr_Y = @transport_reaction dY Y binding_tr_XY = @transport_reaction dXY XY @@ -167,13 +165,11 @@ sigmaB_system = @reaction_network begin λW * v0 * ((1 + F * σB) / (K + σB)), ∅ ⟶ w λV * v0 * ((1 + F * σB) / (K + σB)), ∅ ⟶ v end -sigmaB_p = [ - :kBw => 3600, :kDw => 18, :kB1 => 3600, :kB2 => 3600, :kB3 => 3600, +sigmaB_p = [:kBw => 3600, :kDw => 18, :kB1 => 3600, :kB2 => 3600, :kB3 => 3600, :kB4 => 1800, :kB5 => 3600, :kD1 => 18, :kD2 => 18, :kD3 => 18, :kD4 => 1800, :kD5 => 18, :kK1 => 36, :kK2 => 6, :kP => 180, :kDeg => 0.7, - :v0 => 0.4, :F => 30, :K => 0.2, :λW => 4, :λV => 4.5, -] + :v0 => 0.4, :F => 30, :K => 0.2, :λW => 4, :λV => 4.5] sigmaB_u0 = [ :w => 1.0, :w2 => 1.0, @@ -197,16 +193,16 @@ sigmaB_srs_2 = [sigmaB_tr_σB, sigmaB_tr_w, sigmaB_tr_v] # Cartesian grids. very_small_1d_cartesian_grid = CartesianGrid(2) -very_small_2d_cartesian_grid = CartesianGrid((2, 2)) -very_small_3d_cartesian_grid = CartesianGrid((2, 2, 2)) +very_small_2d_cartesian_grid = CartesianGrid((2,2)) +very_small_3d_cartesian_grid = CartesianGrid((2,2,2)) small_1d_cartesian_grid = CartesianGrid(5) -small_2d_cartesian_grid = CartesianGrid((5, 5)) -small_3d_cartesian_grid = CartesianGrid((5, 5, 5)) +small_2d_cartesian_grid = CartesianGrid((5,5)) +small_3d_cartesian_grid = CartesianGrid((5,5,5)) large_1d_cartesian_grid = CartesianGrid(100) -large_2d_cartesian_grid = CartesianGrid((100, 100)) -large_3d_cartesian_grid = CartesianGrid((100, 100, 100)) +large_2d_cartesian_grid = CartesianGrid((100,100)) +large_3d_cartesian_grid = CartesianGrid((100,100,100)) # Masked grids. very_small_1d_masked_grid = fill(true, 2) @@ -231,16 +227,16 @@ very_small_2d_graph_grid = Graphs.grid([2, 2]) very_small_3d_graph_grid = Graphs.grid([2, 2, 2]) small_1d_graph_grid = path_graph(5) -small_2d_graph_grid = Graphs.grid([5, 5]) -small_3d_graph_grid = Graphs.grid([5, 5, 5]) +small_2d_graph_grid = Graphs.grid([5,5]) +small_3d_graph_grid = Graphs.grid([5,5,5]) medium_1d_graph_grid = path_graph(20) -medium_2d_graph_grid = Graphs.grid([20, 20]) -medium_3d_graph_grid = Graphs.grid([20, 20, 20]) +medium_2d_graph_grid = Graphs.grid([20,20]) +medium_3d_graph_grid = Graphs.grid([20,20,20]) large_1d_graph_grid = path_graph(100) -large_2d_graph_grid = Graphs.grid([100, 100]) -large_3d_graph_grid = Graphs.grid([100, 100, 100]) +large_2d_graph_grid = Graphs.grid([100,100]) +large_3d_graph_grid = Graphs.grid([100,100,100]) # Graph - paths. short_path = path_graph(100) diff --git a/test/test_functions.jl b/test/test_functions.jl index 85f6608261..3977a7de04 100644 --- a/test/test_functions.jl +++ b/test/test_functions.jl @@ -17,7 +17,7 @@ end # Generates a random parameter set (in the form of a map). Each value is a Float64. function rnd_ps(sys, rng; factor = 1.0, min = 0.0) - return [p => (min .+ factor .* rand(rng, size(p)...)) for p in parameters(sys)] + return [p => ( min .+ factor .* rand(rng, size(p)...)) for p in parameters(sys)] end # Generates a random parameter set (in the form of a map). Each value is a Float64. @@ -37,10 +37,8 @@ end # Evaluates the the drift function of the ODE corresponding to a reaction network. # Also checks that in place and out of place evaluations are identical. -function f_eval( - rs::ReactionSystem, u, p, t; combinatoric_ratelaws = true, mtkcompile = false, - use_jump_ratelaws = false - ) +function f_eval(rs::ReactionSystem, u, p, t; combinatoric_ratelaws = true, mtkcompile = false, + use_jump_ratelaws = false) prob = ODEProblem(rs, u, 0.0, p; combinatoric_ratelaws, mtkcompile, use_jump_ratelaws) du = zeros(length(u)) prob.f(du, prob.u0, prob.p, t) @@ -54,16 +52,14 @@ function jac_eval(rs::ReactionSystem, u, p, t; combinatoric_ratelaws = true, spa prob = ODEProblem(rs, u, 0.0, p; jac = true, combinatoric_ratelaws, sparse, mtkcompile) J = sparse ? deepcopy(prob.f.jac_prototype) : zeros(length(u), length(u)) prob.f.jac(J, prob.u0, prob.p, t) - @test J ≈ prob.f.jac(prob.u0, prob.p, t) atol = 1.0e-14 rtol = 1.0e-14 + @test J ≈ prob.f.jac(prob.u0, prob.p, t) atol = 1e-14 rtol = 1e-14 return J end # Evaluates the the diffusion function of the SDE corresponding to a reaction network. # Also checks that in place and out of place evaluations are identical. -function g_eval( - rs::ReactionSystem, u, p, t; combinatoric_ratelaws = true, mtkcompile = false, - use_jump_ratelaws = false - ) +function g_eval(rs::ReactionSystem, u, p, t; combinatoric_ratelaws = true, mtkcompile = false, + use_jump_ratelaws = false) prob = SDEProblem(rs, u, 0.0, p; combinatoric_ratelaws, mtkcompile, use_jump_ratelaws) dW = zeros(length(u), numreactions(rs) + length(ModelingToolkitBase.get_brownians(rs))) prob.g(dW, prob.u0, prob.p, t) diff --git a/test/test_networks.jl b/test/test_networks.jl index 38705adbfd..a77fa75a12 100644 --- a/test/test_networks.jl +++ b/test/test_networks.jl @@ -79,10 +79,10 @@ end reaction_networks_standard[10] = @reaction_network rns10 begin p, ∅ ⟶ X1 - (k1, k2), (X1, X1) → X2 - (k3, k4), (X2, X2) → X3 - (k5, k6), (X3, X3) → X4 - (k7, k8), (X4, X4) → X5 + (k1, k2), (X1,X1) → X2 + (k3, k4), (X2,X2) → X3 + (k5, k6), (X3,X3) → X4 + (k7, k8), (X4,X4) → X5 d, X5 ⟶ ∅ end @@ -220,10 +220,8 @@ reaction_networks_conserved[9] = @reaction_network rnc9 begin (k3, k4), X3 + X4 ↔ X5 (k5, k6), X5 + X6 ↔ X7 end -reaction_network_conslaws[9] = [ - 1 0 1 0 1 0 1; -1 1 0 0 0 0 0; 0 0 0 1 1 0 1; - 0 0 0 0 0 1 1 -] +reaction_network_conslaws[9] = [1 0 1 0 1 0 1; -1 1 0 0 0 0 0; 0 0 0 1 1 0 1; + 0 0 0 0 0 1 1] reaction_networks_conserved[10] = @reaction_network rnc10 begin kDeg, (w, w2, w2v, v, w2v2, vP, σB, w2σB) ⟶ ∅ @@ -351,10 +349,8 @@ reaction_networks_weird[10] = @reaction_network rnw10 begin end ### Gathers all networks in a single array ### -reaction_networks_all = [ - reaction_networks_standard..., +reaction_networks_all = [reaction_networks_standard..., reaction_networks_hill..., reaction_networks_conserved..., reaction_networks_real..., - reaction_networks_weird..., -] + reaction_networks_weird...] diff --git a/test/upstream/mtk_structure_indexing.jl b/test/upstream/mtk_structure_indexing.jl index 38d94f0666..f9f066f790 100644 --- a/test/upstream/mtk_structure_indexing.jl +++ b/test/upstream/mtk_structure_indexing.jl @@ -19,8 +19,8 @@ begin # Creates the model and unpacks its context. model = @reaction_network begin @observables XY ~ X + Y - (kp, kd), 0 <--> X - (k1, k2), X <--> Y + (kp,kd), 0 <--> X + (k1,k2), X <--> Y end @unpack XY, X, Y, kp, kd, k1, k2 = model @@ -31,7 +31,7 @@ begin # Creates problems. oprob = ODEProblem(model, u0_vals, tspan, p_vals) - sprob = SDEProblem(model, u0_vals, tspan, p_vals) + sprob = SDEProblem(model,u0_vals, tspan, p_vals) jprob = JumpProblem(model, u0_vals, tspan, p_vals; rng) nprob = NonlinearProblem(model, u0_vals, p_vals) ssprob = SteadyStateProblem(model, u0_vals, p_vals) @@ -64,16 +64,16 @@ end # Tests problem indexing and updating. let - for prob in deepcopy([oprob, sprob, jprob, nprob, ssprob, eoprob, esprob, ejprob, enprob, essprob]) + for prob in deepcopy([oprob, sprob, jprob, nprob, ssprob, eoprob, esprob, ejprob, enprob, essprob]) # Get u values (including observables). @test prob[X] == prob[model.X] == prob[:X] == 4 @test prob[XY] == prob[model.XY] == prob[:XY] == 9 - @test prob[[XY, Y]] == prob[[model.XY, model.Y]] == prob[[:XY, :Y]] == [9, 5] - @test prob[(XY, Y)] == prob[(model.XY, model.Y)] == prob[(:XY, :Y)] == (9, 5) + @test prob[[XY,Y]] == prob[[model.XY,model.Y]] == prob[[:XY,:Y]] == [9, 5] + @test prob[(XY,Y)] == prob[(model.XY,model.Y)] == prob[(:XY,:Y)] == (9, 5) @test getu(prob, X)(prob) == getu(prob, model.X)(prob) == getu(prob, :X)(prob) == 4 @test getu(prob, XY)(prob) == getu(prob, model.XY)(prob) == getu(prob, :XY)(prob) == 9 - @test getu(prob, [XY, Y])(prob) == getu(prob, [model.XY, model.Y])(prob) == getu(prob, [:XY, :Y])(prob) == [9, 5] - @test getu(prob, (XY, Y))(prob) == getu(prob, (model.XY, model.Y))(prob) == getu(prob, (:XY, :Y))(prob) == (9, 5) + @test getu(prob, [XY,Y])(prob) == getu(prob, [model.XY,model.Y])(prob) == getu(prob, [:XY,:Y])(prob) == [9, 5] + @test getu(prob, (XY,Y))(prob) == getu(prob, (model.XY,model.Y))(prob) == getu(prob, (:XY,:Y))(prob) == (9, 5) # Set u values. prob[X] = 20 @@ -91,11 +91,11 @@ let # Get p values. @test prob.ps[kp] == prob.ps[model.kp] == prob.ps[:kp] == 1.0 - @test prob.ps[[k1, k2]] == prob.ps[[model.k1, model.k2]] == prob.ps[[:k1, :k2]] == [0.25, 0.5] - @test prob.ps[(k1, k2)] == prob.ps[(model.k1, model.k2)] == prob.ps[(:k1, :k2)] == (0.25, 0.5) + @test prob.ps[[k1,k2]] == prob.ps[[model.k1,model.k2]] == prob.ps[[:k1,:k2]] == [0.25, 0.5] + @test prob.ps[(k1,k2)] == prob.ps[(model.k1,model.k2)] == prob.ps[(:k1,:k2)] == (0.25, 0.5) @test getp(prob, kp)(prob) == getp(prob, model.kp)(prob) == getp(prob, :kp)(prob) == 1.0 - @test getp(prob, [k1, k2])(prob) == getp(prob, [model.k1, model.k2])(prob) == getp(prob, [:k1, :k2])(prob) == [0.25, 0.5] - @test getp(prob, (k1, k2))(prob) == getp(prob, (model.k1, model.k2))(prob) == getp(prob, (:k1, :k2))(prob) == (0.25, 0.5) + @test getp(prob, [k1,k2])(prob) == getp(prob, [model.k1,model.k2])(prob) == getp(prob, [:k1,:k2])(prob) == [0.25, 0.5] + @test getp(prob, (k1,k2))(prob) == getp(prob, (model.k1,model.k2))(prob) == getp(prob, (:k1,:k2))(prob) == (0.25, 0.5) # Set p values. prob.ps[kp] = 2.0 @@ -156,12 +156,12 @@ let # Get u values. @test int[X] == int[model.X] == int[:X] == 4 @test int[XY] == int[model.XY] == int[:XY] == 9 - @test int[[XY, Y]] == int[[model.XY, model.Y]] == int[[:XY, :Y]] == [9, 5] - @test int[(XY, Y)] == int[(model.XY, model.Y)] == int[(:XY, :Y)] == (9, 5) + @test int[[XY,Y]] == int[[model.XY,model.Y]] == int[[:XY,:Y]] == [9, 5] + @test int[(XY,Y)] == int[(model.XY,model.Y)] == int[(:XY,:Y)] == (9, 5) @test getu(int, X)(int) == getu(int, model.X)(int) == getu(int, :X)(int) == 4 @test getu(int, XY)(int) == getu(int, model.XY)(int) == getu(int, :XY)(int) == 9 - @test getu(int, [XY, Y])(int) == getu(int, [model.XY, model.Y])(int) == getu(int, [:XY, :Y])(int) == [9, 5] - @test getu(int, (XY, Y))(int) == getu(int, (model.XY, model.Y))(int) == getu(int, (:XY, :Y))(int) == (9, 5) + @test getu(int, [XY,Y])(int) == getu(int, [model.XY,model.Y])(int) == getu(int, [:XY,:Y])(int) == [9, 5] + @test getu(int, (XY,Y))(int) == getu(int, (model.XY,model.Y))(int) == getu(int, (:XY,:Y))(int) == (9, 5) # Set u values. int[X] = 20 @@ -179,11 +179,11 @@ let # Get p values. @test int.ps[kp] == int.ps[model.kp] == int.ps[:kp] == 1.0 - @test int.ps[[k1, k2]] == int.ps[[model.k1, model.k2]] == int.ps[[:k1, :k2]] == [0.25, 0.5] - @test int.ps[(k1, k2)] == int.ps[(model.k1, model.k2)] == int.ps[(:k1, :k2)] == (0.25, 0.5) + @test int.ps[[k1,k2]] == int.ps[[model.k1,model.k2]] == int.ps[[:k1,:k2]] == [0.25, 0.5] + @test int.ps[(k1,k2)] == int.ps[(model.k1,model.k2)] == int.ps[(:k1,:k2)] == (0.25, 0.5) @test getp(int, kp)(int) == getp(int, model.kp)(int) == getp(int, :kp)(int) == 1.0 - @test getp(int, [k1, k2])(int) == getp(int, [model.k1, model.k2])(int) == getp(int, [:k1, :k2])(int) == [0.25, 0.5] - @test getp(int, (k1, k2))(int) == getp(int, (model.k1, model.k2))(int) == getp(int, (:k1, :k2))(int) == (0.25, 0.5) + @test getp(int, [k1,k2])(int) == getp(int, [model.k1,model.k2])(int) == getp(int, [:k1,:k2])(int) == [0.25, 0.5] + @test getp(int, (k1,k2))(int) == getp(int, (model.k1,model.k2))(int) == getp(int, (:k1,:k2))(int) == (0.25, 0.5) # Set p values. int.ps[kp] = 2.0 @@ -208,24 +208,24 @@ let # Save single variable if !(solver isa SSAStepper) - @test solve(prob, solver; seed, save_idxs = X)[X][1] == 4 - @test solve(prob, solver; seed, save_idxs = model.X)[X][1] == 4 - @test solve(prob, solver; seed, save_idxs = :X)[X][1] == 4 + @test solve(prob, solver; seed, save_idxs=X)[X][1] == 4 + @test solve(prob, solver; seed, save_idxs=model.X)[X][1] == 4 + @test solve(prob, solver; seed, save_idxs=:X)[X][1] == 4 else - @test_broken solve(prob, solver; seed, save_idxs = X)[X][1] == 4 - @test_broken solve(prob, solver; seed, save_idxs = model.X)[X][1] == 4 - @test_broken solve(prob, solver; seed, save_idxs = :X)[X][1] == 4 + @test_broken solve(prob, solver; seed, save_idxs=X)[X][1] == 4 + @test_broken solve(prob, solver; seed, save_idxs=model.X)[X][1] == 4 + @test_broken solve(prob, solver; seed, save_idxs=:X)[X][1] == 4 end # Save observable. - @test_broken solve(prob, solver; seed, save_idxs = XY)[XY][1] == 9 - @test_broken solve(prob, solver; seed, save_idxs = model.XY)[XY][1] == 9 - @test_broken solve(prob, solver; seed, save_idxs = :XY)[XY][1] == 9 + @test_broken solve(prob, solver; seed, save_idxs=XY)[XY][1] == 9 + @test_broken solve(prob, solver; seed, save_idxs=model.XY)[XY][1] == 9 + @test_broken solve(prob, solver; seed, save_idxs=:XY)[XY][1] == 9 # Save vector of stuff. - @test_broken solve(prob, solver; seed, save_idxs = [XY, Y])[[XY, Y]][1] == [9, 5] - @test_broken solve(prob, solver; seed, save_idxs = [model.XY, model.Y])[[model.XY, model.Y]][1] == [9, 5] - @test_broken solve(prob, solver; seed, save_idxs = [:XY, :Y])[[:XY, :Y]][1] == [9, 5] + @test_broken solve(prob, solver; seed, save_idxs=[XY,Y])[[XY,Y]][1] == [9, 5] + @test_broken solve(prob, solver; seed, save_idxs=[model.XY,model.Y])[[model.XY,model.Y]][1] == [9, 5] + @test_broken solve(prob, solver; seed, save_idxs=[:XY,:Y])[[:XY,:Y]][1] == [9, 5] end end @@ -235,26 +235,26 @@ let # Get u values. @test sol[X][1] == sol[model.X][1] == sol[:X][1] == 4 @test sol[XY][1] == sol[model.XY][1] == sol[:XY][1] == 9 - @test sol[[XY, Y]][1] == sol[[model.XY, model.Y]][1] == sol[[:XY, :Y]][1] == [9, 5] - @test sol[(XY, Y)][1] == sol[(model.XY, model.Y)][1] == sol[(:XY, :Y)][1] == (9, 5) + @test sol[[XY,Y]][1] == sol[[model.XY,model.Y]][1] == sol[[:XY,:Y]][1] == [9, 5] + @test sol[(XY,Y)][1] == sol[(model.XY,model.Y)][1] == sol[(:XY,:Y)][1] == (9, 5) @test getu(sol, X)(sol)[1] == getu(sol, model.X)(sol)[1] == getu(sol, :X)(sol)[1] == 4 @test getu(sol, XY)(sol)[1] == getu(sol, model.XY)(sol)[1] == getu(sol, :XY)(sol)[1] == 9 - @test getu(sol, [XY, Y])(sol)[1] == getu(sol, [model.XY, model.Y])(sol)[1] == getu(sol, [:XY, :Y])(sol)[1] == [9, 5] - @test getu(sol, (XY, Y))(sol)[1] == getu(sol, (model.XY, model.Y))(sol)[1] == getu(sol, (:XY, :Y))(sol)[1] == (9, 5) + @test getu(sol, [XY,Y])(sol)[1] == getu(sol, [model.XY,model.Y])(sol)[1] == getu(sol, [:XY,:Y])(sol)[1] == [9, 5] + @test getu(sol, (XY,Y))(sol)[1] == getu(sol, (model.XY,model.Y))(sol)[1] == getu(sol, (:XY,:Y))(sol)[1] == (9, 5) # Get u values via idxs and functional call. - @test sol(0.0; idxs = X) == sol(0.0; idxs = model.X) == sol(0.0; idxs = :X) == 4 - @test sol(0.0; idxs = XY) == sol(0.0; idxs = model.XY) == sol(0.0; idxs = :XY) == 9 - @test sol(0.0; idxs = [XY, Y]) == sol(0.0; idxs = [model.XY, model.Y]) == sol(0.0; idxs = [:XY, :Y]) == [9, 5] - @test_broken sol(0.0; idxs = (XY, Y)) == sol(0.0; idxs = (model.XY, model.Y)) == sol(0.0; idxs = (:XY, :Y)) == (9, 5) # https://github.com/SciML/SciMLBase.jl/issues/711 + @test sol(0.0; idxs=X) == sol(0.0; idxs=model.X) == sol(0.0; idxs=:X) == 4 + @test sol(0.0; idxs=XY) == sol(0.0; idxs=model.XY) == sol(0.0; idxs=:XY) == 9 + @test sol(0.0; idxs = [XY,Y]) == sol(0.0; idxs = [model.XY,model.Y]) == sol(0.0; idxs = [:XY,:Y]) == [9, 5] + @test_broken sol(0.0; idxs = (XY,Y)) == sol(0.0; idxs = (model.XY,model.Y)) == sol(0.0; idxs = (:XY,:Y)) == (9, 5) # https://github.com/SciML/SciMLBase.jl/issues/711 # Get p values. @test sol.ps[kp] == sol.ps[model.kp] == sol.ps[:kp] == 1.0 - @test sol.ps[[k1, k2]] == sol.ps[[model.k1, model.k2]] == sol.ps[[:k1, :k2]] == [0.25, 0.5] - @test sol.ps[(k1, k2)] == sol.ps[(model.k1, model.k2)] == sol.ps[(:k1, :k2)] == (0.25, 0.5) + @test sol.ps[[k1,k2]] == sol.ps[[model.k1,model.k2]] == sol.ps[[:k1,:k2]] == [0.25, 0.5] + @test sol.ps[(k1,k2)] == sol.ps[(model.k1,model.k2)] == sol.ps[(:k1,:k2)] == (0.25, 0.5) @test getp(sol, kp)(sol) == getp(sol, model.kp)(sol) == getp(sol, :kp)(sol) == 1.0 - @test getp(sol, [k1, k2])(sol) == getp(sol, [model.k1, model.k2])(sol) == getp(sol, [:k1, :k2])(sol) == [0.25, 0.5] - @test getp(sol, (k1, k2))(sol) == getp(sol, (model.k1, model.k2))(sol) == getp(sol, (:k1, :k2))(sol) == (0.25, 0.5) + @test getp(sol, [k1,k2])(sol) == getp(sol, [model.k1,model.k2])(sol) == getp(sol, [:k1,:k2])(sol) == [0.25, 0.5] + @test getp(sol, (k1,k2))(sol) == getp(sol, (model.k1,model.k2))(sol) == getp(sol, (:k1,:k2))(sol) == (0.25, 0.5) end # Handles nonlinear and steady state solutions differently. @@ -263,20 +263,20 @@ let # Get u values. @test sol[X] == sol[model.X] == sol[:X] @test sol[XY] == sol[model.XY][1] == sol[:XY] - @test sol[[XY, Y]] == sol[[model.XY, model.Y]] == sol[[:XY, :Y]] - @test sol[(XY, Y)] == sol[(model.XY, model.Y)] == sol[(:XY, :Y)] + @test sol[[XY,Y]] == sol[[model.XY,model.Y]] == sol[[:XY,:Y]] + @test sol[(XY,Y)] == sol[(model.XY,model.Y)] == sol[(:XY,:Y)] @test getu(sol, X)(sol) == getu(sol, model.X)(sol)[1] == getu(sol, :X)(sol) @test getu(sol, XY)(sol) == getu(sol, model.XY)(sol)[1] == getu(sol, :XY)(sol) - @test getu(sol, [XY, Y])(sol) == getu(sol, [model.XY, model.Y])(sol) == getu(sol, [:XY, :Y])(sol) - @test getu(sol, (XY, Y))(sol) == getu(sol, (model.XY, model.Y))(sol) == getu(sol, (:XY, :Y))(sol) + @test getu(sol, [XY,Y])(sol) == getu(sol, [model.XY,model.Y])(sol) == getu(sol, [:XY,:Y])(sol) + @test getu(sol, (XY,Y))(sol) == getu(sol, (model.XY,model.Y))(sol) == getu(sol, (:XY,:Y))(sol) # Get p values. @test sol.ps[kp] == sol.ps[model.kp] == sol.ps[:kp] - @test sol.ps[[k1, k2]] == sol.ps[[model.k1, model.k2]] == sol.ps[[:k1, :k2]] - @test sol.ps[(k1, k2)] == sol.ps[(model.k1, model.k2)] == sol.ps[(:k1, :k2)] + @test sol.ps[[k1,k2]] == sol.ps[[model.k1,model.k2]] == sol.ps[[:k1,:k2]] + @test sol.ps[(k1,k2)] == sol.ps[(model.k1,model.k2)] == sol.ps[(:k1,:k2)] @test getp(sol, kp)(sol) == getp(sol, model.kp)(sol) == getp(sol, :kp)(sol) - @test getp(sol, [k1, k2])(sol) == getp(sol, [model.k1, model.k2])(sol) == getp(sol, [:k1, :k2])(sol) - @test getp(sol, (k1, k2))(sol) == getp(sol, (model.k1, model.k2))(sol) == getp(sol, (:k1, :k2))(sol) + @test getp(sol, [k1,k2])(sol) == getp(sol, [model.k1,model.k2])(sol) == getp(sol, [:k1,:k2])(sol) + @test getp(sol, (k1,k2))(sol) == getp(sol, (model.k1,model.k2))(sol) == getp(sol, (:k1,:k2))(sol) end end end @@ -293,12 +293,12 @@ let @test length(plot(sol; idxs = :XY).series_list) == 1 # As vector. - @test length(plot(sol; idxs = [X, Y]).series_list) == 2 - @test length(plot(sol; idxs = [XY, Y]).series_list) == 2 - @test length(plot(sol; idxs = [model.X, model.Y]).series_list) == 2 - @test length(plot(sol; idxs = [model.XY, model.Y]).series_list) == 2 - @test length(plot(sol; idxs = [:X, :Y]).series_list) == 2 - @test length(plot(sol; idxs = [:XY, :Y]).series_list) == 2 + @test length(plot(sol; idxs = [X,Y]).series_list) == 2 + @test length(plot(sol; idxs = [XY,Y]).series_list) == 2 + @test length(plot(sol; idxs = [model.X,model.Y]).series_list) == 2 + @test length(plot(sol; idxs = [model.XY,model.Y]).series_list) == 2 + @test length(plot(sol; idxs = [:X,:Y]).series_list) == 2 + @test length(plot(sol; idxs = [:XY,:Y]).series_list) == 2 # As tuple. @test length(plot(sol; idxs = (X, Y)).series_list) == 1 @@ -351,14 +351,10 @@ end prob6 = remake(prob1, u0 = [Y2 => 40.0], p = [k1 => 0.4]) prob7 = remake(prob1, u0 = [X1 => 10.0, X2 => 20.0], p = [V0 => 50.0]) prob8 = remake(prob1, u0 = [W => 60.0]) - prob9 = remake( - prob2; u0 = [X2 => nothing, Y2 => nothing], - p = [Γ => [10.0, 20.0]] - ) - prob10 = remake( - prob1; u0 = [Y1 => 20.0, Y2 => nothing, X2 => nothing], - p = [Γ => [20.0, 30.0], k1 => 0.4] - ) + prob9 = remake(prob2; u0 = [X2 => nothing, Y2 => nothing], + p = [Γ => [10.0, 20.0]]) + prob10 = remake(prob1; u0 = [Y1 => 20.0, Y2 => nothing, X2 => nothing], + p = [Γ => [20.0, 30.0], k1 => 0.4]) prob11 = remake(prob10, u0 = [X1 => 10.0], p = [k2 => 0.5]) # Creates a testing function. @@ -380,61 +376,39 @@ end # Checks that all problem values are correct. Γ = prob1.f.sys.Γ - test_vals( - prob1, + test_vals(prob1, Dict(X1 => 1.0, X2 => 2.0, Y1 => 3.0, Y2 => 4.0, V => 3.0, W => 6.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 3.0, Γ[2] => 7.0) - ) - test_vals( - prob2, + Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 3.0, Γ[2] => 7.0)) + test_vals(prob2, Dict(X1 => 10.0, X2 => 2.0, Y1 => 3.0, Y2 => 4.0, V => 3.0, W => 6.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 12.0, Γ[2] => 7.0) - ) - test_vals( - prob3, + Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 12.0, Γ[2] => 7.0)) + test_vals(prob3, Dict(X1 => 10.0, X2 => 20.0, Y1 => 3.0, Y2 => 4.0, V => 3.0, W => 6.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 30.0, Γ[2] => 7.0) - ) - test_vals( - prob4, + Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 30.0, Γ[2] => 7.0)) + test_vals(prob4, Dict(X1 => 1.0, X2 => 20.0, Y1 => 30.0, Y2 => 4.0, V => 3.0, W => 6.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 21.0, Γ[2] => 34.0) - ) - test_vals( - prob5, + Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 21.0, Γ[2] => 34.0)) + test_vals(prob5, Dict(X1 => 10.0, X2 => 20.0, Y1 => 3.0, Y2 => 4.0, V => 3.0, W => 6.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 30.0, Γ[2] => 7.0) - ) - test_vals( - prob6, + Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 30.0, Γ[2] => 7.0)) + test_vals(prob6, Dict(X1 => 1.0, X2 => 2.0, Y1 => 3.0, Y2 => 40.0, V => 3.0, W => 6.0), - Dict(k1 => 0.4, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 3.0, Γ[2] => 43.0) - ) - test_vals( - prob7, + Dict(k1 => 0.4, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 3.0, Γ[2] => 43.0)) + test_vals(prob7, Dict(X1 => 10.0, X2 => 20.0, Y1 => 3.0, Y2 => 4.0, V => 50.0, W => 6.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 50.0, v => 50.0, w => 6.0, Γ[1] => 30.0, Γ[2] => 7.0) - ) - test_vals( - prob8, + Dict(k1 => 0.1, k2 => 0.2, V0 => 50.0, v => 50.0, w => 6.0, Γ[1] => 30.0, Γ[2] => 7.0)) + test_vals(prob8, Dict(X1 => 1.0, X2 => 2.0, Y1 => 3.0, Y2 => 4.0, V => 3.0, W => 60.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 60.0, Γ[1] => 3.0, Γ[2] => 7.0) - ) - test_vals( - prob9, + Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 60.0, Γ[1] => 3.0, Γ[2] => 7.0)) + test_vals(prob9, Dict(X1 => 10.0, X2 => 0.0, Y1 => 3.0, Y2 => 17.0, V => 3.0, W => 6.0), - Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 10.0, Γ[2] => 20.0) - ) - test_vals( - prob10, + Dict(k1 => 0.1, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 10.0, Γ[2] => 20.0)) + test_vals(prob10, Dict(X1 => 1.0, X2 => 19.0, Y1 => 20.0, Y2 => 10.0, V => 3.0, W => 6.0), - Dict(k1 => 0.4, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 20.0, Γ[2] => 30.0) - ) - test_vals( - prob11, + Dict(k1 => 0.4, k2 => 0.2, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 20.0, Γ[2] => 30.0)) + test_vals(prob11, Dict(X1 => 10.0, X2 => 10.0, Y1 => 20.0, Y2 => 10.0, V => 3.0, W => 6.0), - Dict(k1 => 0.4, k2 => 0.5, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 20.0, Γ[2] => 30.0) - ) + Dict(k1 => 0.4, k2 => 0.5, V0 => 3.0, v => 3.0, w => 6.0, Γ[1] => 20.0, Γ[2] => 30.0)) end end @@ -448,7 +422,7 @@ end let # Creates the model. rn = @reaction_network begin - p1 * p2, A + B --> C + p1*p2, A + B --> C end @unpack p1, p2 = rn