Non-autonomous Linear ODE / Lie Group ODE Solvers
Non-autonomous linear ODE solvers focus on equations in the general form of
and utilize the Lie group structure of the solution to accelerate the numerical methods and capture certain properties in the solution process. One common simplification is for solvers to require state-independent operators, which implies the form:
Another type of solver is needed when the operators are state-dependent, i.e.
Others specifically require linearity, i.e.
where is a constant operator.
Standard ODE Integrators
The standard ODE integrators will work on Non-autonomous linear ODE problems via an automatic transformation to a first-order ODE. See the ODE solvers page for more details.
Specialized OrdinaryDiffEq.jl Integrators
Unless otherwise specified, the OrdinaryDiffEq algorithms all come with a 3rd order Hermite polynomial interpolation. The algorithms denoted as having a “free” interpolation means that no extra steps are required for the interpolation. For the non-free higher order interpolating functions, the extra steps are computed lazily (i.e. not during the solve).
Note that all of these methods are fixed timestep unless otherwise specified.
Exponential Methods for Linear and Affine Problems
These methods require that is constant.
-
LinearExponential
- Exact solution formula for linear, time-independent problems.
Options:
-
krylov
- symbol. One of-
:off (default) - cache the operator beforehand. Requires
Matrix(A)
method defined for the operatorA
. -
:simple - uses simple Krylov approximations with fixed subspace size
m
. -
:adaptive - uses adaptive Krylov approximations with internal timestepping.
-
-
m
- integer, default:30
. Controls the size of Krylov subspace ifkrylov=:simple
, and the initial subspace size ifkrylov=:adaptive
. -
iop
- integer, default:0
. If not zero, determines the length of the incomplete orthogonalization procedure (IOP) [1]. Note that if the linear operator/Jacobian is hermitian, then the Lanczos algorithm will always be used and the IOP setting is ignored.
using DifferentialEquations
_A = [2 -1; -3 -5] / 5
A = DiffEqArrayOperator(_A)
prob = ODEProblem(A, [1.0, -1.0], (1.0, 6.0))
sol = solve(prob, LinearExponential())
retcode: Success
Interpolation: 3rd order Hermite
t: 2-element Vector{Float64}:
1.0
6.0
u: 2-element Vector{Vector{Float64}}:
[1.0, -1.0]
[11.923375744981003, -4.833128734161089]
LinearExponential is exact, and thus it uses |
State-Independent Solvers
These methods require is only dependent on the independent variable, i.e. .
-
MagnusMidpoint
- Second order Magnus Midpoint method. -
MagnusLeapfrog
- Second order Magnus Leapfrog method. -
MagnusGauss4
- Fourth order Magnus method approximated using a two stage Gauss quadrature. -
MagnusGL4
- Fourth order Magnus method approximated using Gauss-Legendre quadrature. -
MagnusNC6
- Sixth order Magnus method approximated using Newton-Cotes quadrature. -
MagnusGL6
- Sixth order Magnus method approximated using Gauss-Legendre quadrature. -
MagnusNC8
- Eighth order Magnus method approximated using Newton-Cotes quadrature. -
MagnusGL8
- Eighth order Magnus method approximated using Gauss-Legendre quadrature.
Example:
function update_func(A, u, p, t)
A[1, 1] = cos(t)
A[2, 1] = sin(t)
A[1, 2] = -sin(t)
A[2, 2] = cos(t)
end
A = DiffEqArrayOperator(ones(2, 2), update_func = update_func)
prob = ODEProblem(A, ones(2), (1.0, 6.0))
sol = solve(prob, MagnusGL6(), dt = 1 / 10)
retcode: Success
Interpolation: 3rd order Hermite
t: 51-element Vector{Float64}:
1.0
1.1
1.2000000000000002
1.3000000000000003
1.4000000000000004
1.5000000000000004
1.6000000000000005
1.7000000000000006
1.8000000000000007
1.9000000000000008
⋮
5.199999999999998
5.299999999999998
5.399999999999998
5.499999999999997
5.599999999999997
5.699999999999997
5.799999999999996
5.899999999999996
6.0
u: 51-element Vector{Vector{Float64}}:
[1.0, 1.0]
[0.9560322602141308, 1.138059339025732]
[0.8837222756549167, 1.2712953135220313]
[0.7836511349460551, 1.3924887039087297]
[0.6585912321071351, 1.4945430086037101]
[0.5134387044387002, 1.5712483084024886]
[0.35484956094894243, 1.6179942444961344]
[0.190617494783206, 1.6323079788211143]
[0.028885769775000115, 1.6141203517260592]
[-0.12267917859524063, 1.5657147744869344]
⋮
[0.1649471518131617, 0.19050761906534755]
[0.19016510949800752, 0.18488701621917145]
[0.21691662630888853, 0.17939687440026209]
[0.24554653627671552, 0.17419950752424784]
[0.27642938801560896, 0.16953149318836871]
[0.3099648774418323, 0.16572504624198953]
[0.3465668460745136, 0.16323354028339193]
[0.3866431012103112, 0.1626603704342466]
[0.4305628420420221, 0.16478922184291447]
The initial values for are irrelevant in this and similar cases, as the update_func
immediately overwrites them. Starting with ones(2,2)
is just a convenient way to get a mutable 2x2 matrix.
State-Dependent Solvers
These methods can be used when is dependent on the state variables, i.e. .
-
CayleyEuler
- First order method using Cayley transformations. -
LieEuler
- First order Lie Euler method. -
RKMK2
- Second order Runge—Kutta—Munthe-Kaas method. -
RKMK4
- Fourth order Runge—Kutta—Munthe-Kaas method. -
LieRK4
- Fourth order Lie Runge-Kutta method. -
CG2
- Second order Crouch—Grossman method. -
MagnusAdapt4
- Fourth Order Adaptive Magnus method.
Example:
function update_func(A, u, p, t)
A[1, 1] = 0
A[2, 1] = sin(u[1])
A[1, 2] = -1
A[2, 2] = 0
end
A = DiffEqArrayOperator(ones(2, 2), update_func = update_func)
prob = ODEProblem(A, ones(2), (0, 30.0))
sol = solve(prob, LieRK4(), dt = 1 / 4)
retcode: Success
Interpolation: 3rd order Hermite
t: 121-element Vector{Float64}:
0.0
0.25
0.5
0.75
1.0
1.25
1.5
1.75
2.0
2.25
⋮
28.0
28.25
28.5
28.75
29.0
29.25
29.5
29.75
30.0
u: 121-element Vector{Vector{Float64}}:
[1.0, 1.0]
[0.7273968579551914, 1.165794872747704]
[0.4243104556165744, 1.2459210126860043]
[0.10953160297897346, 1.2654872693089263]
[-0.2070137554757648, 1.2681541526375768]
[-0.5272224527692214, 1.3028146975278558]
[-0.8648991354908154, 1.4149945285244698]
[-1.244399456687535, 1.642767157085863]
[-1.6975604074674677, 2.0038463754183535]
[-2.2537711317460407, 2.449176910606262]
⋮
[-2.6663888753291105, 2.6954837342889535]
[-3.3611719087644736, 2.7811209839661157]
[-4.008187561635523, 2.2967303664591197]
[-4.470316530373341, 1.3459658518794193]
[-4.66626299411022, 0.2081263142861722]
[-4.57289678174422, -0.9494989022112654]
[-4.200152270252206, -1.99585038589234]
[-3.6052140155232593, -2.6767378307341736]
[-2.9109618325260116, -2.781287635056373]
The above example solves a non-stiff Non-Autonomous Linear ODE with a state dependent operator, using the LieRK4
method. Similarly, a stiff Non-Autonomous Linear ODE with state dependent operators can be solved using specialized adaptive algorithms, like MagnusAdapt4
.
Example:
function update_func(A, u, p, t)
A[1, 1] = 0
A[2, 1] = 1
A[1, 2] = -2 * (1 - cos(u[2]) - u[2] * sin(u[2]))
A[2, 2] = 0
end
A = DiffEqArrayOperator(ones(2, 2), update_func = update_func)
prob = ODEProblem(A, ones(2), (30, 150.0))
sol = solve(prob, MagnusAdapt4())
retcode: Success
Interpolation: 3rd order Hermite
t: 592-element Vector{Float64}:
30.0
30.051554410975918
30.14327232699745
30.247524724052145
30.375696909344892
30.510855783890896
30.65090569337366
30.788186122035587
30.91952329587291
31.04078864675185
⋮
147.96273686915632
148.1386523776747
148.34620600715417
148.59559211110502
148.8847559565441
149.1635807149984
149.44955972217943
149.73645683847397
150.0
u: 592-element Vector{Vector{Float64}}:
[1.0, 1.0]
[1.0418828143197263, 1.0526121015605732]
[1.1297712835913454, 1.1520649699813994]
[1.2527691803606753, 1.276033498183453]
[1.4404455264543194, 1.4481983217458168]
[1.6784196223822325, 1.6585827542112963]
[1.941152341880844, 1.9121745307485782]
[2.1365107433203687, 2.1934881055190636]
[2.1250337632314107, 2.476421374656319]
[1.7927262923353107, 2.7176941957849485]
⋮
[1.3382785710099845, -1.3478235487981378]
[1.1224319158310865, -1.132482377423453]
[0.9547031935838253, -0.918358952842659]
[0.8437842085519446, -0.6957609582428903]
[0.7910831565788868, -0.4608017890713062]
[0.7783134037066098, -0.24250843704692368]
[0.7772133962093504, -0.020168014665712365]
[0.7777533743732511, 0.20284549420577422]
[0.7858794742201944, 0.4085646081595665]