-
-
Notifications
You must be signed in to change notification settings - Fork 208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix first ODE perturbation example #3098
Conversation
We are doing that over time. Most examples are already updated. We shouldn't merge this one until it's using |
Nice! Since I am working on something somewhat similar to this example, just a few suggestions:
|
I'll also update the second part of the example and can change the whole example to use the |
If you've just got this working, mark the whole thing as |
Very nice! But why the recursive implementation and not directly implement the mathematical equation you gave: function get_power(poly, sym, p)
x = sym
∂ₓ = Differential(x)
p! = factorial(p)
substitute(expand_derivatives((∂ₓ^p)(poly)) / p!, x => 0)
end If there aren't any performance concerns, this probably could/should be used as implementation for an zeroth order respecting |
Beautiful, that's even better! I didn't think about the
I think it primarily depends on what the function should do. Suppose you have a non-polynomial expression, like Also, #2671 was merged, so 0-based arrays should work now (on the master branch), if you would like to try it. |
You are right. But this also means |
This still fails: julia> using Symbolics
julia> def_taylor(x, ps) = sum([a * x^(i - 1) for (i, a) in enumerate(ps)])
def_taylor (generic function with 1 method)
julia> function collect_powers(eq, x, ns; max_power = 100)
eq = substitute(expand(eq), Dict(x^j => 0 for j in (last(ns) + 1):max_power))
eqs = []
for i in ns
powers = Dict(x^j => (i == j ? 1 : 0) for j in 1:last(ns))
e = substitute(eq, powers)
# manually remove zeroth order from higher orders
if 0 in ns && i != 0
e = e - eqs[1]
end
push!(eqs, e)
end
eqs
end
collect_powers (generic function with 1 method)
julia> function solve_coef(eqs, ps)
vals = Dict()
for (i, p) in enumerate(ps)
eq = substitute(eqs[i], vals)
vals[p] = Symbolics.symbolic_linear_solve(eq ~ 0, p)
end
vals
end
solve_coef (generic function with 1 method)
julia> using ModelingToolkit: t_nounits as t, D_nounits as D
julia> order = 2
2
julia> @variables ϵ (y(t))[0:order] (∂∂y(t))[0:order]
3-element Vector{Any}:
ϵ
(y(t))[0:2]
(∂∂y(t))[0:2]
julia> x = def_taylor(ϵ, y)
(y(t))[0] + (y(t))[1]*ϵ + (y(t))[2]*(ϵ^2)
julia> ∂∂x = def_taylor(ϵ, ∂∂y)
(∂∂y(t))[0] + ϵ*(∂∂y(t))[1] + (ϵ^2)*(∂∂y(t))[2]
julia> eq = ∂∂x * (1 + ϵ * x)^2 + 1
1 + ((1 + ((y(t))[0] + (y(t))[1]*ϵ + (y(t))[2]*(ϵ^2))*ϵ)^2)*((∂∂y(t))[0] + ϵ*(∂∂y(t))[1] + (ϵ^2)*(∂∂y(t))[2])
julia> eqs = collect_powers(eq, ϵ, 0:order)
3-element Vector{Any}:
1 + (∂∂y(t))[0]
(∂∂y(t))[1] + 2(y(t))[0]*(∂∂y(t))[0]
(∂∂y(t))[2] + 2(y(t))[0]*(∂∂y(t))[1] + 2(y(t))[1]*(∂∂y(t))[0] + ((y(t))[0]^2)*(∂∂y(t))[0]
julia> vals = solve_coef(eqs, ∂∂y)
Dict{Any, Any} with 3 entries:
(∂∂y(t))[2] => 2.0(y(t))[1] - 3.0((y(t))[0]^2)
(∂∂y(t))[1] => 2.0(y(t))[0]
(∂∂y(t))[0] => -1.0
julia> subs = Dict(∂∂y[i] => D(D(y[i])) for i in eachindex(y))
Dict{Num, Num} with 3 entries:
(∂∂y(t))[2] => Differential(t)(Differential(t)((y(t))[2]))
(∂∂y(t))[1] => Differential(t)(Differential(t)((y(t))[1]))
(∂∂y(t))[0] => Differential(t)(Differential(t)((y(t))[0]))
julia> eqs = [substitute(first(v), subs) ~ substitute(last(v), subs) for v in vals]
3-element Vector{Equation}:
Differential(t)(Differential(t)((y(t))[2])) ~ 2.0(y(t))[1] - 3.0((y(t))[0]^2)
Differential(t)(Differential(t)((y(t))[1])) ~ 2.0(y(t))[0]
Differential(t)(Differential(t)((y(t))[0])) ~ -1.0
julia> using ModelingToolkit, DifferentialEquations
julia> @mtkbuild sys = ODESystem(eqs, t)
Model sys with 6 equations
Unknowns (6):
var"y(t)ˍt"[2]
var"y(t)ˍt"[1]
var"y(t)ˍt"[0]
(y(t))[2]
(y(t))[1]
(y(t))[0]
Parameters (0):
julia> unknowns(sys)
6-element Vector{SymbolicUtils.BasicSymbolic{Real}}:
var"y(t)ˍt"[2]
var"y(t)ˍt"[1]
var"y(t)ˍt"[0]
(y(t))[2]
(y(t))[1]
(y(t))[0]
julia> u0 = zeros(2order + 2)
6-element Vector{Float64}:
0.0
0.0
0.0
0.0
0.0
0.0
julia> u0[3] = 1.0
1.0
julia> prob = ODEProblem(sys, u0, (0, 3.0))
ERROR: BoundsError: attempt to access 3-element Vector{SymbolicUtils.BasicSymbolic{Real}} at index [0]
Stacktrace:
[1] getindex(A::Vector{SymbolicUtils.BasicSymbolic{Real}}, i1::Int64)
@ Base ./essentials.jl:13
[2] fast_substitute(expr::SymbolicUtils.BasicSymbolic{Real}, subs::Dict{Any, Any}; operator::Type)
@ Symbolics ~/.julia/packages/Symbolics/rtkf9/src/variable.jl:577
[3] fast_substitute(eq::Equation, subs::Dict{Any, Any}; operator::Type)
@ Symbolics ~/.julia/packages/Symbolics/rtkf9/src/variable.jl:543
[4] tearing_reassemble(state::TearingState{…}, var_eq_matching::ModelingToolkit.BipartiteGraphs.Matching{…}, full_var_eq_matching::ModelingToolkit.BipartiteGraphs.Matching{…}; simplify::Bool, mm::ModelingToolkit.SparseMatrixCLIL{…})
@ ModelingToolkit.StructuralTransformations ~/.julia/packages/ModelingToolkit/5OzIt/src/structural_transformation/symbolics_tearing.jl:594
[5] tearing_reassemble
@ ~/.julia/packages/ModelingToolkit/5OzIt/src/structural_transformation/symbolics_tearing.jl:230 [inlined]
[6] tearing(sys::NonlinearSystem, state::TearingState{…}; mm::ModelingToolkit.SparseMatrixCLIL{…}, simplify::Bool, kwargs::@Kwargs{…})
@ ModelingToolkit.StructuralTransformations ~/.julia/packages/ModelingToolkit/5OzIt/src/structural_transformation/symbolics_tearing.jl:641
[7] _structural_simplify!(state::TearingState{…}, io::Nothing; simplify::Bool, check_consistency::Bool, fully_determined::Bool, warn_initialize_determined::Bool, dummy_derivative::Bool, kwargs::@Kwargs{…})
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/systemstructure.jl:706
[8] _structural_simplify!
@ ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/systemstructure.jl:676 [inlined]
[9] #structural_simplify!#1221
@ ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/systemstructure.jl:669 [inlined]
[10] __structural_simplify(sys::NonlinearSystem, io::Nothing; simplify::Bool, kwargs::@Kwargs{…})
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/systems.jl:94
[11] __structural_simplify
@ ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/systems.jl:75 [inlined]
[12] structural_simplify(sys::NonlinearSystem, io::Nothing; simplify::Bool, split::Bool, allow_symbolic::Bool, allow_parameter::Bool, conservative::Bool, fully_determined::Bool, kwargs::@Kwargs{})
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/systems.jl:33
[13] structural_simplify (repeats 2 times)
@ ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/systems.jl:28 [inlined]
[14] ModelingToolkit.InitializationProblem{…}(sys::ODESystem, t::Int64, u0map::Vector{…}, parammap::SciMLBase.NullParameters; guesses::Dict{…}, check_length::Bool, warn_initialize_determined::Bool, initialization_eqs::Vector{…}, fully_determined::Bool, check_units::Bool, kwargs::@Kwargs{…})
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/diffeqs/abstractodesystem.jl:1475
[15] (ModelingToolkit.InitializationProblem{…})(::ODESystem, ::Int64, ::Vararg{…}; kwargs::@Kwargs{…})
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/diffeqs/abstractodesystem.jl:1434
[16] ModelingToolkit.InitializationProblem(::ODESystem, ::Int64, ::Vararg{…}; kwargs::@Kwargs{…})
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/diffeqs/abstractodesystem.jl:1422
[17] process_DEProblem(constructor::Type, sys::ODESystem, u0map::Vector{…}, parammap::SciMLBase.NullParameters; implicit_dae::Bool, du0map::Nothing, version::Nothing, tgrad::Bool, jac::Bool, checkbounds::Bool, sparse::Bool, simplify::Bool, linenumbers::Bool, parallel::Symbolics.SerialForm, eval_expression::Bool, eval_module::Module, use_union::Bool, tofloat::Bool, symbolic_u0::Bool, u0_constructor::typeof(identity), guesses::Dict{…}, t::Int64, warn_initialize_determined::Bool, build_initializeprob::Bool, initialization_eqs::Vector{…}, fully_determined::Bool, check_units::Bool, kwargs::@Kwargs{…})
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/diffeqs/abstractodesystem.jl:852
[18] process_DEProblem
@ ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/diffeqs/abstractodesystem.jl:792 [inlined]
[19] (ODEProblem{…})(sys::ODESystem, u0map::Vector{…}, tspan::Tuple{…}, parammap::SciMLBase.NullParameters; callback::Nothing, check_length::Bool, warn_initialize_determined::Bool, eval_expression::Bool, eval_module::Module, kwargs::@Kwargs{})
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/diffeqs/abstractodesystem.jl:1022
[20] ODEProblem
@ ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/diffeqs/abstractodesystem.jl:1010 [inlined]
[21] (ODEProblem{true, SciMLBase.AutoSpecialize})(sys::ODESystem, u0map::Vector{Float64}, tspan::Tuple{Int64, Float64})
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/diffeqs/abstractodesystem.jl:1010
[22] (ODEProblem{true})(::ODESystem, ::Vector{Float64}, ::Vararg{Any}; kwargs::@Kwargs{})
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/diffeqs/abstractodesystem.jl:997
[23] (ODEProblem{true})(::ODESystem, ::Vector{Float64}, ::Vararg{Any})
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/diffeqs/abstractodesystem.jl:996
[24] ODEProblem(::ODESystem, ::Vector{Float64}, ::Vararg{Any}; kwargs::@Kwargs{})
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/diffeqs/abstractodesystem.jl:986
[25] ODEProblem(::ODESystem, ::Vector{Float64}, ::Vararg{Any})
@ ModelingToolkit ~/.julia/packages/ModelingToolkit/5OzIt/src/systems/diffeqs/abstractodesystem.jl:985
[26] top-level scope
@ REPL[22]:1
Some type information was truncated. Use `show(err)` to see complete types. |
Both examples work now (with one-based arrays only) and I added the outputs using the I am not completely happy with the definition of the initial condition with u0 = zeros(2order + 2)
u0[3] = 1.0 The order of the equations defined by I think making the indices zero based and using a different implementation for |
I think so, yes! I will try to add it in JuliaSymbolics/Symbolics.jl#1298.
Ah shit. If I get time I will have a look.
Agreed, making the example correct is most important! :) |
I think you can do something like (with 1-based u0 = Dict(unknowns(sys) .=> 0.0) # set all initial conditions
u0[D(y[1])] => 1.0 # ... except this one |
Thank you, that worked. Now everything is good to merge from my side. |
Checklist
contributor guidelines, in particular the SciML Style Guide and
COLPRAC.
Additional context
This PR makes the first part from the Perturbation theory for ODEs example work. This fixes #1994.
The following things needed to be changed:
collect powers
included zero order terms in each order term,Symbolics.jl
was wrong,def_taylor
did not match,enumerate
instead ofeachindex
for symbolic arrays to get a linear index instead of a CartesianIndex (eachindex(IndexLinear(), ...)
would also work).The following things were not changed but would probably improve the example:
Symbolics.coeff
incollect_powers
, butcoeff
currently can't be used to get the zeroth order term correctly (see second point in Symbolics.coeff silently returns incorrect results for products that need expansion, unbounded recursion on fractions. JuliaSymbolics/Symbolics.jl#910).y
and its derivative at zero, to match the order they represent, but non-one-based indexing isn't well supported yet (see Structural simplification fails on non-1-indexed variable array #2670).@example ...
feature fromDocumenter.jl
to add the output of commands to the documentation. This way it would be easier to detect when some change breaks the example.