Skip to content

Commit a1e967a

Browse files
committed
save progress
1 parent 4e720ee commit a1e967a

File tree

4 files changed

+66
-77
lines changed

4 files changed

+66
-77
lines changed

src/dsl.jl

Lines changed: 41 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ struct DSLReaction
221221
subs = recursive_find_reactants!(sub_line, 1, Vector{DSLReactant}(undef, 0))
222222
prods = recursive_find_reactants!(prod_line, 1, Vector{DSLReactant}(undef, 0))
223223
metadata = extract_metadata(metadata_line)
224-
new(sub, prod, rate, metadata, rx_line)
224+
new(subs, prods, rate, metadata, rx_line)
225225
end
226226
end
227227

@@ -232,13 +232,13 @@ end
232232
function recursive_find_reactants!(ex::ExprValues, mult::ExprValues,
233233
reactants::Vector{DSLReactant})
234234
# We have reached the end of the expression tree and can finalise and return the reactants.
235-
if typeof(ex) != Expr || (ex.head == :escape) || (ex.head == :ref)
235+
if (typeof(ex) != Expr) || (ex.head == :escape) || (ex.head == :ref)
236236
# The final bit of the expression is not a relevant reactant, no additions are required.
237237
(ex == 0 || in(ex, empty_set)) && (return reactants)
238238

239239
# If the expression corresponds to a reactant on our list, increase its multiplicity.
240-
if any(ex == reactant.reactant for reactant in reactants)
241-
idx = findfirst(r.reactant == ex for r in reactants)
240+
idx = findfirst(r.reactant == ex for r in reactants)
241+
if !isnothing(idx)
242242
new_mult = processmult(+, mult, reactants[idx].stoichiometry)
243243
reactants[idx] = DSLReactant(ex, new_mult)
244244

@@ -324,12 +324,13 @@ function make_reaction_system(ex::Expr, name)
324324

325325
# Reads options (round 1, options which must be read before the reactions, e.g. because
326326
# they might declare parameters/species/variables).
327+
requiredec = haskey(options, :require_declaration)
327328
compound_expr_init, compound_species = read_compound_options(options)
328329
species_declared = [extract_syms(options, :species); compound_species]
329330
parameters_declared = extract_syms(options, :parameters)
330331
variables_declared = extract_syms(options, :variables)
331332
vars_extracted, add_default_diff, equations = read_equations_options(options,
332-
variables_declared)
333+
variables_declared; requiredec)
333334

334335
# Extracts all reactions. Extracts all parameters, species, and variables of the system and
335336
# creates lists with them.
@@ -338,31 +339,29 @@ function make_reaction_system(ex::Expr, name)
338339
syms_declared = Set(Iterators.flatten((parameters_declared, species_declared,
339340
variables)))
340341
species_extracted, parameters_extracted = extract_species_and_parameters(reactions,
341-
syms_declared)
342+
syms_declared; requiredec)
342343
species = vcat(species_declared, species_extracted)
343344
parameters = vcat(parameters_declared, parameters_extracted)
344345

345346
# Reads options (round 2, options that either can, or must, be read after the reactions).
346-
tiv, sivs, ivs, ivexpr = read_ivs_option(options)
347+
tiv, sivs, ivs, ivsexpr = read_ivs_option(options)
347348
continuous_events_expr = read_events_option(options, :continuous_events)
348349
discrete_events_expr = read_events_option(options, :discrete_events)
349-
observed_expr, observed_eqs, obs_syms = read_observed_options(options,
350-
[species_declared; variables], ivs)
350+
obsexpr, observed_eqs, obs_syms = read_observed_options(options,
351+
[species_declared; variables], ivs; requiredec)
351352
diffexpr = create_differential_expr(options, add_default_diff,
352353
[species; parameters; variables], tiv)
353354
default_reaction_metadata = read_default_noise_scaling_option(options)
354355
combinatoric_ratelaws = read_combinatoric_ratelaws_option(options)
355356

356357
# Checks for input errors.
357-
if (sum(length, [reaction_lines, option_lines]) != length(ex.args))
358-
error("@reaction_network input contain $(length(ex.args) - sum(length.([reaction_lines,option_lines]))) malformed lines.")
359-
end
360-
if any(!in(opt_in, option_keys) for opt_in in keys(options))
361-
error("The following unsupported options were used: $(filter(opt_in->!in(opt_in,option_keys), keys(options)))")
362-
end
363358
forbidden_symbol_check(union(species, parameters))
364359
forbidden_variable_check(variables)
365360
unique_symbol_check(vcat(species, parameters, variables, ivs))
361+
(sum(length, [reaction_lines, option_lines]) != length(ex.args)) &&
362+
error("@reaction_network input contain $(length(ex.args) - sum(length.([reaction_lines,option_lines]))) malformed lines.")
363+
any(!in(option_keys), keys(options)) &&
364+
error("The following unsupported options were used: $(filter(opt_in->!in(opt_in,option_keys), keys(options)))")
366365

367366
# Creates expressions corresponding to actual code from the internal DSL representation.
368367
psexpr_init = get_pexpr(parameters_extracted, options)
@@ -371,35 +370,37 @@ function make_reaction_system(ex::Expr, name)
371370
psexpr, psvar = scalarize_macro(psexpr_init, "ps")
372371
spsexpr, spsvar = scalarize_macro(spsexpr_init, "specs")
373372
vsexpr, vsvar = scalarize_macro(vsexpr_init, "vars")
374-
compound_expr, compsvar = scalarize_macro(compound_expr_init, "comps")
373+
cmpsexpr, cmpsvar = scalarize_macro(compound_expr_init, "comps")
375374
rxsexprs = make_rxsexprs(reactions, equations)
376375

377376
# Assemblies the full expression that declares all required symbolic variables, and
378377
# then the output `ReactionSystem`.
379-
quote
378+
MacroTools.flatten(striplines(quote
379+
# Inserts the expressions which generates the `ReactionSystem` input.
380+
$ivsexpr
380381
$psexpr
381-
$ivexpr
382382
$spsexpr
383383
$vsexpr
384-
$observed_expr
385-
$compound_expr
384+
$obsexpr
385+
$cmpsexpr
386386
$diffexpr
387387

388-
sivs_vec = $sivs
389-
rx_eq_vec = $rxexprs
390-
vars = setdiff(union($spssym, $varssym, $compssym), $obs_syms)
391-
obseqs = $observed_eqs
392-
cevents = $continuous_events_expr
393-
devents = $discrete_events_expr
388+
# Stores each kwarg in a variable. Not necessary but useful when inspecting code for debugging.
389+
name = $name
390+
spatial_ivs = $sivs
391+
rx_eq_vec = $rxsexprs
392+
vars = setdiff(union($spsvar, $vsvar, $cmpsvar), $obs_syms)
393+
observed = $observed_eqs
394+
continuous_events = $continuous_events_expr
395+
discrete_events = $discrete_events_expr
396+
combinatoric_ratelaws = $combinatoric_ratelaws
397+
default_reaction_metadata = $default_reaction_metadata
394398

395399
remake_ReactionSystem_internal(
396-
make_ReactionSystem_internal(
397-
rx_eq_vec, $tiv, vars, $pssym;
398-
name = $name, spatial_ivs = sivs_vec, observed = obseqs,
399-
continuous_events = cevents, discrete_events = devents,
400-
combinatoric_ratelaws = $combinatoric_ratelaws);
401-
default_reaction_metadata = $default_reaction_metadata)
402-
end
400+
make_ReactionSystem_internal(rx_eq_vec, $tiv, vars, $psvar; name, spatial_ivs,
401+
observed, continuous_events, discrete_events, combinatoric_ratelaws);
402+
default_reaction_metadata)
403+
end))
403404
end
404405

405406
### DSL Reaction Reading Functions ###
@@ -451,7 +452,7 @@ function read_reaction_line(line::Expr)
451452

452453
# Handles metadata. If not provided, empty metadata is created.
453454
if length(line.args) == 2
454-
in(arrow, double_arrows) ? :(([], [])) : :([])
455+
metadata = in(arrow, double_arrows) ? :(([], [])) : :([])
455456
elseif length(line.args) == 3
456457
metadata = line.args[3]
457458
else
@@ -511,7 +512,7 @@ end
511512

512513
# Function looping through all reactions, to find undeclared symbols (species or
513514
# parameters) and assign them to the right category.
514-
function extract_species_and_parameters(reactions, excluded_syms)
515+
function extract_species_and_parameters(reactions, excluded_syms; requiredec = false)
515516
# Loops through all reactant, extract undeclared ones as species.
516517
species = OrderedSet{Union{Symbol, Expr}}()
517518
for reaction in reactions
@@ -649,17 +650,17 @@ function read_ivs_option(options)
649650
# Creates the independent variables expressions (depends on whether the `ivs` option was used).
650651
if haskey(options, :ivs)
651652
ivs = Tuple(extract_syms(options, :ivs))
652-
ivexpr = copy(options[:ivs])
653-
ivexpr.args[1] = Symbol("@", "parameters")
653+
ivsexpr = copy(options[:ivs])
654+
ivsexpr.args[1] = Symbol("@", "parameters")
654655
else
655656
ivs = (DEFAULT_IV_SYM,)
656-
ivexpr = :($(DEFAULT_IV_SYM) = default_t())
657+
ivsexpr = :($(DEFAULT_IV_SYM) = default_t())
657658
end
658659

659660
# Extracts the independet variables symbols (time and spatial), and returns the output.
660661
tiv = ivs[1]
661662
sivs = (length(ivs) > 1) ? Expr(:vect, ivs[2:end]...) : nothing
662-
return tiv, sivs, ivs, ivexpr
663+
return tiv, sivs, ivs, ivsexpr
663664
end
664665

665666
# Returns the `default_reaction_metadata` output. Technically Catalyst's code could have been made
@@ -977,9 +978,8 @@ end
977978
# Reads a single line and creates the corresponding DSLReaction.
978979
function get_reaction(line)
979980
reaction = get_reactions([line])
980-
if (length(reaction) != 1)
981+
(length(reaction) != 1) &&
981982
error("Malformed reaction. @reaction macro only creates a single reaction. E.g. double arrows, such as `<-->` are not supported.")
982-
end
983983
return only(reaction)
984984
end
985985

src/expression_utils.jl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ function esc_dollars!(ex)
55
# If we do not have an expression: recursion has finished and we return the input.
66
(ex isa Expr) || (return ex)
77

8-
# If we have encountered an interpolation, perform the appropriate modification, else recur.
8+
# If we have encountered an interpolation, perform the appropriate modification, else recur.
99
if ex.head == :$
1010
return esc(:($(ex.args[1])))
1111
else
@@ -26,27 +26,27 @@ end
2626

2727
# Throws an error when a forbidden symbol is used.
2828
function forbidden_symbol_check(sym)
29-
isempty(intersect(forbidden_symbols_error, sym)) && return
3029
used_forbidden_syms = intersect(forbidden_symbols_error, sym)
30+
isempty(used_forbidden_syms) && return
3131
error("The following symbol(s) are used as species or parameters: $used_forbidden_syms, this is not permitted.")
3232
end
3333

3434
# Throws an error when a forbidden variable is used (a forbidden symbol that is not `:t`).
3535
function forbidden_variable_check(sym)
36-
isempty(intersect(forbidden_variables_error, sym)) && return
3736
used_forbidden_syms = intersect(forbidden_variables_error, sym)
37+
isempty(used_forbidden_syms) && return
3838
error("The following symbol(s) are used as variables: $used_forbidden_syms, this is not permitted.")
3939
end
4040

4141
# Checks that no symbol was sued for multiple purposes.
4242
function unique_symbol_check(syms)
43-
allunique(syms) && return
44-
error("Reaction network independent variables, parameters, species, and variables must all have distinct names, but a duplicate has been detected. ")
43+
allunique(syms)||
44+
error("Reaction network independent variables, parameters, species, and variables must all have distinct names, but a duplicate has been detected. ")
4545
end
4646

4747
### Catalyst-specific Expressions Manipulation ###
4848

49-
# Some options takes input on form that is either `@option ...` or `@option begin ... end`.
49+
# Some options takes input on form that is either `@option ...` or `@option begin ... end`.
5050
# This transforms input of the latter form to the former (with only one line in the `begin ... end` block)
5151
function option_block_form(expr)
5252
(expr.head == :block) && return expr
@@ -72,12 +72,12 @@ function find_varinfo_in_declaration(expr)
7272

7373
# Case: X
7474
(expr isa Symbol) && (return expr, [], nothing, nothing)
75-
# Case: X(t)
75+
# Case: X(t)
7676
(expr.head == :call) && (return expr.args[1], expr.args[2:end], nothing, nothing)
7777
if expr.head == :(=)
7878
# Case: X = 1.0
7979
(expr.args[1] isa Symbol) && (return expr.args[1], [], expr.args[2], nothing)
80-
# Case: X(t) = 1.0
80+
# Case: X(t) = 1.0
8181
(expr.args[1].head == :call) &&
8282
(return expr.args[1].args[1], expr.args[1].args[2:end], expr.args[2].args[1],
8383
nothing)
@@ -114,7 +114,7 @@ end
114114
# (In this example the independent variable :t was inserted).
115115
# Here, the iv is a iv_expr, which can be anything, which is inserted
116116
function insert_independent_variable(expr_in, iv_expr)
117-
# If expr is a symbol, just attach the iv. If not we have to create a new expr and mutate it.
117+
# If expr is a symbol, just attach the iv. If not we have to create a new expr and mutate it.
118118
# Because Symbols (a possible input) cannot be mutated, this function cannot mutate the input
119119
# (would have been easier if Expr input was guaranteed).
120120
(expr_in isa Symbol) && (return Expr(:call, expr_in, iv_expr))

test/dsl/dsl_advanced_model_construction.jl

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,6 @@ let
123123
@test rn == rn2
124124
end
125125

126-
# Creates a reaction network using `eval` and internal function.
127-
let
128-
ex = quote
129-
(Ka, Depot --> Central)
130-
(CL / Vc, Central --> 0)
131-
end
132-
# Line number nodes aren't ignored so have to be manually removed
133-
Base.remove_linenums!(ex)
134-
@test eval(Catalyst.make_reaction_system(ex, QuoteNode(:name))) isa ReactionSystem
135-
end
136-
137126
# Miscellaneous interpolation tests. Unsure what they do here (not related to DSL).
138127
let
139128
rx = @reaction k*h, A + 2*B --> 3*C + D
@@ -211,11 +200,11 @@ let
211200
end
212201

213202
# Checks that repeated metadata throws errors.
214-
let
215-
@test_throws Exception @eval @reaction k, 0 --> X, [md1=1.0, md1=2.0]
203+
let
204+
@test_throws Exception @eval @reaction k, 0 --> X, [md1=1.0, md1=2.0]
216205
@test_throws Exception @eval @reaction_network begin
217-
k, 0 --> X, [md1=1.0, md1=1.0]
218-
end
206+
k, 0 --> X, [md1=1.0, md1=1.0]
207+
end
219208
end
220209

221210
# Tests for nested metadata.
@@ -368,11 +357,11 @@ let
368357
@species (X(t))[1:2]
369358
(k[1],k[2]), X[1] <--> X[2]
370359
end
371-
360+
372361
@parameters k[1:2]
373362
@species (X(t))[1:2]
374363
rx1 = Reaction(k[1], [X[1]], [X[2]])
375364
rx2 = Reaction(k[2], [X[2]], [X[1]])
376365
@named twostate = ReactionSystem([rx1, rx2], t)
377366
@test twostate == rn
378-
end
367+
end

test/dsl/dsl_options.jl

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ let
418418
end
419419

420420
# Tests errors in `@variables` declarations.
421-
let
421+
let
422422
# Variable used as species in reaction.
423423
@test_throws Exception @eval rn = @reaction_network begin
424424
@variables K(t)
@@ -813,7 +813,7 @@ let
813813
# Observable metadata provided twice.
814814
@test_throws Exception @eval @reaction_network begin
815815
@species X2 [description="Twice the amount of X"]
816-
@observables (X2, [description="X times two."]) ~ 2X
816+
@observables (X2, [description="X times two."]) ~ 2X
817817
d, X --> 0
818818
end
819819

@@ -998,6 +998,14 @@ let
998998
end
999999
end
10001000

1001+
# Erroneous `@default_noise_scaling` declaration (other noise scaling tests are mostly in the SDE file).
1002+
let
1003+
# Default noise scaling with multiple entries.
1004+
@test_throws Exception @eval @reaction_network begin
1005+
@default_noise_scaling η1 η2
1006+
end
1007+
end
1008+
10011009
### Other DSL Option Tests ###
10021010

10031011
# test combinatoric_ratelaws DSL option
@@ -1174,11 +1182,3 @@ let
11741182
@observables X2 ~ X1
11751183
end
11761184
end
1177-
1178-
# Erroneous `@default_noise_scaling` declaration (other noise scaling tests are mostly in the SDE file).
1179-
let
1180-
# Default noise scaling with multiple entries.
1181-
@test_throws Exception @eval @reaction_network begin
1182-
@default_noise_scaling η1 η2
1183-
end
1184-
end

0 commit comments

Comments
 (0)