Nonlinear Time Continuous System

Similarly, we can use the Extended Dynamic Mode Decomposition via a nonlinear Basis of observables. Here, we will look at a rather famous example with a finite dimensional solution.

using DataDrivenDiffEq
using LinearAlgebra
using ModelingToolkit
using OrdinaryDiffEq
using Plots

function slow_manifold(du, u, p, t)
    du[1] = p[1] * u[1]
    du[2] = p[2] * (u[2] - u[1]^2)
end

u0 = [3.0; -2.0]
tspan = (0.0, 5.0)
p = [-0.8; -0.7]

problem = ODEProblem(slow_manifold, u0, tspan, p)
solution = solve(problem, Tsit5(), saveat = 0.01);
plot(solution)

Since we are dealing with a continuous system in time, we define the associated DataDrivenProblem accordingly using the measured states X, their derivatives DX and the time t.

prob = ContinuousDataDrivenProblem(solution)
Continuous DataDrivenProblem{Float64} ##DDProblem#485 in 2 dimensions and 501 samples

Additionally, we need to define the Basis for our lifting, before we solve the problem in the lifted space.

@parameters t
@variables u[1:2](t)
Ψ = Basis([u; u[1]^2], u, independent_variable = t)
res = solve(prob, Ψ, DMDPINV(), digits = 1)
system = result(res)
Linear Solution with 2 equations and 3 parameters.
Returncode: solved
L₂ Norm error : [0.0, 1.0114581578656041e-29]
AIC : [Inf, 73132.14114913782]
R² : [1.0, 1.0]

Model ##Koopman#494 with 2 equations
States : u[1](t) u[2](t)
Parameters : p₁ p₂ p₃
Independent variable: t
Equations
Differential(t)(u[1](t)) = p₁*u[1](t)
Differential(t)(u[2](t)) = p₂*u[2](t) + p₃*(u[1](t)^2)
[-0.8, -0.7, 0.7]

The underlying dynamics have been recovered correctly by the algorithm! Similarly we could use sparse identification to solve the problem

sparse_res = solve(prob, Ψ, STLSQ(), digits = 1)
println(sparse_res)
Linear Solution with 2 equations and 3 parameters.
Returncode: solved
L₂ Norm error : [0.0, 1.0114581578656041e-29]
AIC : [Inf, 73132.14114913782]
R² : [1.0, 1.0]

And the resulting system

sparse_system = result(sparse_res)
println(sparse_system)
Model ##Basis#497 with 2 equations
States : u[1](t) u[2](t)
Parameters : p₁ p₂ p₃
Independent variable: t
Equations
Differential(t)(u[1](t)) = p₁*u[1](t)
Differential(t)(u[2](t)) = p₂*u[2](t) + p₃*(u[1](t)^2)

We can also directly look at the parameters of each result

parameter_map(res)
3-element Vector{Pair{SymbolicUtils.Sym{Real, Base.ImmutableDict{DataType, Any}}, Float64}}:
 p₁ => -0.8
 p₂ => -0.7
 p₃ => 0.7

Note that we are using parameter_map instead of just parameters, which returns a vector suitable to use with ModelingToolkit.

parameter_map(sparse_res)
3-element Vector{Pair{SymbolicUtils.Sym{Real, Base.ImmutableDict{DataType, Any}}, Float64}}:
 p₁ => -0.8
 p₂ => -0.7
 p₃ => 0.7

To simulate the system, we create an ODESystem from the result

Both results can be converted into an ODESystem

@named sys = ODESystem(
    equations(sparse_system),
    get_iv(sparse_system),
    states(sparse_system),
    parameters(sparse_system)
    );


x0 = [u[1] => u0[1], u[2] => u0[2]]
ps = parameter_map(sparse_res)
3-element Vector{Pair{SymbolicUtils.Sym{Real, Base.ImmutableDict{DataType, Any}}, Float64}}:
 p₁ => -0.8
 p₂ => -0.7
 p₃ => 0.7

And simulated using OrdinaryDiffEq.jl using the (known) initial conditions and the parameter mapping of the estimation.

ode_prob = ODEProblem(sys, x0, tspan, ps)
estimate = solve(ode_prob, Tsit5(), saveat = prob.t);

And look at the result

plot(solution, color = :black)
plot!(estimate, color = :red, linestyle = :dash)

Copy-Pasteable Code

using DataDrivenDiffEq
using LinearAlgebra
using ModelingToolkit
using OrdinaryDiffEq

function slow_manifold(du, u, p, t)
    du[1] = p[1] * u[1]
    du[2] = p[2] * (u[2] - u[1]^2)
end

u0 = [3.0; -2.0]
tspan = (0.0, 5.0)
p = [-0.8; -0.7]

problem = ODEProblem(slow_manifold, u0, tspan, p)
solution = solve(problem, Tsit5(), saveat = 0.01);

prob = ContinuousDataDrivenProblem(solution)

@parameters t
@variables u[1:2](t)
Ψ = Basis([u; u[1]^2], u, independent_variable = t)
res = solve(prob, Ψ, DMDPINV(), digits = 1)
system = result(res)

sparse_res = solve(prob, Ψ, STLSQ(), digits = 1)

sparse_system = result(sparse_res)

parameter_map(res)

parameter_map(sparse_res)

@named sys = ODESystem(
    equations(sparse_system),
    get_iv(sparse_system),
    states(sparse_system),
    parameters(sparse_system)
    );


x0 = [u[1] => u0[1], u[2] => u0[2]]
ps = parameter_map(sparse_res)

ode_prob = ODEProblem(sys, x0, tspan, ps)
estimate = solve(ode_prob, Tsit5(), saveat = prob.t);

# This file was generated using Literate.jl, https://github.com/fredrikekre/Literate.jl

This page was generated using Literate.jl.