1+ using ModelingToolkit, OrdinaryDiffEq, StochasticDiffEq
2+ using Test, LinearAlgebra
3+
4+
5+ # Change of variables: z = log(x)
6+ # (this implies that x = exp(z) is automatically non-negative)
7+ @independent_variables t
8+ @variables z (t)[1 : 2 , 1 : 2 ]
9+ D = Differential (t)
10+ eqs = [D (D (z)) ~ ones (2 , 2 )]
11+ @mtkcompile sys = System (eqs, t)
12+ @test_nowarn ODEProblem (sys, [z => zeros (2 , 2 ), D (z) => ones (2 , 2 )], (0.0 , 10.0 ))
13+
14+ @parameters α
15+ @variables x (t)
16+ D = Differential (t)
17+ eqs = [D (x) ~ α* x]
18+
19+ tspan = (0. , 1. )
20+ def = [x => 1.0 , α => - 0.5 ]
21+
22+ @mtkcompile sys = System (eqs, t;defaults= def)
23+ prob = ODEProblem (sys, [], tspan)
24+ sol = solve (prob, Tsit5 ())
25+
26+ @variables z (t)
27+ forward_subs = [log (x) => z]
28+ backward_subs = [x => exp (z)]
29+ new_sys = changeofvariables (sys, t, forward_subs, backward_subs)
30+ @test equations (new_sys)[1 ] == (D (z) ~ α)
31+
32+ new_prob = ODEProblem (new_sys, [], tspan)
33+ new_sol = solve (new_prob, Tsit5 ())
34+
35+ @test isapprox (new_sol[x][end ], sol[x][end ], atol= 1e-4 )
36+
37+
38+
39+ # Riccati equation
40+ @parameters α
41+ @variables x (t)
42+ D = Differential (t)
43+ eqs = [D (x) ~ t^ 2 + α - x^ 2 ]
44+ def = [x=> 1. , α => 1. ]
45+ @mtkcompile sys = System (eqs, t; defaults= def)
46+
47+ @variables z (t)
48+ forward_subs = [t + α/ (x+ t) => z ]
49+ backward_subs = [ x => α/ (z- t) - t]
50+
51+ new_sys = changeofvariables (sys, t, forward_subs, backward_subs; simplify= true , t0= 0. )
52+ # output should be equivalent to
53+ # t^2 + α - z^2 + 2 (but this simplification is not found automatically)
54+
55+ tspan = (0. , 1. )
56+ prob = ODEProblem (sys,[],tspan)
57+ new_prob = ODEProblem (new_sys,[],tspan)
58+
59+ sol = solve (prob, Tsit5 ())
60+ new_sol = solve (new_prob, Tsit5 ())
61+
62+ @test isapprox (sol[x][end ], new_sol[x][end ], rtol= 1e-4 )
63+
64+
65+ # Linear transformation to diagonal system
66+ @independent_variables t
67+ @variables x (t)[1 : 3 ]
68+ x = reshape (x, 3 , 1 )
69+ D = Differential (t)
70+ A = [0. - 1. 0. ; - 0.5 0.5 0. ; 0. 0. - 1. ]
71+ right = A* x
72+ eqs = vec (D .(x) .~ right)
73+
74+ tspan = (0. , 10. )
75+ u0 = [x[1 ] => 1.0 , x[2 ] => 2.0 , x[3 ] => - 1.0 ]
76+
77+ @mtkcompile sys = System (eqs, t; defaults= u0)
78+ prob = ODEProblem (sys,[],tspan)
79+ sol = solve (prob, Tsit5 ())
80+
81+ T = eigen (A). vectors
82+ T_inv = inv (T)
83+
84+ @variables z (t)[1 : 3 ]
85+ z = reshape (z, 3 , 1 )
86+ forward_subs = vec (T_inv* x .=> z)
87+ backward_subs = vec (x .=> T* z)
88+
89+ new_sys = changeofvariables (sys, t, forward_subs, backward_subs; simplify= true )
90+
91+ new_prob = ODEProblem (new_sys, [], tspan)
92+ new_sol = solve (new_prob, Tsit5 ())
93+
94+ # test RHS
95+ new_rhs = [eq. rhs for eq in equations (new_sys)]
96+ new_A = Symbolics. value .(Symbolics. jacobian (new_rhs, z))
97+ A = diagm (eigen (A). values)
98+ A = sortslices (A, dims= 1 )
99+ new_A = sortslices (new_A, dims= 1 )
100+ @test isapprox (A, new_A, rtol = 1e-10 )
101+ @test isapprox ( new_sol[x[1 ],end ], sol[x[1 ],end ], rtol= 1e-4 )
102+
103+ # Change of variables for sde
104+ noise_eqs = ModelingToolkit. get_noise_eqs
105+ value = ModelingToolkit. value
106+
107+ @independent_variables t
108+ @brownians B
109+ @parameters μ σ
110+ @variables x (t) y (t)
111+ D = Differential (t)
112+ eqs = [D (x) ~ μ* x + σ* x* B]
113+
114+ def = [x=> 0. , μ => 2. , σ=> 1. ]
115+ @mtkcompile sys = System (eqs, t; defaults= def)
116+ forward_subs = [log (x) => y]
117+ backward_subs = [x => exp (y)]
118+ new_sys = changeofvariables (sys, t, forward_subs, backward_subs)
119+ @test equations (new_sys)[1 ] == (D (y) ~ μ - 1 / 2 * σ^ 2 )
120+ @test noise_eqs (new_sys)[1 ] === value (σ)
121+
122+ # Multiple Brownian and equations
123+ @independent_variables t
124+ @brownians Bx By
125+ @parameters μ σ α
126+ @variables x (t) y (t) z (t) w (t) u (t) v (t)
127+ D = Differential (t)
128+ eqs = [D (x) ~ μ* x + σ* x* Bx, D (y) ~ α* By, D (u) ~ μ* u + σ* u* Bx + α* u* By]
129+ def = [x=> 0. , y=> 0. , u=> 0. , μ => 2. , σ=> 1. , α=> 3. ]
130+ forward_subs = [log (x) => z, y^ 2 => w, log (u) => v]
131+ backward_subs = [x => exp (z), y => w^ .5 , u => exp (v)]
132+
133+ @mtkcompile sys = System (eqs, t; defaults= def)
134+ new_sys = changeofvariables (sys, t, forward_subs, backward_subs)
135+ @test equations (new_sys)[1 ] == (D (z) ~ μ - 1 / 2 * σ^ 2 )
136+ @test equations (new_sys)[2 ] == (D (w) ~ α^ 2 )
137+ @test equations (new_sys)[3 ] == (D (v) ~ μ - 1 / 2 * (α^ 2 + σ^ 2 ))
138+ @test noise_eqs (new_sys)[1 ,1 ] === value (σ)
139+ @test noise_eqs (new_sys)[1 ,2 ] === value (0 )
140+ @test noise_eqs (new_sys)[2 ,1 ] === value (0 )
141+ @test noise_eqs (new_sys)[2 ,2 ] === value (substitute (2 * α* y, backward_subs[2 ]))
142+ @test noise_eqs (new_sys)[3 ,1 ] === value (σ)
143+ @test noise_eqs (new_sys)[3 ,2 ] === value (α)
144+
145+ # Test for Brownian instead of noise
146+ @named sys = System (eqs, t; defaults= def)
147+ new_sys = changeofvariables (sys, t, forward_subs, backward_subs; simplify= false )
148+ @test simplify (equations (new_sys)[1 ]) == simplify ((D (z) ~ μ - 1 / 2 * σ^ 2 + σ* Bx))
149+ @test simplify (equations (new_sys)[2 ]) == simplify ((D (w) ~ α^ 2 + 2 * α* w^ .5 * By))
150+ @test simplify (equations (new_sys)[3 ]) == simplify ((D (v) ~ μ - 1 / 2 * (α^ 2 + σ^ 2 ) + σ* Bx + α* By))
0 commit comments