Skip to content

Commit 164b5d3

Browse files
committed
Add tests for differentials in dsl
1 parent 7b816cc commit 164b5d3

File tree

2 files changed

+252
-3
lines changed

2 files changed

+252
-3
lines changed

src/reaction_network.jl

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,7 @@ function read_equations_options(options, variables_declared)
835835
add_default_diff = false
836836
for eq in equations
837837
((eq.head != :call) || (eq.args[1] != :~)) && error("Malformed equation: \"$eq\". Equation's left hand and right hand sides should be separated by a \"~\".")
838-
(eq.args[2] isa Symbol || eq.args[2].head != :call) && continue
838+
(!(eq.args[2] isa Expr) || eq.args[2].head != :call) && continue
839839
if (eq.args[2].args[1] == :D) && (eq.args[2].args[2] isa Symbol) && (length(eq.args[2].args) == 2)
840840
diff_var = eq.args[2].args[2]
841841
in(diff_var, forbidden_symbols_error) && error("A forbidden symbol ($(diff_var)) was used as an variable in this differential equation: $eq")
@@ -854,7 +854,6 @@ function create_differential_expr(options, add_default_diff, used_syms)
854854
# If the default differential (D(...)) was used in equations, this is added to the expression.
855855
diffexpr = (haskey(options, :differentials) ? options[:differentials].args[3] : MacroTools.striplines(:(begin end)))
856856
diffexpr = option_block_form(diffexpr)
857-
add_default_diff && push!(diffexpr.args, :(D = Differential($(DEFAULT_IV_SYM))))
858857

859858
# Goes through all differentials, checking that they are correctly formatted and their symbol is not used elsewhere.
860859
for dexpr in diffexpr.args
@@ -863,7 +862,13 @@ function create_differential_expr(options, add_default_diff, used_syms)
863862
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.")
864863
in(dexpr.args[1], forbidden_symbols_error) && error("A forbidden symbol ($(dexpr.args[1])) was used as a differential name.")
865864
end
866-
865+
866+
# If the default differential D has been used, but not pre-declared using the @differenitals
867+
# options, add this declaration to the list of declared differentials.
868+
if add_default_diff && !any(diff_dec.args[1] == :D for diff_dec in diffexpr.args)
869+
push!(diffexpr.args, :(D = Differential($(DEFAULT_IV_SYM))))
870+
end
871+
println(diffexpr)
867872
return diffexpr
868873
end
869874

test/reactionsystem_structure/hybrid_equation_reaction_systems.jl

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,250 @@ let
602602
@test solve(oprob_prog, Rosenbrock23()) == solve(oprob_dsl, Rosenbrock23())
603603
end
604604

605+
# Checks that equations can both be declared in a single line, or within a `begin ... end` block.
606+
let
607+
# Checks for system with a single differential equation.
608+
rs_1_line = @reaction_network rs_1 begin
609+
@equations D(M) ~ -M*I
610+
i, S + I --> 2I
611+
r, I --> R
612+
end
613+
rs_1_block = @reaction_network rs_1 begin
614+
@equations begin
615+
D(M) ~ -M*I
616+
end
617+
i, S + I --> 2I
618+
r, I --> R
619+
end
620+
@test rs_1_line == rs_1_block
621+
622+
# Checks for system with a single algebraic equation.
623+
rs_2_line = @reaction_network rs_2 begin
624+
@variables H(t)
625+
@equations H ~ 100 - I
626+
i, S + I --> 2I
627+
r, I --> R
628+
end
629+
rs_2_block = @reaction_network rs_2 begin
630+
@variables H(t)
631+
@equations begin
632+
H ~ 100 - I
633+
end
634+
i, S + I --> 2I
635+
r, I --> R
636+
end
637+
@test rs_2_line == rs_2_block
638+
end
639+
640+
# Checks that lhs variable is correctly inferred from differential equations.
641+
let
642+
# Checks for system with a differential equation and an algebraic equation.
643+
# Here, `H` is defined using `@variables`, but M should be inferred.
644+
rs_1 = @reaction_network begin
645+
@variables H(t)
646+
@equations begin
647+
D(M) ~ -M*I
648+
H ~ 100 - I
649+
end
650+
i, S + I --> 2I
651+
r, I --> R
652+
end
653+
issetequal(species(rs_1), [rs_1.S, rs_1.I, rs_1.R])
654+
issetequal(unknowns(rs_1)[4:5], [rs_1.H, rs_1.M])
655+
656+
# Checks for system with two differential equations, and which do not use `@variables`,
657+
rs_2 = @reaction_network hybrid_rs begin
658+
@equations begin
659+
D(V) ~ X/(1+X) - V
660+
D(N) ~ - V
661+
end
662+
(p,d), 0 <--> X
663+
end
664+
issetequal(species(rs_2), [rs_2.X])
665+
issetequal(unknowns(rs_2)[2:3], [rs_2.V, rs_2.N])
666+
667+
# Checks for system with two differential equations, where one is defined using `@variables`.
668+
rs_2 = @reaction_network hybrid_rs begin
669+
@variables N(t)
670+
@equations begin
671+
D(V) ~ X/(1+X) - V
672+
D(N) ~ - V
673+
end
674+
(p,d), 0 <--> X
675+
end
676+
issetequal(species(rs_2), [rs_2.X])
677+
issetequal(unknowns(rs_2)[2:3], [rs_2.V, rs_2.N])
678+
end
679+
680+
# Checks that equations can be formatted in various ways. Tries e.g. isolating a single number on
681+
# either side of the equality.
682+
# Checks that various weird function can be used within equations.
683+
# Checks that special symbols, like π and t can be used within equations.
684+
let
685+
# Declares models with a single equation, formatted in various ways.
686+
rs_1 = @reaction_network rs begin
687+
@parameters p q
688+
@species X(t)
689+
@variables A(t) B(t)
690+
@equations X^2 + log(A+X) ~ 1 - sqrt(B) + sin(p + X + π)/exp(A/(1+t)) + q
691+
end
692+
rs_2 = @reaction_network rs begin
693+
@parameters p q
694+
@species X(t)
695+
@variables A(t) B(t)
696+
@equations X^2 + log(A+X) + sqrt(B) - sin(p + X + π)/exp(A/(1+t)) - q ~ 1
697+
end
698+
rs_3 = @reaction_network rs begin
699+
@parameters p q
700+
@species X(t)
701+
@variables A(t) B(t)
702+
@equations X^2 + log(A+X) + sqrt(B) - sin(p + X + π)/exp(A/(1+t)) - 1 - q ~ 0
703+
end
704+
rs_4 = @reaction_network rs begin
705+
@parameters p q
706+
@species X(t)
707+
@variables A(t) B(t)
708+
@equations 0 ~ X^2 + log(A+X) + sqrt(B) - sin(p + X + π)/exp(A/(1+t)) - 1 - q
709+
end
710+
rs_5 = @reaction_network rs begin
711+
@parameters p q
712+
@species X(t)
713+
@variables A(t) B(t)
714+
@equations q ~ X^2 + log(A+X) + sqrt(B) - sin(p + X + π)/exp(A/(1+t)) - 1
715+
end
716+
rs_6 = @reaction_network rs begin
717+
@parameters p q
718+
@species X(t)
719+
@variables A(t) B(t)
720+
@equations X^2 + log(A+X) + (A + B)^p ~ 1 - sqrt(B) + sin(p + X + π)/exp(A/(1+t)) + q + (A + B)^p
721+
end
722+
723+
# Uses a special function to check that all equations indeed are identical.
724+
function is_eqs_equal(rs1, rs2; eq_idx = 1)
725+
eq1 = equations(rs1)[eq_idx]
726+
eq2 = equations(rs2)[eq_idx]
727+
isequal(eq1.lhs - eq1.rhs - eq2.lhs + eq2.rhs, 0.0) && return true
728+
isequal(eq1.lhs - eq1.rhs + eq2.lhs - eq2.rhs, 0.0) && return true
729+
return false
730+
end
731+
@test is_eqs_equal(rs_1, rs_2)
732+
@test is_eqs_equal(rs_1, rs_3)
733+
@test is_eqs_equal(rs_1, rs_4)
734+
@test is_eqs_equal(rs_1, rs_5)
735+
@test is_eqs_equal(rs_1, rs_6)
736+
end
737+
738+
# Checks that custom differentials can be declared.
739+
# Checks that non-default ivs work, and that a new differential using this can overwrite the default one.
740+
let
741+
# Declares the reaction system using the default differential and iv.
742+
rs_1 = @reaction_network begin
743+
@equations D(N) ~ -N
744+
(p,d), 0 <--> X
745+
end
746+
747+
# Declares the reaction system using a new iv, and overwriting the default differential.
748+
rs_2 = @reaction_network begin
749+
@ivs τ
750+
@species X(τ)
751+
@variables N(τ)
752+
@differentials D = Differential(τ)
753+
@equations D(N) ~ -N
754+
(p,d), 0 <--> X
755+
end
756+
757+
# Declares the reaction system using a new differential and iv.
758+
rs_3 = @reaction_network begin
759+
@ivs τ
760+
@species X(τ)
761+
@variables N(τ)
762+
@differentials Δ = Differential(τ)
763+
@equations Δ(N) ~ -N
764+
(p,d), 0 <--> X
765+
end
766+
767+
# Simulates all three models, checking that the results are identical.
768+
u0 = [:X => 5.0, :N => 10.0]
769+
tspan = (0.0, 10.)
770+
ps = [:p => 1.0, :d => 0.2]
771+
oprob_1 = ODEProblem(rs_1, u0, tspan, ps)
772+
oprob_2 = ODEProblem(rs_2, u0, tspan, ps)
773+
oprob_3 = ODEProblem(rs_3, u0, tspan, ps)
774+
@test solve(oprob_1, Tsit5()) == solve(oprob_2, Tsit5()) == solve(oprob_3, Tsit5())
775+
end
776+
777+
778+
# Checks that various misformatted declarations yield errors.
779+
let
780+
# Symbol in equation not appearing elsewhere (1).
781+
@test_throws Exception @eval @reaction_network begin
782+
@equations D(V) ~ -X
783+
end
784+
785+
# Symbol in equation not appearing elsewhere (2).
786+
@test_throws Exception @eval @reaction_network begin
787+
@equations 1 + log(x) ~ 2X
788+
end
789+
790+
# Attempting to infer differential variable not isolated on lhs (1).
791+
@test_throws Exception @eval @reaction_network begin
792+
@equations D(V) + 1 ~ 0
793+
end
794+
795+
# Attempting to infer differential variable not isolated on lhs (2).
796+
@test_throws Exception @eval @reaction_network begin
797+
@equations -1.0 ~ D(V)
798+
end
799+
800+
# Attempting to infer differential operator not isolated on lhs (1).
801+
@test_throws Exception @eval @reaction_network begin
802+
@variables V(t)
803+
@equations D(V) + 1 ~ 0
804+
end
805+
806+
# Attempting to infer a variable when using a non-default differential.
807+
@test_throws Exception @eval @reaction_network begin
808+
@differentials Δ = Differential(t)
809+
@equations Δ(V) ~ -1,0
810+
end
811+
812+
# Attempting to create a new differential from an unknown iv.
813+
@test_throws Exception @eval @reaction_network begin
814+
@differentials D = Differential(τ)
815+
end
816+
817+
# Misformatted expression for a differential.
818+
@reaction_network begin
819+
@variables D
820+
@differentials d ~ D
821+
end
822+
823+
# Several equations without `begin ... end` block.
824+
@test_throws Exception @eval @reaction_network begin
825+
@variables V(t)
826+
@equations D(V) + 1 ~ - 1.0
827+
end
828+
829+
# Undeclared differential.
830+
@test_throws Exception @eval @reaction_network begin
831+
@species V
832+
@equations Δ(V) ~ -1.0
833+
end
834+
835+
# System using multiple ivs.
836+
@test_throws Exception @eval @reaction_network begin
837+
@ivs τ Τ
838+
@variables n(τ) N(Τ)
839+
@differentials begin
840+
δ = Differential(τ)
841+
Δ = Differential(Τ)
842+
end
843+
@equations begin
844+
δ(n) ~ -n
845+
Δ(N) ~ -N
846+
end
847+
end
848+
end
605849

606850
### Error Tests ###
607851

0 commit comments

Comments
 (0)