Skip to content

Commit 6d157c1

Browse files
committed
Add example of eigenfunction of Koopman operator
1 parent 8cd8f96 commit 6d157c1

File tree

1 file changed

+89
-0
lines changed

1 file changed

+89
-0
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# # Eigenfunctions of the Koopman operator
2+
3+
#md # [![](https://mybinder.org/badge_logo.svg)](@__BINDER_ROOT_URL__/generated/Systems and Control/koopman_eigenfunctions.ipynb)
4+
#md # [![](https://img.shields.io/badge/show-nbviewer-579ACA.svg)](@__NBVIEWER_ROOT_URL__/generated/Systems and Control/koopman_eigenfunctions.ipynb)
5+
# **Adapted from**: Example 2.6 of [MAI20]
6+
#
7+
# [MAI20] Mauroy, Alexandre, Aivar Sootla, and Igor Mezić.
8+
# *Koopman framework for global stability analysis.*
9+
# The Koopman Operator in Systems and Control: Concepts, Methodologies, and Applications (2020): 35-58.
10+
11+
using Test #src
12+
using DynamicPolynomials
13+
@polyvar x[1:2]
14+
15+
a = 1
16+
I = 0.05
17+
ε = 0.08
18+
γ = 1
19+
F0 = [-x[2] - x[1] * (x[1] - 1) * (x[1] - a) + I, ε * (x[1] - γ * x[2])]
20+
21+
# We move equilibrium `(0.0256, 0.0256)` to the origin
22+
23+
x1 = x2 = 0.0256
24+
F = [f(x => [x[1] + x1, x[2] + x2]) for f in F0]
25+
26+
# We compute the Jacobian at the equilibrium
27+
28+
J = [j(x => zeros(2)) for j in differentiate(F, x)]
29+
30+
# We see that its eigenvalues indeed have negative real part:
31+
using LinearAlgebra
32+
E = eigen(J)
33+
34+
# We set `w` as its dominant eigenvector:
35+
36+
λ = E.values[end]
37+
w = E.vectors[:, end]
38+
39+
using SumOfSquares
40+
r = 0.3
41+
X = @set x[1]^2 + x[2]^2 r^2
42+
43+
import CSDP
44+
model = SOSModel(CSDP.Optimizer)
45+
@variable(model, γ)
46+
@objective(model, Min, γ)
47+
@variable(model, ϕN, Poly(monomials(x, 2:10)))
48+
ϕ = w x + ϕN
49+
∇ϕ = differentiate(ϕ, x)
50+
@constraint(model, -γ F ∇ϕ - λ * ϕ, domain = X)
51+
@constraint(model, F ∇ϕ - λ * ϕ γ, domain = X)
52+
optimize!(model)
53+
solution_summary(model)
54+
55+
ϕ_opt = value(ϕ)
56+
57+
using Plots
58+
x1s = x2s = range(-0.3, stop = 0.3, length = 40)
59+
ϕs = ϕ_opt.(x1s', x2s)
60+
contour(x1s, x2s, ϕs, levels=[-0.2, -0.1, 0, 0.1, 0.2, 0.5, 0.7])
61+
θ = range(0, stop = 2π, length = 100)
62+
plot!(r * cos.(θ), r * sin.(θ), label = "")
63+
scatter!([0], [0], label = "")
64+
65+
# We can compute the Laplace average as follows:
66+
67+
using DifferentialEquations
68+
function S(t, x1, x2, solver = DifferentialEquations.Tsit5())
69+
tspan = (0.0, t)
70+
prob = DifferentialEquations.ODEProblem((v, p, t) -> [f(x => v) for f in F], [x1, x2], tspan)
71+
traj = DifferentialEquations.solve(prob, solver, reltol=1e-4, abstol=1e-4)
72+
return traj[end]
73+
end
74+
75+
using QuadGK
76+
function laplace_average(f, λ, x1, x2, T = 10, args...)
77+
v, _ = quadgk(0, T, rtol=1e-3) do t
78+
s = S(t, x1, x2, args...)
79+
return f(S(t, x1, x2, args...)) * exp(-λ * t)
80+
end
81+
return v / T
82+
end
83+
84+
lap(x1, x2) = laplace_average(v -> ϕ_opt(x => v), λ, x1, x2, 10)
85+
laplace = lap.(x1s', x2s)
86+
87+
# The error is given by:
88+
89+
norm(laplace - ϕs)

0 commit comments

Comments
 (0)