@@ -53,6 +53,120 @@ function liouville_transform(sys::System; kwargs...)
5353 )
5454end
5555
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+
56170"""
57171 change_independent_variable(
58172 sys::System, iv, eqs = [];
0 commit comments