Skip to content

Commit 78ca087

Browse files
committed
multiple fixes
1 parent 9230667 commit 78ca087

File tree

4 files changed

+47
-62
lines changed

4 files changed

+47
-62
lines changed

src/dsl.jl

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,9 @@ This model also contains production and degradation reactions, where `0` denotes
8484
either no substrates or no products in a reaction.
8585
8686
Options:
87-
In addition to reactions, the macro also supports "option" inputs. Each option is designated
88-
by a tag starting with a `@` followed by its input. A list of options can be found [here](https://docs.sciml.ai/Catalyst/stable/api/#api_dsl_options).
87+
In addition to reactions, the macro also supports "option" inputs (permitting e.g. the addition
88+
of observables). Each option is designated by a tag starting with a `@` followed by its input.
89+
A list of options can be found [here](https://docs.sciml.ai/Catalyst/stable/api/#api_dsl_options).
8990
"""
9091
macro reaction_network(name::Symbol, network_expr::Expr)
9192
make_rs_expr(QuoteNode(name), network_expr)
@@ -198,8 +199,8 @@ function recursive_find_reactants!(ex::ExprValues, mult::ExprValues,
198199
# If the expression corresponds to a reactant on our list, increase its multiplicity.
199200
idx = findfirst(r.reactant == ex for r in reactants)
200201
if !isnothing(idx)
201-
new_mult = processmult(+, mult, reactants[idx].stoichiometry)
202-
reactants[idx] = DSLReactant(ex, new_mult)
202+
newmult = processmult(+, mult, reactants[idx].stoichiometry)
203+
reactants[idx] = DSLReactant(ex, newmult)
203204

204205
# If the expression corresponds to a new reactant, add it to the list.
205206
else
@@ -210,12 +211,12 @@ function recursive_find_reactants!(ex::ExprValues, mult::ExprValues,
210211
elseif ex.args[1] == :*
211212
# The normal case (e.g. 3*X or 3*(X+Y)). Update the current multiplicity and continue.
212213
if length(ex.args) == 3
213-
new_mult = processmult(*, mult, ex.args[2])
214-
recursive_find_reactants!(ex.args[3], new_mult, reactants)
214+
newmult = processmult(*, mult, ex.args[2])
215+
recursive_find_reactants!(ex.args[3], newmult, reactants)
215216
# More complicated cases (e.g. 2*3*X). Yes, `ex.args[1:(end - 1)]` should start at 1 (not 2).
216217
else
217-
new_mult = processmult(*, mult, Expr(:call, ex.args[1:(end - 1)]...))
218-
recursive_find_reactants!(ex.args[end], new_mult, reactants)
218+
newmult = processmult(*, mult, Expr(:call, ex.args[1:(end - 1)]...))
219+
recursive_find_reactants!(ex.args[end], newmult, reactants)
219220
end
220221
# If we have encountered a sum of different reactants, apply recursion on each.
221222
elseif ex.args[1] == :+
@@ -243,11 +244,10 @@ end
243244
function extract_metadata(metadata_line::Expr)
244245
metadata = :([])
245246
for arg in metadata_line.args
246-
if arg.head != :(=)
247+
(arg.head != :(=)) &&
247248
error("Malformatted metadata line: $metadata_line. Each entry in the vector should contain a `=`.")
248-
elseif !(arg.args[1] isa Symbol)
249+
(arg.args[1] isa Symbol) ||
249250
error("Malformatted metadata entry: $arg. Entries left-hand-side should be a single symbol.")
250-
end
251251
push!(metadata.args, :($(QuoteNode(arg.args[1])) => $(arg.args[2])))
252252
end
253253
return metadata
@@ -422,7 +422,7 @@ function push_reactions!(reactions::Vector{DSLReaction}, subs::ExprValues,
422422
lengs = (tup_leng(subs), tup_leng(prods), tup_leng(rate), tup_leng(metadata))
423423
maxl = maximum(lengs)
424424
if any(!(leng == 1 || leng == maxl) for leng in lengs)
425-
throw("Malformed reaction, rate=$rate, subs=$subs, prods=$prods, metadata=$metadata.")
425+
error("Malformed reaction, rate: $rate, subs: $subs, prods: $prods, metadata: $metadata.")
426426
end
427427

428428
# Loops through each reaction encoded by the reaction's different components.
@@ -452,12 +452,12 @@ end
452452
function extract_syms(opts, vartype::Symbol)
453453
# If the corresponding option have been used, uses `Symbolics._parse_vars` to find all
454454
# variable within it (returning them in a vector).
455-
if haskey(opts, vartype)
455+
return if haskey(opts, vartype)
456456
ex = opts[vartype]
457457
vars = Symbolics._parse_vars(vartype, Real, ex.args[3:end])
458-
return Vector{Union{Symbol, Expr}}(vars.args[end].args)
458+
Vector{Union{Symbol, Expr}}(vars.args[end].args)
459459
else
460-
return Union{Symbol, Expr}[]
460+
Union{Symbol, Expr}[]
461461
end
462462
end
463463

@@ -593,6 +593,14 @@ function get_rxexpr(rx::DSLReaction)
593593
return rx_constructor
594594
end
595595

596+
# Recursively escape functions within equations of an equation written using user-defined functions.
597+
# Does not expand special function calls like "hill(...)" and differential operators.
598+
function escape_equation!(eqexpr::Expr, diffsyms)
599+
eqexpr.args[2] = recursive_escape_functions!(eqexpr.args[2], diffsyms)
600+
eqexpr.args[3] = recursive_escape_functions!(eqexpr.args[3], diffsyms)
601+
eqexpr
602+
end
603+
596604
### DSL Option Handling ###
597605

598606
# Finds the time independent variable, and any potential spatial independent variables.
@@ -856,7 +864,7 @@ end
856864

857865
### `@reaction` Macro & its Internals ###
858866

859-
@doc raw"""
867+
"""
860868
@reaction
861869
862870
Macro for generating a single [`Reaction`](@ref) object using a similar syntax as the `@reaction_network`
@@ -962,15 +970,6 @@ function recursive_escape_functions!(expr::ExprValues, diffsyms = [])
962970
expr
963971
end
964972

965-
# Returns the length of a expression tuple, or 1 if it is not an expression tuple (probably
966-
# a Symbol/Numerical). This is used to handle bundled reaction (like `d, (X,Y) --> 0`).
967-
# Recursively escape functions in the right-hand-side of an equation written using user-defined functions. Special function calls like "hill(...)" are not expanded.
968-
function escape_equation!(eqexpr::Expr, diffsyms)
969-
eqexpr.args[2] = recursive_escape_functions!(eqexpr.args[2], diffsyms)
970-
eqexpr.args[3] = recursive_escape_functions!(eqexpr.args[3], diffsyms)
971-
eqexpr
972-
end
973-
974973
# Returns the length of a expression tuple, or 1 if it is not an expression tuple (probably a Symbol/Numerical).
975974
function tup_leng(ex::ExprValues)
976975
(typeof(ex) == Expr && ex.head == :tuple) && (return length(ex.args))

src/expression_utils.jl

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,9 @@ end
2626

2727
# Throws an error when a forbidden symbol is used.
2828
function forbidden_symbol_check(sym)
29-
used_forbidden_syms = intersect(forbidden_symbols_error, sym)
30-
isempty(used_forbidden_syms) && return
31-
error("The following symbol(s) are used as species or parameters: $used_forbidden_syms, this is not permitted.")
32-
end
33-
34-
# Checks that no symbol was sued for multiple purposes.
35-
function unique_symbol_check(syms)
36-
allunique(syms)||
37-
error("Reaction network independent variables, parameters, species, and variables must all have distinct names, but a duplicate has been detected. ")
29+
used_forbidden_syms =
30+
isempty(used_forbidden_syms) ||
31+
error("The following symbol(s) are used as species or parameters: $used_forbidden_syms, this is not permitted.")
3832
end
3933

4034
### Catalyst-specific Expressions Manipulation ###

test/dsl/dsl_basic_model_construction.jl

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ let
189189
u0 = rnd_u0(networks[1], rng; factor)
190190
p = rnd_ps(networks[1], rng; factor)
191191
t = rand(rng)
192-
192+
193193
@test f_eval(networks[1], u0, p, t) f_eval(networks[2], u0, p, t)
194194
@test jac_eval(networks[1], u0, p, t) jac_eval(networks[2], u0, p, t)
195195
@test g_eval(networks[1], u0, p, t) g_eval(networks[2], u0, p, t)
@@ -207,18 +207,18 @@ let
207207
(l3, l4), Y2 Y3
208208
(l5, l6), Y3 Y4
209209
c, Y4
210-
end
210+
end
211211

212212
# Checks that the networks' functions evaluates equally for various randomised inputs.
213213
@unpack X1, X2, X3, X4, p, d, k1, k2, k3, k4, k5, k6 = network
214214
for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3]
215215
u0_1 = Dict(rnd_u0(network, rng; factor))
216216
p_1 = Dict(rnd_ps(network, rng; factor))
217217
u0_2 = [:Y1 => u0_1[X1], :Y2 => u0_1[X2], :Y3 => u0_1[X3], :Y4 => u0_1[X4]]
218-
p_2 = [:q => p_1[p], :c => p_1[d], :l1 => p_1[k1], :l2 => p_1[k2], :l3 => p_1[k3],
218+
p_2 = [:q => p_1[p], :c => p_1[d], :l1 => p_1[k1], :l2 => p_1[k2], :l3 => p_1[k3],
219219
:l4 => p_1[k4], :l5 => p_1[k5], :l6 => p_1[k6]]
220220
t = rand(rng)
221-
221+
222222
@test f_eval(network, u0_1, p_1, t) f_eval(differently_written_5, u0_2, p_2, t)
223223
@test jac_eval(network, u0_1, p_1, t) jac_eval(differently_written_5, u0_2, p_2, t)
224224
@test g_eval(network, u0_1, p_1, t) g_eval(differently_written_5, u0_2, p_2, t)
@@ -271,7 +271,7 @@ let
271271
u0 = rnd_u0(networks[1], rng; factor)
272272
p = rnd_ps(networks[1], rng; factor)
273273
t = rand(rng)
274-
274+
275275
@test f_eval(networks[1], u0, p, t) f_eval(networks[2], u0, p, t)
276276
@test jac_eval(networks[1], u0, p, t) jac_eval(networks[2], u0, p, t)
277277
@test g_eval(networks[1], u0, p, t) g_eval(networks[2], u0, p, t)
@@ -293,7 +293,7 @@ let
293293
(sqrt(3.7), exp(1.9)), X4 X1 + X2
294294
end
295295
push!(identical_networks_3, reaction_networks_standard[9] => no_parameters_9)
296-
push!(parameter_sets, [:p1 => 1.5, :p2 => 1, :p3 => 2, :d1 => 0.01, :d2 => 2.3, :d3 => 1001,
296+
push!(parameter_sets, [:p1 => 1.5, :p2 => 1, :p3 => 2, :d1 => 0.01, :d2 => 2.3, :d3 => 1001,
297297
:k1 => π, :k2 => 42, :k3 => 19.9, :k4 => 999.99, :k5 => sqrt(3.7), :k6 => exp(1.9)])
298298

299299
no_parameters_10 = @reaction_network begin
@@ -305,14 +305,14 @@ let
305305
1.0, X5
306306
end
307307
push!(identical_networks_3, reaction_networks_standard[10] => no_parameters_10)
308-
push!(parameter_sets, [:p => 0.01, :k1 => 3.1, :k2 => 3.2, :k3 => 0.0, :k4 => 2.1, :k5 => 901.0,
308+
push!(parameter_sets, [:p => 0.01, :k1 => 3.1, :k2 => 3.2, :k3 => 0.0, :k4 => 2.1, :k5 => 901.0,
309309
:k6 => 63.5, :k7 => 7, :k8 => 8, :d => 1.0])
310310

311311
for (networks, p_1) in zip(identical_networks_3, parameter_sets)
312312
for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3]
313313
u0 = rnd_u0(networks[1], rng; factor)
314314
t = rand(rng)
315-
315+
316316
@test f_eval(networks[1], u0, p_1, t) f_eval(networks[2], u0, [], t)
317317
@test jac_eval(networks[1], u0, p_1, t) jac_eval(networks[2], u0, [], t)
318318
@test g_eval(networks[1], u0, p_1, t) g_eval(networks[2], u0, [], t)
@@ -383,7 +383,7 @@ let
383383
τ = rand(rng)
384384
u = rnd_u0(reaction_networks_conserved[1], rng; factor)
385385
p_2 = rnd_ps(time_network, rng; factor)
386-
p_1 = [p_2; reaction_networks_conserved[1].k1 => τ;
386+
p_1 = [p_2; reaction_networks_conserved[1].k1 => τ;
387387
reaction_networks_conserved[1].k4 => τ; reaction_networks_conserved[1].k5 => τ]
388388

389389
@test f_eval(reaction_networks_conserved[1], u, p_1, τ) f_eval(time_network, u, p_2, τ)
@@ -463,7 +463,7 @@ let
463463
@test rn1 == rn2
464464
end
465465

466-
# Tests arrow variants in `@reaction`` macro.
466+
# Tests arrow variants in `@reaction` macro.
467467
let
468468
@test isequal((@reaction k, 0 --> X), (@reaction k, X <-- 0))
469469
@test isequal((@reaction k, 0 --> X), (@reaction k, X 0))
@@ -533,4 +533,4 @@ let
533533
@test_throws Exception @eval @reaction_network begin
534534
k, X^Y --> XY
535535
end
536-
end
536+
end

test/dsl/dsl_options.jl

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -417,23 +417,15 @@ let
417417
@test issetequal(species(rn), spcs)
418418
end
419419

420-
# Tests errors in `@variables` declarations.
420+
# Tests error when disallowed name is used for variable.
421421
let
422-
# Variable used as species in reaction.
423-
@test_throws Exception @eval rn = @reaction_network begin
424-
@variables K(t)
425-
k, K + A --> B
426-
end
427-
428-
# Tests error when disallowed name is used for variable.
429422
@test_throws Exception @eval @reaction_network begin
430423
@variables π(t)
431424
end
432425
end
433426

434427
# Tests that explicitly declaring a single symbol as several things does not work.
435428
# Several of these are broken, but note sure how to test broken-ness on `@test_throws false Exception @eval`.
436-
# Relevant issue: https://github.com/SciML/Catalyst.jl/issues/1173
437429
let
438430
# Species + parameter.
439431
@test_throws Exception @eval @reaction_network begin
@@ -1150,14 +1142,6 @@ let
11501142
end
11511143
end
11521144

1153-
# Erroneous `@default_noise_scaling` declaration (other noise scaling tests are mostly in the SDE file).
1154-
let
1155-
# Default noise scaling with multiple entries.
1156-
@test_throws Exception @eval @reaction_network begin
1157-
@default_noise_scaling η1 η2
1158-
end
1159-
end
1160-
11611145
### Other DSL Option Tests ###
11621146

11631147
# test combinatoric_ratelaws DSL option
@@ -1264,6 +1248,14 @@ let
12641248
@test isequal(Catalyst.expand_registered_functions(equations(rn4)[1]), D(A) ~ v*(A^n))
12651249
end
12661250

1251+
# Erroneous `@default_noise_scaling` declaration (other noise scaling tests are mostly in the SDE file).
1252+
let
1253+
# Default noise scaling with multiple entries.
1254+
@test_throws Exception @eval @reaction_network begin
1255+
@default_noise_scaling η1 η2
1256+
end
1257+
end
1258+
12671259
### test that @no_infer properly throws errors when undeclared variables are written ###
12681260

12691261
import Catalyst: UndeclaredSymbolicError

0 commit comments

Comments
 (0)