@@ -71,7 +71,7 @@ const pure_rate_arrows = Set{Symbol}([:(=>), :(<=), :⇐, :⟽, :⇒, :⟾, :⇔
71
71
# Declares the keys used for various options.
72
72
const option_keys = (:species , :parameters , :variables , :ivs , :compounds , :observables ,
73
73
:default_noise_scaling , :differentials , :equations ,
74
- :continuous_events , :discrete_events , :combinatoric_ratelaws )
74
+ :continuous_events , :discrete_events , :combinatoric_ratelaws , :require_declaration )
75
75
76
76
# ## `@species` Macro ###
77
77
@@ -220,13 +220,14 @@ struct ReactionStruct
220
220
products:: Vector{ReactantStruct}
221
221
rate:: ExprValues
222
222
metadata:: Expr
223
+ rxexpr:: Expr
223
224
224
225
function ReactionStruct (sub_line:: ExprValues , prod_line:: ExprValues , rate:: ExprValues ,
225
- metadata_line:: ExprValues )
226
+ metadata_line:: ExprValues , rx_line :: Expr )
226
227
sub = recursive_find_reactants! (sub_line, 1 , Vector {ReactantStruct} (undef, 0 ))
227
228
prod = recursive_find_reactants! (prod_line, 1 , Vector {ReactantStruct} (undef, 0 ))
228
229
metadata = extract_metadata (metadata_line)
229
- new (sub, prod, rate, metadata)
230
+ new (sub, prod, rate, metadata, rx_line )
230
231
end
231
232
end
232
233
@@ -283,6 +284,17 @@ function extract_metadata(metadata_line::Expr)
283
284
return metadata
284
285
end
285
286
287
+
288
+
289
+ struct UndeclaredSymbolicError <: Exception
290
+ msg:: String
291
+ end
292
+
293
+ function Base. showerror (io:: IO , err:: UndeclaredSymbolicError )
294
+ print (io, " UndeclaredSymbolicError: " )
295
+ print (io, err. msg)
296
+ end
297
+
286
298
# ## DSL Internal Master Function ###
287
299
288
300
# Function for creating a ReactionSystem structure (used by the @reaction_network macro).
@@ -308,6 +320,7 @@ function make_reaction_system(ex::Expr; name = :(gensym(:ReactionSystem)))
308
320
compound_expr, compound_species = read_compound_options (options)
309
321
continuous_events_expr = read_events_option (options, :continuous_events )
310
322
discrete_events_expr = read_events_option (options, :discrete_events )
323
+ requiredec = haskey (options, :require_declaration )
311
324
312
325
# Parses reactions, species, and parameters.
313
326
reactions = get_reactions (reaction_lines)
@@ -317,7 +330,7 @@ function make_reaction_system(ex::Expr; name = :(gensym(:ReactionSystem)))
317
330
318
331
# Reads equations.
319
332
vars_extracted, add_default_diff, equations = read_equations_options (
320
- options, variables_declared)
333
+ options, variables_declared; requiredec )
321
334
variables = vcat (variables_declared, vars_extracted)
322
335
323
336
# Handle independent variables
@@ -341,13 +354,13 @@ function make_reaction_system(ex::Expr; name = :(gensym(:ReactionSystem)))
341
354
342
355
# Reads observables.
343
356
observed_vars, observed_eqs, obs_syms = read_observed_options (
344
- options, [species_declared; variables], all_ivs)
357
+ options, [species_declared; variables], all_ivs; requiredec )
345
358
346
359
# Collect species and parameters, including ones inferred from the reactions.
347
360
declared_syms = Set (Iterators. flatten ((parameters_declared, species_declared,
348
361
variables)))
349
362
species_extracted, parameters_extracted = extract_species_and_parameters! (
350
- reactions, declared_syms)
363
+ reactions, declared_syms; requiredec )
351
364
352
365
species = vcat (species_declared, species_extracted)
353
366
parameters = vcat (parameters_declared, parameters_extracted)
@@ -425,15 +438,15 @@ function get_reactions(exprs::Vector{Expr}, reactions = Vector{ReactionStruct}(u
425
438
error (" Error: Must provide a tuple of reaction rates when declaring a bi-directional reaction." )
426
439
end
427
440
push_reactions! (reactions, reaction. args[2 ], reaction. args[3 ],
428
- rate. args[1 ], metadata. args[1 ], arrow)
441
+ rate. args[1 ], metadata. args[1 ], arrow, line )
429
442
push_reactions! (reactions, reaction. args[3 ], reaction. args[2 ],
430
- rate. args[2 ], metadata. args[2 ], arrow)
443
+ rate. args[2 ], metadata. args[2 ], arrow, line )
431
444
elseif in (arrow, fwd_arrows)
432
445
push_reactions! (reactions, reaction. args[2 ], reaction. args[3 ],
433
- rate, metadata, arrow)
446
+ rate, metadata, arrow, line )
434
447
elseif in (arrow, bwd_arrows)
435
448
push_reactions! (reactions, reaction. args[3 ], reaction. args[2 ],
436
- rate, metadata, arrow)
449
+ rate, metadata, arrow, line )
437
450
else
438
451
throw (" Malformed reaction, invalid arrow type used in: $(MacroTools. striplines (line)) " )
439
452
end
467
480
# Takes a reaction line and creates reaction(s) from it and pushes those to the reaction array.
468
481
# Used to create multiple reactions from, for instance, `k, (X,Y) --> 0`.
469
482
function push_reactions! (reactions:: Vector{ReactionStruct} , sub_line:: ExprValues ,
470
- prod_line:: ExprValues , rate:: ExprValues , metadata:: ExprValues , arrow:: Symbol )
483
+ prod_line:: ExprValues , rate:: ExprValues , metadata:: ExprValues , arrow:: Symbol , line :: Expr )
471
484
# The rates, substrates, products, and metadata may be in a tupple form (e.g. `k, (X,Y) --> 0`).
472
485
# This finds the length of these tuples (or 1 if not in tuple forms). Errors if lengs inconsistent.
473
486
lengs = (tup_leng (sub_line), tup_leng (prod_line), tup_leng (rate), tup_leng (metadata))
@@ -490,7 +503,7 @@ function push_reactions!(reactions::Vector{ReactionStruct}, sub_line::ExprValues
490
503
491
504
push! (reactions,
492
505
ReactionStruct (get_tup_arg (sub_line, i),
493
- get_tup_arg (prod_line, i), get_tup_arg (rate, i), metadata_i))
506
+ get_tup_arg (prod_line, i), get_tup_arg (rate, i), metadata_i, line ))
494
507
end
495
508
end
496
509
@@ -511,20 +524,26 @@ end
511
524
512
525
# Function looping through all reactions, to find undeclared symbols (species or
513
526
# parameters), and assign them to the right category.
514
- function extract_species_and_parameters! (reactions, excluded_syms)
527
+ function extract_species_and_parameters! (reactions, excluded_syms; requiredec = false )
515
528
species = OrderedSet {Union{Symbol, Expr}} ()
516
529
for reaction in reactions
517
530
for reactant in Iterators. flatten ((reaction. substrates, reaction. products))
518
531
add_syms_from_expr! (species, reactant. reactant, excluded_syms)
532
+ (! isempty (species) && requiredec) && throw (UndeclaredSymbolicError (
533
+ " Unrecognized variables $(join (species, " , " )) detected in reaction expression: \" $(string (reaction. rxexpr)) \" . Since the flag @require_declaration is declared, all species must be explicitly declared with the @species macro." ))
519
534
end
520
535
end
521
536
522
537
foreach (s -> push! (excluded_syms, s), species)
523
538
parameters = OrderedSet {Union{Symbol, Expr}} ()
524
539
for reaction in reactions
525
540
add_syms_from_expr! (parameters, reaction. rate, excluded_syms)
541
+ (! isempty (parameters) && requiredec) && throw (UndeclaredSymbolicError (
542
+ " Unrecognized parameter $(join (parameters, " , " )) detected in rate expression: $(reaction. rate) for the following reaction expression: \" $(string (reaction. rxexpr)) \" . Since the flag @require_declaration is declared, all parameters must be explicitly declared with the @parameters macro." ))
526
543
for reactant in Iterators. flatten ((reaction. substrates, reaction. products))
527
544
add_syms_from_expr! (parameters, reactant. stoichiometry, excluded_syms)
545
+ (! isempty (parameters) && requiredec) && throw (UndeclaredSymbolicError (
546
+ " Unrecognized parameters $(join (parameters, " , " )) detected in the stoichiometry for reactant $(reactant. reactant) in the following reaction expression: \" $(string (reaction. rxexpr)) \" . Since the flag @require_declaration is declared, all parameters must be explicitly declared with the @parameters macro." ))
528
547
end
529
548
end
530
549
682
701
# `vars_extracted`: A vector with extracted variables (lhs in pure differential equations only).
683
702
# `dtexpr`: If a differential equation is defined, the default derivative (D ~ Differential(t)) must be defined.
684
703
# `equations`: a vector with the equations provided.
685
- function read_equations_options (options, variables_declared)
704
+ function read_equations_options (options, variables_declared; requiredec = false )
686
705
# Prepares the equations. First, extracts equations from provided option (converting to block form if required).
687
706
# Next, uses MTK's `parse_equations!` function to split input into a vector with the equations.
688
707
eqs_input = haskey (options, :equations ) ? options[:equations ]. args[3 ] : :(begin end )
@@ -711,9 +730,13 @@ function read_equations_options(options, variables_declared)
711
730
diff_var = lhs. args[2 ]
712
731
if in (diff_var, forbidden_symbols_error)
713
732
error (" A forbidden symbol ($(diff_var) ) was used as an variable in this differential equation: $eq " )
733
+ elseif (! in (diff_var, variables_declared)) && requiredec
734
+ throw (UndeclaredSymbolicError (
735
+ " Unrecognized symbol $(diff_var) was used as a variable in an equation: \" $eq \" . Since the @require_declaration flag is set, all variables in equations must be explicitly declared via @variables, @species, or @parameters." ))
736
+ else
737
+ add_default_diff = true
738
+ in (diff_var, variables_declared) || push! (vars_extracted, diff_var)
714
739
end
715
- add_default_diff = true
716
- in (diff_var, variables_declared) || push! (vars_extracted, diff_var)
717
740
end
718
741
end
719
742
@@ -752,7 +775,7 @@ function create_differential_expr(options, add_default_diff, used_syms, tiv)
752
775
end
753
776
754
777
# Reads the observables options. Outputs an expression ofr creating the observable variables, and a vector of observable equations.
755
- function read_observed_options (options, species_n_vars_declared, ivs_sorted)
778
+ function read_observed_options (options, species_n_vars_declared, ivs_sorted; requiredec = false )
756
779
if haskey (options, :observables )
757
780
# Gets list of observable equations and prepares variable declaration expression.
758
781
# (`options[:observables]` includes `@observables`, `.args[3]` removes this part)
@@ -763,6 +786,10 @@ function read_observed_options(options, species_n_vars_declared, ivs_sorted)
763
786
for (idx, obs_eq) in enumerate (observed_eqs. args)
764
787
# Extract the observable, checks errors, and continues the loop if the observable has been declared.
765
788
obs_name, ivs, defaults, metadata = find_varinfo_in_declaration (obs_eq. args[2 ])
789
+ if (requiredec && ! in (obs_name, species_n_vars_declared))
790
+ throw (UndeclaredSymbolicError (
791
+ " An undeclared variable ($obs_name ) was declared as an observable in the following observable equation: \" $obs_eq \" . Since the flag @require_declaration is set, all variables must be declared with the @species, @parameters, or @variables macros." ))
792
+ end
766
793
isempty (ivs) ||
767
794
error (" An observable ($obs_name ) was given independent variable(s). These should not be given, as they are inferred automatically." )
768
795
isnothing (defaults) ||
0 commit comments