Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Equations and events reupload #801

Merged
merged 46 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
afced2c
Initiate
TorkelE Mar 31, 2024
a299c84
init
TorkelE Apr 1, 2024
0cf2781
adds the functions for managing equations
TorkelE Apr 1, 2024
c9d98cc
save progress
TorkelE Apr 3, 2024
fa5a235
Finish events and add test
TorkelE Apr 4, 2024
bd00bd2
hybrid equation tests
TorkelE Apr 5, 2024
8a1baf6
Fixes extension support, add more test, fix conversion bug
TorkelE Apr 5, 2024
2309a1b
save changes
TorkelE Apr 5, 2024
fcae9d2
save progress
TorkelE Apr 5, 2024
7b816cc
save progress
TorkelE Apr 5, 2024
164b5d3
Add tests for differentials in dsl
TorkelE Apr 5, 2024
1e97178
up
TorkelE Apr 6, 2024
a838348
up
TorkelE Apr 10, 2024
30eb2af
mtk version update
TorkelE Apr 10, 2024
b22ca4c
up
TorkelE Apr 10, 2024
5ec6ea7
up
TorkelE Apr 10, 2024
a977cf0
writing fix
TorkelE Apr 10, 2024
ce12c32
up
TorkelE Apr 10, 2024
0501f6e
broken test fixed
TorkelE Apr 10, 2024
f3666c2
up
TorkelE Apr 10, 2024
2ba8bcb
format fixes
TorkelE Apr 10, 2024
1f6718c
up
TorkelE Apr 11, 2024
0e13372
up
TorkelE Apr 11, 2024
2187e2f
minor format fix
TorkelE Apr 12, 2024
dcefd62
revert if statement
TorkelE Apr 12, 2024
063ed25
up required MTK version
TorkelE Apr 13, 2024
46af9a2
updates
TorkelE Apr 19, 2024
a3c9b83
up
TorkelE Apr 19, 2024
4ea6733
update
TorkelE Apr 19, 2024
0dea5e7
Update docs/src/catalyst_functionality/dsl_description.md
TorkelE May 8, 2024
1ca47a2
Update src/reactionsystem.jl
TorkelE May 8, 2024
8a719d4
up
TorkelE May 8, 2024
5f865d3
up for MTK regression
TorkelE May 8, 2024
696fc00
up
TorkelE May 8, 2024
0dc58c8
handle observed elimination
TorkelE May 8, 2024
f72bb9c
limit julia version
TorkelE May 8, 2024
e286872
fix julia version compat
TorkelE May 8, 2024
f1f116b
Better handling of julai test version restriction
TorkelE May 9, 2024
19c976c
up
TorkelE May 9, 2024
b9d573d
revert loop
TorkelE May 9, 2024
9c8e5c2
mark test as broken (@unpack does not work on observables)
TorkelE May 9, 2024
bc93f41
up
TorkelE May 9, 2024
01fcab7
fix
TorkelE May 9, 2024
74a8881
fix2
TorkelE May 9, 2024
85e708e
observabes fix
TorkelE May 9, 2024
8601ed1
test no longer broken
TorkelE May 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ version = "13.5.1"
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
DynamicPolynomials = "7c1d4256-1411-5781-91ec-d7bc3513ac07"
isaacsas marked this conversation as resolved.
Show resolved Hide resolved
DynamicQuantities = "06fc5a27-2a28-4c7c-a15d-362465fb6821"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
JumpProcesses = "ccbc3e58-028d-4f4c-8cd5-9ae44345cda5"
Expand Down Expand Up @@ -46,20 +47,21 @@ JumpProcesses = "9.3.2"
LaTeXStrings = "1.3.0"
Latexify = "0.14, 0.15, 0.16"
MacroTools = "0.5.5"
ModelingToolkit = "9.7.1"
ModelingToolkit = "9.10.0"
Parameters = "0.12"
Reexport = "0.2, 1.0"
Requires = "1.0"
RuntimeGeneratedFunctions = "0.5.12"
Setfield = "1"
StructuralIdentifiability = "0.5.1"
SymbolicUtils = "1.0.3"
Symbolics = "5.14"
Symbolics = "5.27"
isaacsas marked this conversation as resolved.
Show resolved Hide resolved
Unitful = "1.12.4"
julia = "1.9"

[extras]
BifurcationKit = "0f109fa4-8a5d-4b75-95aa-f515264e7665"
DiffEqCallbacks = "459566f4-90b8-5000-8ac3-15dfb0a30def"
isaacsas marked this conversation as resolved.
Show resolved Hide resolved
DomainSets = "5b8099bc-c8ec-5219-889f-1d9e522a28bf"
Graphviz_jll = "3c863552-8265-54e4-a6dc-903eb78fde85"
HomotopyContinuation = "f213a82b-91d6-5c5d-acf7-10f1c761b327"
Expand All @@ -80,4 +82,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"

[targets]
test = ["BifurcationKit", "DomainSets", "Graphviz_jll", "HomotopyContinuation", "NonlinearSolve", "OrdinaryDiffEq", "Plots", "Random", "SafeTestsets", "SciMLBase", "SciMLNLSolve", "StableRNGs", "Statistics", "SteadyStateDiffEq", "StochasticDiffEq", "StructuralIdentifiability", "Test", "Unitful"]
test = ["BifurcationKit", "DiffEqCallbacks", "DomainSets", "Graphviz_jll", "HomotopyContinuation", "NonlinearSolve", "OrdinaryDiffEq", "Plots", "Random", "SafeTestsets", "SciMLBase", "SciMLNLSolve", "StableRNGs", "Statistics", "SteadyStateDiffEq", "StochasticDiffEq", "StructuralIdentifiability", "Test", "Unitful"]
2 changes: 1 addition & 1 deletion docs/src/catalyst_functionality/constraint_equations.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ $\lambda$. For now we'll assume the cell can grow indefinitely. We'll also keep
track of one protein $P(t)$, which is produced at a rate proportional $V$ and
can be degraded.

## [Coupling ODE constraints via extending a system](@id constraint_equations_coupling_constratins)
## [Coupling ODE constraints via extending a system](@id constraint_equations_coupling_constraints)

There are several ways we can create our Catalyst model with the two reactions
and ODE for $V(t)$. One approach is to use compositional modeling, create
Expand Down
63 changes: 63 additions & 0 deletions docs/src/catalyst_functionality/dsl_description.md
Original file line number Diff line number Diff line change
Expand Up @@ -701,3 +701,66 @@ rn = @reaction_network begin
end
nothing # hide
```

## Incorporating (differential) equations into reaction network models
isaacsas marked this conversation as resolved.
Show resolved Hide resolved
Some models cannot be purely described as reaction networks. E.g. consider the growth of a cell, where the rate of change in cell's volume depends on some growth factor. Here, the cell's volume would be described by a normal equation. Such equations can be incorporated into a model using the `@equations` option. Here, we create a model where a growth factor ($G$) is produced and degraded at a linear rates, and the rate of change in cell volume ($V$) os linear to the amount of growth factor:
TorkelE marked this conversation as resolved.
Show resolved Hide resolved
```@example eqs1
using Catalyst #hide
rn = @reaction_network begin
@equations begin
D(V) ~ G
end
(p,d), 0 <--> G
isaacsas marked this conversation as resolved.
Show resolved Hide resolved
end
```
Here, `D(V)` indicates the (time) derivative with respect to `D`. The differential equation left and right hand sides are separated by a `~`. The left-hand side should contain differential only, the right hand side can contain any algebraic expression.

We can check the differential equation corresponding to this reaction network using latexify:
```@example eqs1
using Latexify
latexify(rn; form=:ode)
```
We can also simulate it using the normal syntax
```@example eqs1
using DifferentialEquations, Plots # hide
u0 = [:G => 0.0, :V => 0.1]
ps = [:p => 1.0, :d => 0.5]
oprob = ODEProblem(rn, u0, (0.0, 1.0), ps)
sol = solve(oprob)
plot(sol)
```
Here, growth is indefinite. To improve the model, [a callback](@ref advanced_simulations_callbacks) can be used to half the volume (cell division) once some threshold is reached.

When creating differential equations this way, the subject of the differential is automatically inferred to be a variable, however, any component on the right-hand side must be declare somewhere in the macro. E.g. to add a scaling parameter ($k$), we must declare that $k$ is a parameter using the `@parameters` option:
```@example eqs1
rn = @reaction_network begin
@parameters k
@equations begin
D(V) ~ k*G
end
(p,d), 0 <--> G
end
nothing #hide
```
If the differential does not appear isolated on the lhs, its subject variable must also be explicitly declared (as it is not inferred for these cases).

It is possible to add several equations to the model. In this case, each have a separate line. E.g. to keep track of a supply of nutrition ($N$) in the growth media, we can use:
```@example eqs1
rn = @reaction_network begin
@equations begin
D(V) ~ G
D(N) ~ -G
end
(p,d), 0 <--> G
end
nothing #hide
```

When only a single equation is added, the `begin ... end` statement can be omitted. E.g., the first model can be declared equivalently using:
```@example eqs1
rn = @reaction_network begin
@equations D(V) ~ G
(p,d), 0 <--> G
end
nothing # hide
```
1 change: 1 addition & 0 deletions ext/CatalystHomotopyContinuationExtension.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module CatalystHomotopyContinuationExtension

# Fetch packages.
using Catalyst
import DynamicPolynomials
import ModelingToolkit as MT
import HomotopyContinuation as HC
import Setfield: @set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ function steady_state_polynomial(rs::ReactionSystem, ps, u0)
eqs_pars_funcs = vcat(equations(ns), conservedequations(rs))
eqs = map(eq -> substitute(eq.rhs - eq.lhs, p_dict), eqs_pars_funcs)
eqs_intexp = make_int_exps.(eqs)
return Catalyst.to_multivariate_poly(remove_denominators.(eqs_intexp))
ss_poly = Catalyst.to_multivariate_poly(remove_denominators.(eqs_intexp))
return poly_type_convert(ss_poly)
isaacsas marked this conversation as resolved.
Show resolved Hide resolved
end

# If u0s are not given while conservation laws are present, throws an error.
Expand Down Expand Up @@ -94,7 +95,7 @@ end
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.(species(rs)), var_names)
sort_pattern = indexin(MT.getname.(unknowns(rs)), var_names)
isaacsas marked this conversation as resolved.
Show resolved Hide resolved
foreach(sol -> permute!(sol, sort_pattern), sols)
end

Expand All @@ -104,4 +105,13 @@ function filter_negative_f(sols; neg_thres=-1e-20)
(neg_thres < sol[idx] < 0) && (sol[idx] = 0)
end
return filter(sol -> all(>=(0), sol), sols)
end

# Sometimes (when polynomials are created from hybrid 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}}}
isaacsas marked this conversation as resolved.
Show resolved Hide resolved
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
4 changes: 4 additions & 0 deletions src/Catalyst.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,15 @@ import ModelingToolkit: check_variables,
check_parameters, _iszero, _merge, check_units,
get_unit, check_equations, iscomplete

# Event-related MTK stuff.
import ModelingToolkit: PresetTimeCallback
TorkelE marked this conversation as resolved.
Show resolved Hide resolved

import Base: (==), hash, size, getindex, setindex, isless, Sort.defalg, length, show
import MacroTools, Graphs
import Graphs: DiGraph, SimpleGraph, SimpleDiGraph, vertices, edges, add_vertices!, nv, ne
import DataStructures: OrderedDict, OrderedSet
import Parameters: @with_kw_noshow
import Symbolics: occursin, wrap
isaacsas marked this conversation as resolved.
Show resolved Hide resolved

# globals for the modulate
function default_time_deriv()
Expand Down
7 changes: 7 additions & 0 deletions src/expression_utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ function get_tup_arg(ex::ExprValues, i::Int)
return ex.args[i]
end

# Some options takes input on form that is either `@option ...` or `@option begin ... end`.
# This transforms input of the latter form to the former (with only one line in the `begin ... end` block)
function option_block_form(expr)
(expr.head == :block) && return expr
return Expr(:block, expr)
end

isaacsas marked this conversation as resolved.
Show resolved Hide resolved
# In variable/species/parameters on the forms like:
# X
# X = 1.0
Expand Down
121 changes: 116 additions & 5 deletions src/reaction_network.jl
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ const forbidden_variables_error = let
end

# Declares the keys used for various options.
const option_keys = (:species, :parameters, :variables, :ivs, :compounds, :observables, :default_noise_scaling)
const option_keys = (:species, :parameters, :variables, :ivs, :compounds, :observables, :default_noise_scaling,
:differentials, :equations, :continuous_events, :discrete_events)
isaacsas marked this conversation as resolved.
Show resolved Hide resolved

### The @species macro, basically a copy of the @variables macro. ###
macro species(ex...)
Expand Down Expand Up @@ -353,20 +354,28 @@ function make_reaction_system(ex::Expr; name = :(gensym(:ReactionSystem)))
option_lines = Expr[x for x in ex.args if x.head == :macrocall]

# Get macro options.
length(unique(arg.args[1] for arg in option_lines)) < length(option_lines) && error("Some options where given multiple times.")
if length(unique(arg.args[1] for arg in option_lines)) < length(option_lines)
error("Some options where given multiple times.")
end
isaacsas marked this conversation as resolved.
Show resolved Hide resolved
options = Dict(map(arg -> Symbol(String(arg.args[1])[2:end]) => arg,
option_lines))

# Reads options.
default_reaction_metadata = :([])
check_default_noise_scaling!(default_reaction_metadata, options)
compound_expr, compound_species = read_compound_options(options)
continuous_events_expr = read_events_option(options, :continuous_events)
discrete_events_expr = read_events_option(options, :discrete_events)
isaacsas marked this conversation as resolved.
Show resolved Hide resolved

# Parses reactions, species, and parameters.
reactions = get_reactions(reaction_lines)
species_declared = [extract_syms(options, :species); compound_species]
parameters_declared = extract_syms(options, :parameters)
variables = extract_syms(options, :variables)
variables_declared = extract_syms(options, :variables)
isaacsas marked this conversation as resolved.
Show resolved Hide resolved

# Reads more options.
vars_extracted, add_default_diff, equations = read_equations_options(options, variables_declared)
variables = vcat(variables_declared, vars_extracted)
isaacsas marked this conversation as resolved.
Show resolved Hide resolved

# handle independent variables
if haskey(options, :ivs)
Expand All @@ -391,6 +400,9 @@ function make_reaction_system(ex::Expr; name = :(gensym(:ReactionSystem)))
species = vcat(species_declared, species_extracted)
parameters = vcat(parameters_declared, parameters_extracted)

# Create differential expression.
diffexpr = create_differential_expr(options, add_default_diff, [species; parameters; variables])

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creates an expression with all the differentials that should be declared as part of the DSL, e.g.

begin
    D = Differential(t)
end

# Checks for input errors.
(sum(length.([reaction_lines, option_lines])) != length(ex.args)) &&
error("@reaction_network input contain $(length(ex.args) - sum(length.([reaction_lines,option_lines]))) malformed lines.")
Expand All @@ -402,7 +414,7 @@ function make_reaction_system(ex::Expr; name = :(gensym(:ReactionSystem)))

# Creates expressions corresponding to actual code from the internal DSL representation.
sexprs = get_sexpr(species_extracted, options; iv_symbols = ivs)
vexprs = haskey(options, :variables) ? options[:variables] : :()
vexprs = get_sexpr(vars_extracted, options, :variables)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before we could just fetch the input to @variables, now we have to do like for species (where we have both an expression like that, and a few extracted ones)

pexprs = get_pexpr(parameters_extracted, options)
ps, pssym = scalarize_macro(!isempty(parameters), pexprs, "ps")
vars, varssym = scalarize_macro(!isempty(variables), vexprs, "vars")
Expand All @@ -412,6 +424,9 @@ function make_reaction_system(ex::Expr; name = :(gensym(:ReactionSystem)))
for reaction in reactions
push!(rxexprs.args, get_rxexprs(reaction))
end
for equation in equations
push!(rxexprs.args, equation)
end
isaacsas marked this conversation as resolved.
Show resolved Hide resolved

quote
$ps
Expand All @@ -420,11 +435,13 @@ function make_reaction_system(ex::Expr; name = :(gensym(:ReactionSystem)))
$sps
$observed_vars
$comps
$diffexpr

Catalyst.remake_ReactionSystem_internal(
Catalyst.make_ReactionSystem_internal(
$rxexprs, $tiv, setdiff(union($spssym, $varssym, $compssym), $obs_syms),
$pssym; name = $name, spatial_ivs = $sivs, observed = $observed_eqs);
$pssym; name = $name, spatial_ivs = $sivs, observed = $observed_eqs,
continuous_events = $continuous_events_expr, discrete_events = $discrete_events_expr);
default_reaction_metadata = $default_reaction_metadata
)
end
Expand Down Expand Up @@ -799,6 +816,100 @@ function make_observed_eqs(observables_expr)
end
end

# Reads the variables options. Outputs:
# `vars_extracted`: A vector with extracted variables (lhs in pure differential equations only).
# `dtexpr`: If a differentialequation is defined, the default derrivative (D ~ Differential(t)) must be defined.
# `equations`: a vector with the equations provided.
function read_equations_options(options, variables_declared)
# Prepares the equations. First, extracts equations from provided option (converting to block form if requried).
# Next, uses MTK's `parse_equations!` function to split input into a vector with the equations.
eqs_input = haskey(options, :equations) ? options[:equations].args[3] : :(begin end)
eqs_input = option_block_form(eqs_input)
equations = Expr[]
ModelingToolkit.parse_equations!(Expr(:block), equations, Dict{Symbol, Any}(), eqs_input)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This first bit creates the equation expression that we actually use


# Loops through all equations, checks for lhs of the form `D(X) ~ ...`.
# When this is the case, the variable X and differential D are extracted (for automatic declaration).
# Also performs simple error checks.
vars_extracted = []
TorkelE marked this conversation as resolved.
Show resolved Hide resolved
add_default_diff = false
for eq in equations
if (eq.head != :call) || (eq.args[1] != :~)
error("Malformed equation: \"$eq\". Equation's left hand and right hand sides should be separated by a \"~\".")
end

# Checks if the equation have the format D(X) ~ ... (where X is a symbol). This means that the
# default differential has been used. X is added as a declared variable to the system, and
# we make a note that a differential D = Differential(iv) should be made as well.
lhs = eq.args[2]
# if lhs: is an expression. Is a function call. The function's name is D. Calls a single symbol.
if (lhs isa Expr) && (lhs.head == :call) && (lhs.args[1] == :D) && (lhs.args[2] isa Symbol)
diff_var = lhs.args[2]
if in(diff_var, forbidden_symbols_error)
error("A forbidden symbol ($(diff_var)) was used as an variable in this differential equation: $eq")
end
add_default_diff = true
in(diff_var, variables_declared) || push!(vars_extracted, diff_var)
end
isaacsas marked this conversation as resolved.
Show resolved Hide resolved
end

return vars_extracted, add_default_diff, equations
end

# Creates an expression declaring differentials.
function create_differential_expr(options, add_default_diff, used_syms)
# Creates the differential expression.
# If differentials was 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.
diffexpr = (haskey(options, :differentials) ? options[:differentials].args[3] : MacroTools.striplines(:(begin end)))
diffexpr = option_block_form(diffexpr)

# Goes through all differentials, checking that they are correctly formatted and their symbol is not used elsewhere.
for dexpr in diffexpr.args
(dexpr.head != :(=)) && error("Differential declaration must have form like D = Differential(t), instead \"$(dexpr)\" was given.")
(dexpr.args[1] isa Symbol) || error("Differential left-hand side must be a single symbol, instead \"$(dexpr.args[1])\" was given.")
in(dexpr.args[1], used_syms) && error("Differential name ($(dexpr.args[1])) is also a species, variable, or parameter. This is ambigious and not allowed.")
in(dexpr.args[1], forbidden_symbols_error) && error("A forbidden symbol ($(dexpr.args[1])) was used as a differential name.")
end

# If the default differential D has been used, but not pre-declared using the @differenitals
# options, add this declaration to the list of declared differentials.
if add_default_diff && !any(diff_dec.args[1] == :D for diff_dec in diffexpr.args)
push!(diffexpr.args, :(D = Differential($(DEFAULT_IV_SYM))))
end

return diffexpr
end
isaacsas marked this conversation as resolved.
Show resolved Hide resolved

# Read the events (continious or discrete) provided as options to the DSL. Returns an expression which evalutes to these.
function read_events_option(options, event_type::Symbol)
# Prepares the events, if required to, converts them to block form.
(event_type in [:continuous_events, :discrete_events]) || error("Trying to read an unsupported event type.")
events_input = haskey(options, event_type) ? options[event_type].args[3] : MacroTools.striplines(:(begin end))
events_input = option_block_form(events_input)

# Goes throgh the events, checks for errors, and adds them to the output vector.
events_expr = :([])
for arg in events_input.args
# 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
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)
error("The condition part of continious 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)
error("The affect part of all events (the righ-hand side) must be a vector. This is not the case for: $(arg).")
end

# Adds the correctly formatted event to the event creation expression.
push!(events_expr.args, arg)
end

return events_expr
isaacsas marked this conversation as resolved.
Show resolved Hide resolved
end

# Checks if the `@default_noise_scaling` option is used. If so, read its input and adds it as a
# default metadata value to the `default_reaction_metadata` vector.
function check_default_noise_scaling!(default_reaction_metadata, options)
Expand Down
Loading
Loading