Skip to content

Commit 0d6b76a

Browse files
authored
Merge pull request #1211 from SciML/add_jac_tests
Add additional Jacobian tests (including sparsity)
2 parents 92a98a2 + 3b0a815 commit 0d6b76a

File tree

2 files changed

+113
-10
lines changed

2 files changed

+113
-10
lines changed

test/simulation_and_solving/jacobian_construction.jl

Lines changed: 107 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
### Prepares Tests ###
22

33
# Fetch packages.
4-
using Catalyst, DiffEqBase, Test
4+
using Catalyst, DiffEqBase, OrdinaryDiffEqRosenbrock, Test
55

66
# Sets stable rng number.
77
using StableRNGs
@@ -19,7 +19,7 @@ let
1919
(3.0, 1.0), ∅ Y
2020
(5.0, 2.0), X + Y XY
2121
end
22-
22+
2323
test_jac = jac_eval(jacobian_network_1, [:X => 1.0, :Y => 1.0, :XY => 1.0], [], 0.0)
2424
real_jac = [-6.0 -5.0 2.0; -5.0 -6.0 2.0; 5.0 5.0 -2.0]
2525
@test test_jac == real_jac
@@ -33,7 +33,7 @@ let
3333
(p3 * X, 1.0), X + Y XY
3434
end
3535
@unpack X, Y, XY, p1, p2, p3 = jacobian_network_2
36-
36+
3737
for factor in [1e-2, 1e-1, 1e0, 1e1, 1e2], repeat in 1:10
3838
u = Dict(rnd_u0(jacobian_network_2, rng; factor))
3939
p = Dict(rnd_ps(jacobian_network_2, rng; factor))
@@ -67,4 +67,107 @@ let
6767
p[k3]*u[B] p[k3]*u[A] -p[k4]-3 * p[k5] * u[C]^2 / 2]
6868
@test test_jac real_jac
6969
end
70-
end
70+
end
71+
72+
# Checks that the Jacobians (dense and sparse) are identical for different system types.
73+
let
74+
# Creates model (vaguely messy model without conserved quantities).
75+
rn = @reaction_network begin
76+
(p,d), 0 <--> (X,Y)
77+
(k1,k2), X + Y <--> XY
78+
(k1,k2), X + 2Y <--> XY2
79+
(k1,k2), XY + XY2 <--> X2Y3
80+
d, (XY2,X2Y3) --> 0
81+
mm(X2Y3,v,K), 0 --> Z
82+
(k3,k4), 3Z <--> Z3
83+
1.0, X3 --> 0
84+
end
85+
86+
# Performs tests for different randomised values (to be thoroughly sure).
87+
for factor in [0.1, 1.0, 10.0]
88+
# Creates randomised species and parameter values. Generates jacobians (dense/sparse).
89+
u0 = rnd_u0(rn, rng; factor)
90+
ps = rnd_ps(rn, rng; factor)
91+
oprob_jac = ODEProblem(rn, u0, 1.0, ps; jac = true, sparse = false)
92+
oprob_sjac = ODEProblem(rn, u0, 1.0, ps; jac = true, sparse = true)
93+
sprob_jac = SDEProblem(rn, u0, 1.0, ps; jac = true, sparse = false)
94+
sprob_sjac = SDEProblem(rn, u0, 1.0, ps; jac = true, sparse = true)
95+
nlprob_jac = NonlinearProblem(rn, u0, ps; jac = true, sparse = false)
96+
nlprob_sjac = NonlinearProblem(rn, u0, ps; jac = true, sparse = true)
97+
98+
# Checks that Jacobians ar identical.
99+
function eval_jac(prob, sparse)
100+
J = sparse ? deepcopy(prob.f.jac_prototype) : zeros(length(prob.u0), length(prob.u0))
101+
ModelingToolkit.is_time_dependent(prob) ? prob.f.jac(J, prob.u0, prob.p, 0.0) : prob.f.jac(J, prob.u0, prob.p)
102+
return J
103+
end
104+
@test eval_jac(oprob_jac, false) == eval_jac(sprob_jac, false) == eval_jac(nlprob_jac, false)
105+
@test_broken eval_jac(oprob_sjac, true) == eval_jac(sprob_sjac, true) == eval_jac(nlprob_sjac, true) # https://github.com/SciML/ModelingToolkit.jl/issues/3527
106+
end
107+
end
108+
109+
### Sparse Jacobian Tests ###
110+
111+
# Checks that generated dense/sparse Jacobians are identical.
112+
let
113+
# Creates model (vaguely messy model without conserved quantities).
114+
# Model includes a time-dependent reaction.
115+
rn = @reaction_network begin
116+
(p,d), 0 <--> (X,Y,Z)
117+
k1, X + Y --> XY
118+
k2, X + 2Z --> XZ2
119+
k3, Y3 +X2 --> Y3Z2
120+
k4, X + Y + Z --> XYZ
121+
k5, XZ2 + Y3Z2 --> XY3Z4
122+
k6, XYZ + XYZ --> X2Y2Z2
123+
d, (XY3Z4, X2Y2Z2) --> 0
124+
X + Y, V --> 0
125+
k7/(1 + t), 2V --> V2
126+
Z, V2 --> 0
127+
end
128+
129+
# Performs tests for different randomised values (to be thoroughly sure).
130+
for factor in [0.1, 1.0, 10.0]
131+
# Creates randomised species and parameter values. Generates jacobians (dense/sparse).
132+
u0 = rnd_u0(rn, rng; factor)
133+
t_val = factor*rand()
134+
ps = rnd_ps(rn, rng; factor)
135+
jac = jac_eval(rn, u0, ps, t_val; sparse = false)
136+
jac_sparse = jac_eval(rn, u0, ps, t_val; sparse = true)
137+
138+
# Check correctness (both by converting to sparse jac to dense, and through multiplication with other matrix).
139+
@test Matrix(jac_sparse) == jac
140+
mat = factor*rand(rng, length(u0), length(u0))
141+
@test jac_sparse * mat jac * mat
142+
end
143+
end
144+
145+
# Tests that simulations with different Jacobian and sparsity options are identical.
146+
let
147+
# Creates model (vaguely messy model without conserved quantities).
148+
rn = @reaction_network begin
149+
(v0 + mm(X,v,K),d), 0 <--> X + 2Y
150+
(k1,k2), X + Y <--> XY
151+
(k1,k2), X + Y2 <--> XY2
152+
(k3,k4), XY + XY2 <--> X2Y3
153+
1.0, (XY,XY2,X2Y3) --> 0
154+
mm(X2Y3,v,K), 0 --> Z
155+
(k3*X,k4*Y), 3Z <--> Z3
156+
d, Z --> 0
157+
end
158+
159+
# Generates initial conditions and parameter values. Creates problems with/o (sparse/dense) jacobian.
160+
u0 = rnd_u0(rn, rng)
161+
ps = rnd_ps(rn, rng)
162+
oprob = ODEProblem(rn, u0, 1.0, ps; jac = false, sparse = false)
163+
oprob_j = ODEProblem(rn, u0, 1.0, ps; jac = true, sparse = false)
164+
oprob_s = ODEProblem(rn, u0, 1.0, ps; jac = false, sparse = true)
165+
oprob_js = ODEProblem(rn, u0, 1.0, ps; jac = true, sparse = true)
166+
167+
# Simulates system with implicit solver. Checks that all solutions are identical.
168+
sol = solve(oprob, Rosenbrock23(), saveat = 0.1, abstol = 1e-8, reltol = 1e-8)
169+
sol_j = solve(oprob_j, Rosenbrock23(), saveat = 0.1, abstol = 1e-8, reltol = 1e-8)
170+
sol_s = solve(oprob_s, Rosenbrock23(), saveat = 0.1, abstol = 1e-8, reltol = 1e-8)
171+
sol_js = solve(oprob_js, Rosenbrock23(), saveat = 0.1, abstol = 1e-8, reltol = 1e-8)
172+
@test sol sol_j sol_s sol_js
173+
end

test/test_functions.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ end
3838
# Evaluates the the drift function of the ODE corresponding to a reaction network.
3939
# Also checks that in place and out of place evaluations are identical.
4040
function f_eval(rs::ReactionSystem, u, p, t; combinatoric_ratelaws = true)
41-
prob = ODEProblem(rs, u, (0.0, 0.0), p; combinatoric_ratelaws)
41+
prob = ODEProblem(rs, u, 0.0, p; combinatoric_ratelaws)
4242
du = zeros(length(u))
4343
prob.f(du, prob.u0, prob.p, t)
4444
@test du == prob.f(prob.u0, prob.p, t)
@@ -47,9 +47,9 @@ end
4747

4848
# Evaluates the the Jacobian of the drift function of the ODE corresponding to a reaction network.
4949
# Also checks that in place and out of place evaluations are identical.
50-
function jac_eval(rs::ReactionSystem, u, p, t; combinatoric_ratelaws = true)
51-
prob = ODEProblem(rs, u, (0.0, 0.0), p; jac = true, combinatoric_ratelaws)
52-
J = zeros(length(u), length(u))
50+
function jac_eval(rs::ReactionSystem, u, p, t; combinatoric_ratelaws = true, sparse = false)
51+
prob = ODEProblem(rs, u, 0.0, p; jac = true, combinatoric_ratelaws, sparse)
52+
J = sparse ? deepcopy(prob.f.jac_prototype) : zeros(length(u), length(u))
5353
prob.f.jac(J, prob.u0, prob.p, t)
5454
@test J == prob.f.jac(prob.u0, prob.p, t)
5555
return J
@@ -58,9 +58,9 @@ end
5858
# Evaluates the the diffusion function of the SDE corresponding to a reaction network.
5959
# Also checks that in place and out of place evaluations are identical.
6060
function g_eval(rs::ReactionSystem, u, p, t; combinatoric_ratelaws = true)
61-
prob = SDEProblem(rs, u, (0.0, 0.0), p; combinatoric_ratelaws)
61+
prob = SDEProblem(rs, u, 0.0, p; combinatoric_ratelaws)
6262
dW = zeros(length(u), numreactions(rs))
6363
prob.g(dW, prob.u0, prob.p, t)
6464
@test dW == prob.g(prob.u0, prob.p, t)
6565
return dW
66-
end
66+
end

0 commit comments

Comments
 (0)