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