From 1c10a9d73ceff21a4c65d4bd5838d590063e17ce Mon Sep 17 00:00:00 2001 From: Torkel Loman Date: Fri, 11 Apr 2025 17:19:29 +0100 Subject: [PATCH 1/2] init --- src/dsl.jl | 14 ++++- src/expression_utils.jl | 11 ++++ src/reactionsystem.jl | 4 +- .../serialisation_support.jl | 48 +++++++------- test/visualisation/latexify.jl | 63 ++++++++++++++++--- 5 files changed, 104 insertions(+), 36 deletions(-) diff --git a/src/dsl.jl b/src/dsl.jl index 380eeaa1a0..ac9a74138d 100644 --- a/src/dsl.jl +++ b/src/dsl.jl @@ -23,11 +23,13 @@ macro species(ex...) lastarg = vars.args[end] # start adding metadata statements where the vector of symbols was previously declared + # adds `[latexwrapper = string]` metadata which improves latexify printing. idx = length(vars.args) resize!(vars.args, idx + length(lastarg.args) + 1) for sym in lastarg.args - vars.args[idx] = :($sym = ModelingToolkit.wrap(setmetadata( - ModelingToolkit.value($sym), Catalyst.VariableSpecies, true))) + vars.args[idx] = :($sym = ModelingToolkit.wrap(setmetadata(setmetadata( + ModelingToolkit.value($sym), Catalyst.VariableSpecies, true), + Symbolics.SymLatexWrapper, string))) idx += 1 end @@ -519,6 +521,10 @@ function get_psexpr(parameters_extracted, options) :(@parameters) end foreach(p -> push!(pexprs.args, p), parameters_extracted) + + # Adds the `SymLatexWrapper` metadata to all parameter's declaration. + insert_metadata!(pexprs, [:(latexwrapper = string)]) + pexprs end @@ -536,6 +542,10 @@ function get_usexpr(us_extracted, options, key = :species; ivs = (DEFAULT_IV_SYM for u in us_extracted u isa Symbol && push!(usexpr.args, Expr(:call, u, ivs...)) end + + # For variables (but not species), adds the `SymLatexWrapper` metadata to all parameter's declaration. + (key == :variables) && insert_metadata!(usexpr, [:(latexwrapper = string)]) + usexpr end diff --git a/src/expression_utils.jl b/src/expression_utils.jl index b839844fcf..b6cb00ba00 100644 --- a/src/expression_utils.jl +++ b/src/expression_utils.jl @@ -134,3 +134,14 @@ function insert_independent_variable(expr_in, iv_expr) end return expr end + + +# For a symbolic variable declaration expression (@parameters, @variables, @species), inserts +# metadata to the declaration (each metadata in input vector is inserted). E.g. +# @parameters X(t) [description = "A species"] Y(t) +# becomes +# @parameters X(t) [description = "A species", new_md=new_md_value] Y(t) [new_md=new_md_value] +# handles expressions with/without begin/end statements. +function insert_metadata!(symexprs, mds::Vector{Expr}) + +end diff --git a/src/reactionsystem.jl b/src/reactionsystem.jl index ca11373820..648f646ec8 100644 --- a/src/reactionsystem.jl +++ b/src/reactionsystem.jl @@ -1445,7 +1445,7 @@ function MT.flatten(rs::ReactionSystem; name = nameof(rs)) end function complete_check(sys, method) - if MT.iscomplete(sys) + 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 @@ -1506,7 +1506,7 @@ function ModelingToolkit.extend(sys::MT.AbstractSystem, rs::ReactionSystem; complete_check(sys, "ModelingToolkit.extend") complete_check(rs, "ModelingToolkit.extend") - + any(T -> sys isa T, (ReactionSystem, ODESystem, NonlinearSystem)) || error("ReactionSystems can only be extended with ReactionSystems, ODESystems and NonlinearSystems currently. Received a $(typeof(sys)) system.") diff --git a/src/reactionsystem_serialisation/serialisation_support.jl b/src/reactionsystem_serialisation/serialisation_support.jl index fd552074e8..b057d991f2 100644 --- a/src/reactionsystem_serialisation/serialisation_support.jl +++ b/src/reactionsystem_serialisation/serialisation_support.jl @@ -75,7 +75,7 @@ function expression_2_string(expr; return repr(strip_called_expr) end -# Converts a vector of symbolics (e.g. the species or parameter vectors) to a string vector. Strips +# Converts a vector of symbolics (e.g. the species or parameter vectors) to a string vector. Strips # any calls (e.g. X(t) becomes X). E.g. a species vector [X, Y, Z] is converted to "[X, Y, Z]". function syms_2_strings(syms) strip_called_syms = [strip_call(Symbolics.unwrap(sym)) for sym in syms] @@ -150,6 +150,7 @@ x_2_string(x::Symbol) = ":$x" x_2_string(x::Number) = string(x) x_2_string(x::Pair) = "$(x_2_string(x[1])) => $(x_2_string(x[2]))" x_2_string(x::Nothing) = "nothing" +x_2_string(x::Function) = String(Symbol(x)) function x_2_string(x::Vector) output = "[" for val in x @@ -188,11 +189,11 @@ end ### Symbolics Metadata Handling ### -# For a Symbolic, retrieve all metadata that needs to be added to its declaration. Certain metadata +# For a Symbolic, retrieve all metadata that needs to be added to its declaration. Certain metadata # (such as default values and whether a variable is a species or not) are skipped (these are stored # in the `SKIPPED_METADATA` constant). # Because it is impossible to retrieve the keyword used to declare individual metadata from the -# metadata entry, these must be stored manually (in `RECOGNISED_METADATA`). If one of these are +# metadata entry, these must be stored manually (in `RECOGNISED_METADATA`). If one of these are # encountered, a warning is thrown and it is skipped (we could also throw an error). I have asked # Shashi, and he claims there is not alternative (general) solution. function get_metadata_to_declare(sym) @@ -205,31 +206,32 @@ function get_metadata_to_declare(sym) return metadata_keys end -# Converts a given metadata into the string used to declare it. +# Converts a given metadata into the string used to declare it. function metadata_2_string(sym, metadata) return RECOGNISED_METADATA[metadata] * " = " * x_2_string(sym.metadata[metadata]) 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" - ModelingToolkit.VariableDescription => "description" - ModelingToolkit.VariableBounds => "bounds" - ModelingToolkit.VariableUnit => "unit" - ModelingToolkit.VariableConnectType => "connect" - ModelingToolkit.VariableNoiseType => "noise" - ModelingToolkit.VariableInput => "input" - ModelingToolkit.VariableOutput => "output" - ModelingToolkit.VariableIrreducible => "irreducible" - ModelingToolkit.VariableStatePriority => "state_priority" - ModelingToolkit.VariableMisc => "misc" - ModelingToolkit.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", + ModelingToolkit.VariableDescription => "description", + ModelingToolkit.VariableBounds => "bounds", + ModelingToolkit.VariableUnit => "unit", + ModelingToolkit.VariableConnectType => "connect", + ModelingToolkit.VariableNoiseType => "noise", + ModelingToolkit.VariableInput => "input", + ModelingToolkit.VariableOutput => "output", + ModelingToolkit.VariableIrreducible => "irreducible", + ModelingToolkit.VariableStatePriority => "state_priority", + ModelingToolkit.VariableMisc => "misc", + ModelingToolkit.TimeDomain => "timedomain", + Symbolics.SymLatexWrapper => "latexwrapper"]) # List of metadata that does not need to be explicitly declared to be added (or which is handled separately). const SKIPPED_METADATA = [ModelingToolkit.MTKVariableTypeCtx, Symbolics.VariableSource, @@ -237,7 +239,7 @@ const SKIPPED_METADATA = [ModelingToolkit.MTKVariableTypeCtx, Symbolics.Variable ### Generic Expression Handling ### -# Potentially strips the call for a symbolics. E.g. X(t) becomes X (but p remains p). This is used +# Potentially strips the call for a symbolics. E.g. X(t) becomes X (but p remains p). This is used # when variables are written to files, as in code they are used without the call part. function strip_call(sym) return iscall(sym) ? Sym{Real}(Symbolics.getname(sym)) : sym diff --git a/test/visualisation/latexify.jl b/test/visualisation/latexify.jl index 550288a109..f7e02aadb1 100644 --- a/test/visualisation/latexify.jl +++ b/test/visualisation/latexify.jl @@ -67,7 +67,7 @@ raw"\begin{align*} \mathrm{X3} &\xrightarrow{d3} \varnothing \\ \mathrm{X4} &\xrightarrow{d4} \varnothing \\ \mathrm{X5} &\xrightarrow{d5} \varnothing \\ -\mathrm{X6} &\xrightarrow{d6} \varnothing +\mathrm{X6} &\xrightarrow{d6} \varnothing \end{align*} ", "\r\n"=>"\n") @@ -88,10 +88,10 @@ raw"\begin{align*} \mathrm{X3} &\xrightarrow{d3} \varnothing \\ \mathrm{X4} &\xrightarrow{d4} \varnothing \\ \mathrm{X5} &\xrightarrow{d5} \varnothing \\ -\mathrm{X6} &\xrightarrow{d6} \varnothing +\mathrm{X6} &\xrightarrow{d6} \varnothing \end{align*} ", "\r\n"=>"\n") - + # Latexify.@generate_test latexify(rn, mathjax = false) @test latexify(rn, mathjax = false) == replace( raw"\begin{align*} @@ -109,7 +109,7 @@ raw"\begin{align*} \mathrm{X3} &\xrightarrow{d3} \varnothing \\ \mathrm{X4} &\xrightarrow{d4} \varnothing \\ \mathrm{X5} &\xrightarrow{d5} \varnothing \\ -\mathrm{X6} &\xrightarrow{d6} \varnothing +\mathrm{X6} &\xrightarrow{d6} \varnothing \end{align*} ", "\r\n"=>"\n") end @@ -127,7 +127,7 @@ let raw"\begin{align*} \varnothing &\xrightleftharpoons[d_{a}]{\frac{p_{a} B^{n}}{k^{n} + B^{n}}} \mathrm{A} \\ \varnothing &\xrightleftharpoons[d_{b}]{p_{b}} \mathrm{B} \\ -3 \mathrm{B} &\xrightleftharpoons[r_{b}]{r_{a}} \mathrm{A} +3 \mathrm{B} &\xrightleftharpoons[r_{b}]{r_{a}} \mathrm{A} \end{align*} ", "\r\n"=>"\n") @@ -136,7 +136,7 @@ raw"\begin{align*} raw"\begin{align*} \varnothing &\xrightleftharpoons[d_{a}]{\frac{p_{a} B^{n}}{k^{n} + B^{n}}} \mathrm{A} \\ \varnothing &\xrightleftharpoons[d_{b}]{p_{b}} \mathrm{B} \\ -3 \mathrm{B} &\xrightleftharpoons[r_{b}]{r_{a}} \mathrm{A} +3 \mathrm{B} &\xrightleftharpoons[r_{b}]{r_{a}} \mathrm{A} \end{align*} ", "\r\n"=>"\n") end @@ -150,7 +150,7 @@ let # Latexify.@generate_test latexify(rn) @test latexify(rn) == replace( raw"\begin{align*} -\varnothing &\xrightarrow{p} (m + n)\mathrm{X} +\varnothing &\xrightarrow{p} (m + n)\mathrm{X} \end{align*} ", "\r\n"=>"\n") end @@ -241,7 +241,7 @@ let # Latexify.@generate_test latexify(rn) @test latexify(rn) == replace( raw"\begin{align*} -\mathrm{Y} &\xrightarrow{Y k} \varnothing +\mathrm{Y} &\xrightarrow{Y k} \varnothing \end{align*} ", "\r\n"=>"\n") end @@ -258,7 +258,7 @@ let @test latexify(rn) == replace( raw"\begin{align*} \varnothing &\xrightleftharpoons[\frac{d}{V\left( t \right)}]{\frac{p}{V\left( t \right)}} \mathrm{X} \\ -\frac{\mathrm{d} V\left( t \right)}{\mathrm{d}t} &= X\left( t \right) - V\left( t \right) +\frac{\mathrm{d} V\left( t \right)}{\mathrm{d}t} &= X\left( t \right) - V\left( t \right) \end{align*} ", "\r\n"=>"\n") end @@ -280,3 +280,48 @@ let # Latexify.@generate_test latexify(extended, mathjax=false) @test_broken false end + +# Checks that the `SymLatexWrapper` metadata option is correctly added to all symbolics created +# by the DSL. Checks that this is also the case for species created through the `@species` macro. +let + # Via the DSL (@reaction_network). + rn1 = @reaction_network begin + @species Y(t) + @variables W(t) + @parameters p + @equations D(V) ~ - V + k1, 0 --> X + end + @test all(hasmetadata(u, Symbolics.SymLatexWrapper) && (getmetadata(u, Symbolics.SymLatexWrapper) == string) for u in unknowns(rn1)) + @test all(hasmetadata(p, Symbolics.SymLatexWrapper) && (getmetadata(p, Symbolics.SymLatexWrapper) == string) for p in parameters(rn1)) + + # Via the DSL but using `begin .. end` in declarations and additional options. + rn2 = @reaction_network begin + @species begin + Y(t), [description = "A species"] + Z(t) = 1.0 + end + @variables begin + W(t), [description = "A variable"] + U(t) = 2.0 + end + @variables begin + q(t), [description = "A parameter"] + r(t) = 3.0 + end + @equations D(V) ~ - V + k1, 0 --> X + end + @test all(hasmetadata(u, Symbolics.SymLatexWrapper) && (getmetadata(u, Symbolics.SymLatexWrapper) == string) for u in unknowns(rn2)) + @test all(hasmetadata(p, Symbolics.SymLatexWrapper) && (getmetadata(p, Symbolics.SymLatexWrapper) == string) for p in parameters(rn2)) + + # Via the DSL (@reaction). + rx = @reaction p, 0 --> x + hasmetadata(rx.rate, Symbolics.SymLatexWrapper) && (getmetadata(rx.rate, Symbolics.SymLatexWrapper) == string) + hasmetadata(rx.products[1], Symbolics.SymLatexWrapper) && (getmetadata(rx.products[1], Symbolics.SymLatexWrapper) == string) + + # Via the @species. + t = default_t() + @species XYZ(t) + getmetadata(XYZ, Symbolics.SymLatexWrapper) == string +end From fe2fc4566d167c9794510a311df079385664ca88 Mon Sep 17 00:00:00 2001 From: Torkel Loman Date: Sat, 12 Apr 2025 14:07:41 +0100 Subject: [PATCH 2/2] up --- src/dsl.jl | 8 ------- src/expression_utils.jl | 11 --------- .../serialisation_support.jl | 2 +- test/visualisation/latexify.jl | 24 +++++++++++-------- 4 files changed, 15 insertions(+), 30 deletions(-) diff --git a/src/dsl.jl b/src/dsl.jl index ac9a74138d..3fbdfb5f7c 100644 --- a/src/dsl.jl +++ b/src/dsl.jl @@ -521,10 +521,6 @@ function get_psexpr(parameters_extracted, options) :(@parameters) end foreach(p -> push!(pexprs.args, p), parameters_extracted) - - # Adds the `SymLatexWrapper` metadata to all parameter's declaration. - insert_metadata!(pexprs, [:(latexwrapper = string)]) - pexprs end @@ -542,10 +538,6 @@ function get_usexpr(us_extracted, options, key = :species; ivs = (DEFAULT_IV_SYM for u in us_extracted u isa Symbol && push!(usexpr.args, Expr(:call, u, ivs...)) end - - # For variables (but not species), adds the `SymLatexWrapper` metadata to all parameter's declaration. - (key == :variables) && insert_metadata!(usexpr, [:(latexwrapper = string)]) - usexpr end diff --git a/src/expression_utils.jl b/src/expression_utils.jl index b6cb00ba00..b839844fcf 100644 --- a/src/expression_utils.jl +++ b/src/expression_utils.jl @@ -134,14 +134,3 @@ function insert_independent_variable(expr_in, iv_expr) end return expr end - - -# For a symbolic variable declaration expression (@parameters, @variables, @species), inserts -# metadata to the declaration (each metadata in input vector is inserted). E.g. -# @parameters X(t) [description = "A species"] Y(t) -# becomes -# @parameters X(t) [description = "A species", new_md=new_md_value] Y(t) [new_md=new_md_value] -# handles expressions with/without begin/end statements. -function insert_metadata!(symexprs, mds::Vector{Expr}) - -end diff --git a/src/reactionsystem_serialisation/serialisation_support.jl b/src/reactionsystem_serialisation/serialisation_support.jl index b057d991f2..cf612e66a9 100644 --- a/src/reactionsystem_serialisation/serialisation_support.jl +++ b/src/reactionsystem_serialisation/serialisation_support.jl @@ -234,7 +234,7 @@ const RECOGNISED_METADATA = Dict([Catalyst.ParameterConstantSpecies => "isconsta Symbolics.SymLatexWrapper => "latexwrapper"]) # List of metadata that does not need to be explicitly declared to be added (or which is handled separately). -const SKIPPED_METADATA = [ModelingToolkit.MTKVariableTypeCtx, Symbolics.VariableSource, +const SKIPPED_METADATA = [ModelingToolkit.MTKVariableTypeCtx, Symbolics.SymLatexWrapper, Symbolics.VariableSource, Symbolics.VariableDefaultValue, Catalyst.VariableSpecies] ### Generic Expression Handling ### diff --git a/test/visualisation/latexify.jl b/test/visualisation/latexify.jl index f7e02aadb1..3a2c34e8b4 100644 --- a/test/visualisation/latexify.jl +++ b/test/visualisation/latexify.jl @@ -283,6 +283,7 @@ end # Checks that the `SymLatexWrapper` metadata option is correctly added to all symbolics created # by the DSL. Checks that this is also the case for species created through the `@species` macro. +# Currently only implemented for species. let # Via the DSL (@reaction_network). rn1 = @reaction_network begin @@ -292,36 +293,39 @@ let @equations D(V) ~ - V k1, 0 --> X end - @test all(hasmetadata(u, Symbolics.SymLatexWrapper) && (getmetadata(u, Symbolics.SymLatexWrapper) == string) for u in unknowns(rn1)) - @test all(hasmetadata(p, Symbolics.SymLatexWrapper) && (getmetadata(p, Symbolics.SymLatexWrapper) == string) for p in parameters(rn1)) + @test_broken all(hasmetadata(u, Symbolics.SymLatexWrapper) && (getmetadata(u, Symbolics.SymLatexWrapper) == string) for u in unknowns(rn1)) + @test_broken all(hasmetadata(p, Symbolics.SymLatexWrapper) && (getmetadata(p, Symbolics.SymLatexWrapper) == string) for p in parameters(rn1)) # Via the DSL but using `begin .. end` in declarations and additional options. rn2 = @reaction_network begin @species begin + X(t) Y(t), [description = "A species"] Z(t) = 1.0 end @variables begin + V(t) W(t), [description = "A variable"] U(t) = 2.0 end - @variables begin - q(t), [description = "A parameter"] - r(t) = 3.0 + @parameters begin + k1 + q, [description = "A parameter"] + r = 3.0 end @equations D(V) ~ - V k1, 0 --> X end - @test all(hasmetadata(u, Symbolics.SymLatexWrapper) && (getmetadata(u, Symbolics.SymLatexWrapper) == string) for u in unknowns(rn2)) - @test all(hasmetadata(p, Symbolics.SymLatexWrapper) && (getmetadata(p, Symbolics.SymLatexWrapper) == string) for p in parameters(rn2)) + @test_broken all(hasmetadata(u, Symbolics.SymLatexWrapper) && (getmetadata(u, Symbolics.SymLatexWrapper) == string) for u in unknowns(rn2)) + @test_broken all(hasmetadata(p, Symbolics.SymLatexWrapper) && (getmetadata(p, Symbolics.SymLatexWrapper) == string) for p in parameters(rn2)) # Via the DSL (@reaction). rx = @reaction p, 0 --> x - hasmetadata(rx.rate, Symbolics.SymLatexWrapper) && (getmetadata(rx.rate, Symbolics.SymLatexWrapper) == string) - hasmetadata(rx.products[1], Symbolics.SymLatexWrapper) && (getmetadata(rx.products[1], Symbolics.SymLatexWrapper) == string) + @test_broken hasmetadata(rx.rate, Symbolics.SymLatexWrapper) && (getmetadata(rx.rate, Symbolics.SymLatexWrapper) == string) + @test hasmetadata(rx.products[1], Symbolics.SymLatexWrapper) && (getmetadata(rx.products[1], Symbolics.SymLatexWrapper) == string) # Via the @species. t = default_t() @species XYZ(t) - getmetadata(XYZ, Symbolics.SymLatexWrapper) == string + @test hasmetadata(XYZ, Symbolics.SymLatexWrapper) && (getmetadata(XYZ, Symbolics.SymLatexWrapper) == string) end