Skip to content

Commit fd97442

Browse files
committed
up
1 parent b7f3ebc commit fd97442

File tree

2 files changed

+45
-30
lines changed

2 files changed

+45
-30
lines changed

src/reaction_network.jl

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ function make_reaction_system(ex::Expr; name = :(gensym(:ReactionSystem)))
355355
option_lines = Expr[x for x in ex.args if x.head == :macrocall]
356356

357357
# Get macro options.
358+
length(unique(arg.args[1] for arg in option_lines)) < length(option_lines) && error("Some options where given multiple times.")
358359
options = Dict(map(arg -> Symbol(String(arg.args[1])[2:end]) => arg,
359360
option_lines))
360361

@@ -367,9 +368,6 @@ function make_reaction_system(ex::Expr; name = :(gensym(:ReactionSystem)))
367368
parameters_declared = extract_syms(options, :parameters)
368369
variables = extract_syms(options, :variables)
369370

370-
# Reads more options.
371-
observed_vars, observed_eqs = read_observed_options(options, [species_declared; variables])
372-
373371
# handle independent variables
374372
if haskey(options, :ivs)
375373
ivs = Tuple(extract_syms(options, :ivs))
@@ -381,6 +379,10 @@ function make_reaction_system(ex::Expr; name = :(gensym(:ReactionSystem)))
381379
end
382380
tiv = ivs[1]
383381
sivs = (length(ivs) > 1) ? Expr(:vect, ivs[2:end]...) : nothing
382+
all_ivs = (isnothing(sivs) ? [tiv] : [tiv; sivs.args])
383+
384+
# Reads more options.
385+
observed_vars, observed_eqs = read_observed_options(options, [species_declared; variables], all_ivs)
384386

385387
declared_syms = Set(Iterators.flatten((parameters_declared, species_declared,
386388
variables)))
@@ -411,7 +413,6 @@ function make_reaction_system(ex::Expr; name = :(gensym(:ReactionSystem)))
411413
push!(rxexprs.args, get_rxexprs(reaction))
412414
end
413415

414-
# Returns the rephrased expression.
415416
quote
416417
$ps
417418
$ivexpr
@@ -434,7 +435,7 @@ function make_reaction(ex::Expr)
434435

435436
# Parses reactions, species, and parameters.
436437
reaction = get_reaction(ex)
437-
species, parameters = extract_species_and_parameters!([reaction], [])
438+
species, parameters = extract_species_and_parameters!([reaivs_get_exprction], [])
438439

439440
# Checks for input errors.
440441
forbidden_symbol_check(union(species, parameters))
@@ -690,20 +691,23 @@ end
690691
# Most options handled in previous sections, when code re-organised, these should ideally be moved to the same place.
691692

692693
# Reads the observables options. Outputs an expression ofr creating the obervable variables, and a vector of observable equations.
693-
function read_observed_options(options, species_declared)
694+
function read_observed_options(options, species_declared, ivs_sorted)
694695
if haskey(options, :observables)
695696
# Gets list of observable equations and prepares variable declaration expression.
696697
# (`options[:observables]` inlucdes `@observables`, `.args[3]` removes this part)
697698
observed_eqs = make_observed_eqs(options[:observables].args[3])
698699
observed_vars = Expr(:block, :(@variables))
699700

700-
for obs_eq in observed_eqs.args
701+
for (idx, obs_eq) in enumerate(observed_eqs.args)
701702
# Extract the observable, checks errors, and continues the loop if the observable has been declared.
702-
obs_name, ivs, defaults, _ = find_varinfo_in_declaration(obs_eq.args[2])
703+
obs_name, ivs, defaults, metadata = find_varinfo_in_declaration(obs_eq.args[2])
703704
isempty(ivs) || error("An observable ($obs_name) was given independent variable(s). These should not be given, as they are inferred automatically.")
704705
isnothing(defaults) || error("An observable ($obs_name) was given a default value. This is forbidden.")
705706
in(obs_name, forbidden_symbols_error) && error("A forbidden symbol ($(obs_eq.args[2])) was used as an observable name.")
706-
(obs_name in species_declared) && continue
707+
if (obs_name in species_declared)
708+
isnothing(metadata) || error("Metadata was provided to observable $obs_name in the `@observables` macro. However, the obervable was also declared separately (using either @species or @variables). When this is done, metadata cannot be provided within the @observables declaration.")
709+
continue
710+
end
707711

708712
# Appends (..) to the observable (which is later replaced with the extracted ivs).
709713
# Adds the observable to the first line of the output expression (starting with `@variables`).
@@ -713,11 +717,13 @@ function read_observed_options(options, species_declared)
713717
# Adds a line to the `observed_vars` expression, setting the ivs for this observable.
714718
# Cannot extract directly using e.g. "getfield.(dependants_structs, :reactant)" because
715719
# then we get something like :([:X1, :X2]), rather than :([X1, X2]).
716-
dependants_structs = Catalyst.recursive_find_reactants!(obs_eq.args[3], 1, Vector{ReactantStruct}(undef, 0))
717-
dependants = :([])
718-
foreach(dep -> push!(dependants.args, dep.reactant), dependants_structs)
719-
ivs_get_expr = :(unique(reduce(vcat,[arguments(ModelingToolkit.unwrap(dep)) for dep in $dependants])))
720-
push!(observed_vars.args, :($obs_name = $(obs_name)($(ivs_get_expr)...)))
720+
dep_var_expr = :(filter(!MT.isparameter, Symbolics.get_variables($(obs_eq.args[3]))))
721+
ivs_get_expr = :(unique(reduce(vcat,[arguments(MT.unwrap(dep)) for dep in $dep_var_expr])))
722+
ivs_get_expr_sorted = :(sort($(ivs_get_expr); by = iv -> findfirst(MT.getname(iv) == ivs for ivs in $ivs_sorted)))
723+
push!(observed_vars.args, :($obs_name = $(obs_name)($(ivs_get_expr_sorted)...)))
724+
725+
# In case metadata was given, this must be cleared from `observed_eqs`
726+
observed_eqs.args[idx].args[2] = obs_name
721727
end
722728

723729
# If nothing was added to `observed_vars`, it has to be modified not to throw an error.

test/dsl/dsl_options.jl

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ let
411411
r6 = Reaction(d, [y], nothing, [1], nothing)
412412
r7 = Reaction(d, [x2y], nothing, [1], nothing)
413413
obs_eqs = [X ~ x + 2x2y, Y ~ y + x2y]
414-
rn_prog = ReactionSystem([r1, r2, r3, r4, r5, r6, r7], t, [x, y, x2y], [k, kB, kD, d]; observed = obs_eqs)
414+
@named rn_prog = ReactionSystem([r1, r2, r3, r4, r5, r6, r7], t, [x, y, x2y], [k, kB, kD, d]; observed = obs_eqs)
415415

416416
# Make simulations.
417417
u0 = [x => 1.0, y => 0.5, x2y => 0.0]
@@ -453,7 +453,7 @@ let
453453
@test sol[:X][1] == u0[:X1]^2 + ps[:op_1]*(u0[:X2] + 2*u0[:X3]) + u0[:X1]*u0[:X4]/ps[:op_2] + ps[:p]
454454
end
455455

456-
# Checks that ivs are correctly found
456+
# Checks that ivs are correctly found.
457457
let
458458
rn = @reaction_network begin
459459
@ivs t x y
@@ -464,8 +464,17 @@ let
464464
end
465465
end
466466
V,W = getfield.(observed(rn), :lhs)
467-
@test isequal(arguments(ModelingToolkit.unwrap(V)), [rn.iv, rn.sivs[1], rn.sivs[2]])
468-
@test isequal(arguments(ModelingToolkit.unwrap(W)), [rn.iv, rn.sivs[2]])
467+
@test isequal(arguments(ModelingToolkit.unwrap(V)), Any[rn.iv, rn.sivs[1], rn.sivs[2]])
468+
@test isequal(arguments(ModelingToolkit.unwrap(W)), Any[rn.iv, rn.sivs[2]])
469+
end
470+
471+
# Checks that metadata is written properly.
472+
let
473+
rn = @reaction_network rn_observed begin
474+
@observables (X, [description="my_description"]) ~ X1 + X2
475+
k, 0 --> X1 + X2
476+
end
477+
@test getdescription(observed(rn)[1].lhs) == "my_description"
469478
end
470479

471480
# Declares observables implicitly/explicitly.
@@ -480,25 +489,25 @@ let
480489
@observables X ~ X1 + X2
481490
k, 0 --> X1 + X2
482491
end
483-
@test isequal(rn1, rn2)
492+
@test_broken isequal(rn1, rn2)
484493

485494
# Case with metadata.
486495
rn3 = @reaction_network rn_observed begin
487-
@observables (X, [bounds=(0.0, 10.0)]) ~ X1 + X2
496+
@observables (X, [description="description"]) ~ X1 + X2
488497
k, 0 --> X1 + X2
489498
end
490499
rn4 = @reaction_network rn_observed begin
491-
@variables X(t) [bounds=(0.0, 10.0)]
500+
@variables X(t) [description="description"]
492501
@observables X ~ X1 + X2
493502
k, 0 --> X1 + X2
494503
end
495-
@test isequal(rn3, rn4)
504+
@test_broken isequal(rn3, rn4)
496505
end
497506

498507
# Tests various erroneous declarations throw errors.
499508
let
500509
# Independent variable in @compounds.
501-
@test_throws Exception @eval @reaction_network rn_observed begin
510+
@test_throws Exception @eval @reaction_network begin
502511
@observables X(t) ~ X1 + X2
503512
k, 0 --> X1 + X2
504513
end
@@ -513,7 +522,7 @@ let
513522
end
514523

515524
# Multiple @compounds options
516-
@test_throws Exception @eval @reaction_network rn_observed begin
525+
@test_throws Exception @eval @reaction_network begin
517526
@observables X ~ X1 + X2
518527
@observables Y ~ Y1 + Y2
519528
k, 0 --> X1 + X2
@@ -524,35 +533,35 @@ let
524533
X ~ X1 + X2
525534
end
526535
@observables begin
527-
X2 ~ 2(X1 + X2)
536+
X ~ 2(X1 + X2)
528537
end
529538
(p,d), 0 <--> X1 + X2
530539
end
531540

532541
# Default value for compound.
533-
@test_throws Exception @eval @reaction_network rn_observed begin
542+
@test_throws Exception @eval @reaction_network begin
534543
@observables (X = 1.0) ~ X1 + X2
535544
k, 0 --> X1 + X2
536545
end
537546

538547
# Forbidden symbols as observable names.
539-
@test_throws Exception @eval @reaction_network rn_observed begin
548+
@test_throws Exception @eval @reaction_network begin
540549
@observables t ~ t1 + t2
541550
k, 0 --> t1 + t2
542551
end
543-
@test_throws Exception @eval @reaction_network rn_observed begin
552+
@test_throws Exception @eval @reaction_network begin
544553
@observables im ~ i + m
545554
k, 0 --> i + m
546555
end
547556

548557
# Non-trivial observables expression.
549-
@test_throws Exception @eval @reaction_network rn_observed begin
558+
@test_throws Exception @eval @reaction_network begin
550559
@observables X - X1 ~ X2
551560
k, 0 --> X1 + X2
552561
end
553562

554563
# Occurrence of undeclared dependants.
555-
@test_throws Exception @eval @reaction_network rn_observed begin
564+
@test_throws Exception @eval @reaction_network begin
556565
@observables X ~ X1 + X2
557566
k, 0 --> X1
558567
end

0 commit comments

Comments
 (0)