@@ -53,6 +53,120 @@ function liouville_transform(sys::System; kwargs...)
53
53
)
54
54
end
55
55
56
+ """
57
+ $(TYPEDSIGNATURES)
58
+
59
+ Generates the set of ODEs after change of variables.
60
+
61
+
62
+ Example:
63
+
64
+ ```julia
65
+ using ModelingToolkit, OrdinaryDiffEq, Test
66
+
67
+ # Change of variables: z = log(x)
68
+ # (this implies that x = exp(z) is automatically non-negative)
69
+
70
+ @parameters t α
71
+ @variables x(t)
72
+ D = Differential(t)
73
+ eqs = [D(x) ~ α*x]
74
+
75
+ tspan = (0., 1.)
76
+ u0 = [x => 1.0]
77
+ p = [α => -0.5]
78
+
79
+ @named sys = ODESystem(eqs; defaults=u0)
80
+ prob = ODEProblem(sys, [], tspan, p)
81
+ sol = solve(prob, Tsit5())
82
+
83
+ @variables z(t)
84
+ forward_subs = [log(x) => z]
85
+ backward_subs = [x => exp(z)]
86
+
87
+ @named new_sys = changeofvariables(sys, forward_subs, backward_subs)
88
+ @test equations(new_sys)[1] == (D(z) ~ α)
89
+
90
+ new_prob = ODEProblem(new_sys, [], tspan, p)
91
+ new_sol = solve(new_prob, Tsit5())
92
+
93
+ @test isapprox(new_sol[x][end], sol[x][end], atol=1e-4)
94
+ ```
95
+
96
+ """
97
+ function changeofvariables (sys:: System , forward_subs, backward_subs; simplify= false , t0= missing )
98
+ t = independent_variable (sys)
99
+
100
+ old_vars = first .(backward_subs)
101
+ new_vars = last .(forward_subs)
102
+ kept_vars = setdiff (states (sys), old_vars)
103
+ rhs = [eq. rhs for eq in equations (sys)]
104
+
105
+ # use: dz/dt = ∂z/∂x dx/dt + ∂z/∂t
106
+ dzdt = Symbolics. derivative ( first .(forward_subs), t )
107
+ new_eqs = Equation[]
108
+ for (new_var, ex) in zip (new_vars, dzdt)
109
+ for ode_eq in equations (sys)
110
+ ex = substitute (ex, ode_eq. lhs => ode_eq. rhs)
111
+ end
112
+ ex = substitute (ex, Dict (forward_subs))
113
+ ex = substitute (ex, Dict (backward_subs))
114
+ if simplify
115
+ ex = Symbolics. simplify (ex, expand= true )
116
+ end
117
+ push! (new_eqs, Differential (t)(new_var) ~ ex)
118
+ end
119
+
120
+ defs = get_defaults (sys)
121
+ new_defs = Dict ()
122
+ for f_sub in forward_subs
123
+ # TODO call value(...)?
124
+ ex = substitute (first (f_sub), defs)
125
+ if ! ismissing (t0)
126
+ ex = substitute (ex, t => t0)
127
+ end
128
+ new_defs[last (f_sub)] = ex
129
+ end
130
+ return ODESystem (new_eqs;
131
+ defaults= new_defs,
132
+ observed= vcat (observed (sys),first .(backward_subs) .~ last .(backward_subs))
133
+ )
134
+ end
135
+
136
+ function change_of_variable_SDE (sys:: System , forward_subs, backward_subs, iv; simplify= false , t0= missing )
137
+ t = independent_variable (sys)
138
+
139
+ old_vars = first .(backward_subs)
140
+ new_vars = last .(forward_subs)
141
+
142
+ # use: f = Y(t, X)
143
+ # use: dY = (∂f/∂t + μ∂f/∂x + (1/2)*σ^2*∂2f/∂x2)dt + σ∂f/∂xdW
144
+ old_eqs = equations (sys)
145
+ old_noise = get_noiseeqs (sys)
146
+ ∂f∂t = Symbolics. derivative ( first .(forward_subs), t )
147
+ ∂f∂x = Symbolics. derivative ( first .(forward_subs), old_vars )
148
+ ∂2f∂x2 = Symbolics. derivative ( ∂f∂x, old_vars )
149
+ new_eqs = Equation[]
150
+
151
+ for (new_var, eq, noise, first, second, third) in zip (new_vars, old_eqs, old_noise, ∂f∂t, ∂f∂x, ∂2f∂x2)
152
+ ex = first + eq. rhs * second + 1 / 2 * noise^ 2 * third
153
+ for eqs in old_eqs
154
+ ex = substitute (ex, eqs. lhs => eqs. rhs)
155
+ end
156
+ ex = substitute (ex, Dict (forward_subs))
157
+ ex = substitute (ex, Dict (backward_subs))
158
+ if simplify
159
+ ex = Symbolics. simplify (ex, expand= true )
160
+ end
161
+ push! (new_eqs, Differential (t)(new_var) ~ ex)
162
+ end
163
+ new_noise = [noise * div for (noise, div) in zip (old_noise, ∂f∂x)]
164
+
165
+ return SDESystem (new_eqs, new_noise;
166
+ observed= vcat (observed (sys),first .(backward_subs) .~ last .(backward_subs))
167
+ )
168
+ end
169
+
56
170
"""
57
171
change_independent_variable(
58
172
sys::System, iv, eqs = [];
0 commit comments