Skip to content

Commit 7483f0d

Browse files
authored
Merge pull request #140 from SciML/sindy_api
SINDY Signature and rename
2 parents 8b323c3 + d4f3eb6 commit 7483f0d

File tree

16 files changed

+118
-106
lines changed

16 files changed

+118
-106
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ end
6969
basis = Basis(polys, u)
7070

7171
opt = STRRidge(0.1)
72-
Ψ = SInDy(X, DX, basis, maxiter = 100, opt = opt, normalize = true)
72+
Ψ = SINDy(X, DX, basis, opt, maxiter = 100, normalize = true)
7373
print_equations(Ψ)
7474
get_error(Ψ)
7575
```

docs/src/quickstart.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,9 @@ Looking at the eigenvalues of the system, we see that the estimated eigenvalues
191191

192192
## Nonlinear Systems - Sparse Identification of Nonlinear Dynamics
193193

194-
Okay, so far we can fit linear models via DMD and nonlinear models via EDMD. But what if we want to find a model of a nonlinear system *without moving to Koopman space*? Simple, we use [Sparse Identification of Nonlinear Dynamics](https://www.pnas.org/content/113/15/3932) or `SInDy`.
194+
Okay, so far we can fit linear models via DMD and nonlinear models via EDMD. But what if we want to find a model of a nonlinear system *without moving to Koopman space*? Simple, we use [Sparse Identification of Nonlinear Dynamics](https://www.pnas.org/content/113/15/3932) or `SINDy`.
195195

196-
As the name suggests, `SInDy` finds the sparsest basis of functions which build the observed trajectory. Again, we will start with a nonlinear system
196+
As the name suggests, `SINDy` finds the sparsest basis of functions which build the observed trajectory. Again, we will start with a nonlinear system
197197

198198
```@example 3
199199
using DataDrivenDiffEq
@@ -224,7 +224,7 @@ savefig("nonlinear_pendulum.png") # hide
224224

225225
which is the simple nonlinear pendulum with damping.
226226

227-
Suppose we are like John and know nothing about the system, we have just the data in front of us. To apply `SInDy`, we need three ingredients:
227+
Suppose we are like John and know nothing about the system, we have just the data in front of us. To apply `SINDy`, we need three ingredients:
228228

229229
+ A `Basis` containing all possible candidate functions which might be in the model
230230
+ An optimizer which is able to produce a sparse output
@@ -248,11 +248,11 @@ nothing # hide
248248

249249
```@example 3
250250
opt = SR3(3e-1, 1.0)
251-
Ψ = SInDy(X[:, 1:1000], DX[:, 1:1000], basis, maxiter = 10000, opt = opt, normalize = true)
251+
Ψ = SINDy(X[:, 1:1000], DX[:, 1:1000], basis, opt, maxiter = 10000, normalize = true)
252252
print_equations(Ψ) # hide
253253
```
254254

255-
We recovered the equations! Let's transform the `SInDyResult` into a performant piece of
255+
We recovered the equations! Let's transform the `SINDyResult` into a performant piece of
256256
Julia Code using `ODESystem`
257257

258258
```@example 3
@@ -267,6 +267,6 @@ estimation = solve(estimator, Tsit5(), saveat = solution.t)
267267
plot(solution.t[1:1000], solution[:,1:1000]', color = :red, line = :dot, label = nothing) # hide
268268
plot!(solution.t[1000:end], solution[:,1000:end]', color = :blue, line = :dot,label = nothing) # hide
269269
plot!(estimation, color = :green, label = "Estimation") # hide
270-
savefig("sindy_estimation.png") # hide
270+
savefig("SINDy_estimation.png") # hide
271271
```
272-
![](sindy_estimation.png)
272+
![](SINDy_estimation.png)

docs/src/sparse_identification/isindy.md

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Implicit Sparse Identification of Nonlinear Dynamics
22

3-
While `SInDy` works well for ODEs, some systems take the form of [DAE](https://diffeq.sciml.ai/stable/types/dae_types/)s. A common form is `f(x, p, t) - g(x, p, t)*dx = 0`. These can be inferred via `ISInDy`, which extends `SInDy` [for Implicit problems](https://ieeexplore.ieee.org/abstract/document/7809160). In particular, it solves
3+
While `SINDy` works well for ODEs, some systems take the form of [DAE](https://diffeq.sciml.ai/stable/types/dae_types/)s. A common form is `f(x, p, t) - g(x, p, t)*dx = 0`. These can be inferred via `ISINDy`, which extends `SINDy` [for Implicit problems](https://ieeexplore.ieee.org/abstract/document/7809160). In particular, it solves
44

55
```math
66
\Xi = \min ~ \left\lVert \Theta(X, p, t)^{T} \Xi \right\rVert_{2} + \lambda ~ \left\lVert \Xi \right\rVert_{1}
@@ -13,7 +13,7 @@ where ``\Xi`` lies in the nullspace of ``\Theta``.
1313
Let's try to infer the [Michaelis-Menten Kinetics](https://en.wikipedia.org/wiki/Michaelis%E2%80%93Menten_kinetics), like in the corresponding paper. We start by generating the
1414
corresponding data.
1515

16-
```@example isindy_1
16+
```@example iSINDy_1
1717
using DataDrivenDiffEq
1818
using ModelingToolkit
1919
using OrdinaryDiffEq
@@ -32,11 +32,11 @@ problem = ODEProblem(michaelis_menten, u0, tspan)
3232
solution = solve(problem, Tsit5(), saveat = 0.1, atol = 1e-7, rtol = 1e-7)
3333
3434
plot(solution) # hide
35-
savefig("isindy_example.png")
35+
savefig("iSINDy_example.png")
3636
```
37-
![](isindy_example.png)
37+
![](iSINDy_example.png)
3838

39-
```@example isindy_1
39+
```@example iSINDy_1
4040
X = solution[:,:]
4141
DX = similar(X)
4242
for (i, xi) in enumerate(eachcol(X))
@@ -47,24 +47,24 @@ end
4747
basis= Basis([u^i for i in 0:4], [u])
4848
```
4949

50-
The signature of `ISInDy` is equal to `SInDy`, but requires an `AbstractSubspaceOptimizer`. Currently, `DataDrivenDiffEq` just implements `ADM()` based on [alternating directions](https://arxiv.org/pdf/1412.4659.pdf). `rtol` gets passed into the derivation of the `nullspace` via `LinearAlgebra`.
50+
The signature of `ISINDy` is equal to `SINDy`, but requires an `AbstractSubspaceOptimizer`. Currently, `DataDrivenDiffEq` just implements `ADM()` based on [alternating directions](https://arxiv.org/pdf/1412.4659.pdf). `rtol` gets passed into the derivation of the `nullspace` via `LinearAlgebra`.
5151

5252

53-
```@example isindy_1
53+
```@example iSINDy_1
5454
opt = ADM(1.1e-1)
5555
```
5656

57-
Since `ADM()` returns sparsified columns of the nullspace we need to find a pareto optimal solution. To achieve this, we provide a sufficient cost function `g` to `ISInDy`. This allows us to evaluate each individual column of the sparse matrix on its 0-norm (sparsity) and the 2-norm of the matrix vector product of ``\Theta^T \xi`` (nullspace). This is a default setting which can be changed by providing a function `f` which maps the coefficients and the library onto a feature space. Here, we want to set the focus on the the magnitude of the deviation from the nullspace.
57+
Since `ADM()` returns sparsified columns of the nullspace we need to find a pareto optimal solution. To achieve this, we provide a sufficient cost function `g` to `ISINDy`. This allows us to evaluate each individual column of the sparse matrix on its 0-norm (sparsity) and the 2-norm of the matrix vector product of ``\Theta^T \xi`` (nullspace). This is a default setting which can be changed by providing a function `f` which maps the coefficients and the library onto a feature space. Here, we want to set the focus on the the magnitude of the deviation from the nullspace.
5858

59-
```@example isindy_1
60-
Ψ = ISInDy(X, DX, basis, opt, g = x->norm([1e-1*x[1]; x[2]]), maxiter = 1000, rtol = 0.99)
59+
```@example iSINDy_1
60+
Ψ = ISINDy(X, DX, basis, opt, g = x->norm([1e-1*x[1]; x[2]]), maxiter = 1000, rtol = 0.99)
6161
nothing #hide
6262
```
6363

6464
The function call returns a `SparseIdentificationResult`.
6565
As in [Sparse Identification of Nonlinear Dynamics](@ref), we can transform the `SparseIdentificationResult` into an `ODESystem`.
6666

67-
```@example isindy_1
67+
```@example iSINDy_1
6868
# Transform into ODE System
6969
sys = ODESystem(Ψ)
7070
dudt = ODEFunction(sys)
@@ -75,13 +75,13 @@ estimation = solve(estimator, Tsit5(), saveat = 0.1)
7575
7676
plot(solution, color = :red, label = "True") # hide
7777
plot!(estimation, color = :green, label = "Estimation") # hide
78-
savefig("isindy_example_final.png") # hide
78+
savefig("iSINDy_example_final.png") # hide
7979
```
80-
![](isindy_example_final.png)
80+
![](iSINDy_example_final.png)
8181

82-
The model recovered by `ISInDy` is correct
82+
The model recovered by `ISINDy` is correct
8383

84-
```@example isindy_1
84+
```@example iSINDy_1
8585
print_equations(Ψ)
8686
```
8787

@@ -92,7 +92,7 @@ The parameters are off a little, but, as before, we can use `DiffEqFlux` to tune
9292

9393
Implicit dynamics can also be reformulated as an explicit problem as stated in [this paper](https://arxiv.org/pdf/2004.02322.pdf). The algorithm searches the correct equations by trying out all candidate functions as a right hand side and performing a sparse regression onto the remaining set of candidates. Let's start by defining the problem and generate the data:
9494

95-
```@example isindy_2
95+
```@example iSINDy_2
9696
9797
using DataDrivenDiffEq
9898
using ModelingToolkit
@@ -125,15 +125,15 @@ for (i, xi) in enumerate(eachcol(X))
125125
end
126126
127127
plot(solution) # hide
128-
savefig("isindy_cartpole_data.png") # hide
128+
savefig("iSINDy_cartpole_data.png") # hide
129129
130130
```
131-
![](isindy_cartpole_data.png)
131+
![](iSINDy_cartpole_data.png)
132132

133133
We see that we include a forcing term `F` inside the model which is depending on `t`.
134134
As before, we will also need a `Basis` to derive our equations from:
135135

136-
```@example isindy_2
136+
```@example iSINDy_2
137137
@variables u[1:4] t
138138
polys = Operation[]
139139
for i ∈ 0:4
@@ -164,12 +164,12 @@ We added the time dependent input directly into the basis to account for its inf
164164

165165
*NOTE : Including input signals may change in future releases!*
166166

167-
Like for a `SInDy`, we can use any `AbstractOptimizer` with a pareto front optimization over different thresholds.
167+
Like for a `SINDy`, we can use any `AbstractOptimizer` with a pareto front optimization over different thresholds.
168168

169-
```@example isindy_2
169+
```@example iSINDy_2
170170
λ = exp10.(-4:0.1:-1)
171171
g(x) = norm([1e-3; 10.0] .* x, 2)
172-
Ψ = ISInDy(X[:,:], DX[:, :], basis, λ, STRRidge(), maxiter = 100, normalize = false, t = solution.t, g = g)
172+
Ψ = ISINDy(X[:,:], DX[:, :], basis, λ, STRRidge(), maxiter = 100, normalize = false, t = solution.t, g = g)
173173
174174
# Transform into ODE System
175175
sys = ODESystem(Ψ)
@@ -182,13 +182,13 @@ sol_ = solve(estimator, Tsit5(), saveat = dt)
182182
183183
plot(solution.t[:], solution[:,:]', color = :red, label = nothing) # hide
184184
plot!(sol_.t, sol_[:, :]', color = :green, label = "Estimation") # hide
185-
savefig("isindy_cartpole_estimation.png") # hide
185+
savefig("iSINDy_cartpole_estimation.png") # hide
186186
```
187-
![](isindy_cartpole_estimation.png)
187+
![](iSINDy_cartpole_estimation.png)
188188

189189
Let's have a look at the equations recovered. They nearly match up.
190190

191-
```@example isindy_2
191+
```@example iSINDy_2
192192
print_equations(Ψ)
193193
```
194194

@@ -198,5 +198,5 @@ print_equations(Ψ)
198198
## Functions
199199

200200
```@docs
201-
ISInDy
201+
ISINDy
202202
```

docs/src/sparse_identification/optimizers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ mutable struct MyOpt <: DataDrivenDiffEq.Optimize.AbstractOptimizer
2424
end
2525
```
2626

27-
To use `MyOpt` within `SInDy`, an `init!` function has to be implemented.
27+
To use `MyOpt` within `SINDy`, an `init!` function has to be implemented.
2828

2929
```julia
3030
function init!(X::AbstractArray, o::MyOpt, A::AbstractArray, Y::AbstractArray)

docs/src/sparse_identification/sindy.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ where, in most cases, ``Y``is the data matrix containing the derivatives of the
1515
As in the original paper, we will estimate the [Lorenz System](https://en.wikipedia.org/wiki/Lorenz_system).
1616
First, let's create the necessary data and have a look at the trajectory.
1717

18-
```@example sindy_1
18+
```@example SINDy_1
1919
using DataDrivenDiffEq
2020
using ModelingToolkit
2121
using OrdinaryDiffEq
@@ -47,7 +47,7 @@ savefig("lorenz.png") #hide
4747

4848
Additionally, we generate the *ideal* derivative data.
4949

50-
```@example sindy_1
50+
```@example SINDy_1
5151
X = Array(solution)
5252
DX = similar(X)
5353
for (i, xi) in enumerate(eachcol(X))
@@ -57,7 +57,7 @@ end
5757

5858
To generate the symbolic equations, we need to define a ` Basis` over the variables `x y z`. In this example, we will use all monomials up to degree of 4 and their products:
5959

60-
```@example sindy_1
60+
```@example SINDy_1
6161
@variables x y z
6262
u = Operation[x; y; z]
6363
polys = Operation[]
@@ -79,25 +79,25 @@ nothing #hide
7979

8080
To perform the sparse identification on our data, we need to define an `Optimizer`. Here, we will use `STRRidge`, which is described in the original paper. The threshold of the optimizer is set to `0.1`. An overview of the different optimizers can be found below.
8181

82-
```@example sindy_1
82+
```@example SINDy_1
8383
opt = STRRidge(0.1)
84-
Ψ = SInDy(X, DX, basis, maxiter = 100, opt = opt, normalize = true)
84+
Ψ = SINDy(X, DX, basis, opt, maxiter = 100, normalize = true)
8585
```
8686

87-
`Ψ` is a `SInDyResult`, which stores some about the regression. As we can see, we have 7 active terms inside the model.
87+
`Ψ` is a `SINDyResult`, which stores some about the regression. As we can see, we have 7 active terms inside the model.
8888
To look at the equations, simply type
8989

90-
```@example sindy_1
90+
```@example SINDy_1
9191
print_equations(Ψ)
9292
```
9393

9494
First, let's have a look at the ``L2``-Error and Akaikes Information Criterion of the result
9595

96-
```@example sindy_1
96+
```@example SINDy_1
9797
get_error(Ψ)
9898
```
9999

100-
```@example sindy_1
100+
```@example SINDy_1
101101
get_aicc(Ψ)
102102
```
103103

@@ -106,7 +106,7 @@ We can also access the coefficient matrix ``\Xi`` directly via `get_coefficients
106106
To generate a numerical model usable in `DifferentialEquations`, we simply use the `ODESystem` function from `ModelingToolkit`.
107107
The resulting parameters used for the identification can be accessed via `parameters(Ψ)`. The returned vector also includes the parameters of the original `Basis` used to generate the result.
108108

109-
```@example sindy_1
109+
```@example SINDy_1
110110
ps = parameters(Ψ)
111111
sys = ODESystem(Ψ)
112112
dudt = ODEFunction(sys)
@@ -137,7 +137,7 @@ which resembles the papers results. Next, we could use [classical parameter esti
137137
## Functions
138138

139139
```@docs
140-
SInDy
140+
SINDy
141141
sparse_regression
142142
```
143143

examples/Havok_Examples.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ plot(plot(z[1, :]),plot(z[end, :].^2), layout = (2, 1))
6060

6161
basis = Basis(u, u)
6262
opt = SR3(1e-1)
63-
b = SInDy(z[:, 1:end], dz[1:end-1, 1:end], basis, maxiter = 1000, opt = opt, normalize = true)
63+
b = SINDy(z[:, 1:end], dz[1:end-1, 1:end], basis, opt, maxiter = 1000, normalize = true)
6464

6565
# Coincides with the paper results
6666
println(b)

examples/ISInDy_Examples.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,15 @@ for i ∈ 0:6
4040
end
4141

4242
basis= Basis(polys, u)
43-
Ψ = ISInDy(X, DX, basis, ADM(1e-2), maxiter = 10, rtol = 0.1)
43+
Ψ = ISINDy(X, DX, basis, ADM(1e-2), maxiter = 10, rtol = 0.1)
4444
println(Ψ)
4545
print_equations(Ψ)
4646

47-
# Lets use sindy pi for a parallel implicit implementation
47+
# Lets use SINDy pi for a parallel implicit implementation
4848
# Create a basis
4949
@variables u[1:3]
5050
polys = Operation[]
51-
# Lots of basis functions -> sindy pi can handle more than ADM()
51+
# Lots of basis functions -> SINDy pi can handle more than ADM()
5252
for i 0:10
5353
if i == 0
5454
push!(polys, u[1]^0)
@@ -62,8 +62,8 @@ end
6262

6363
basis= Basis(polys, u)
6464

65-
# Simply use any optimizer you would use for sindy
66-
Ψ = ISInDy(X[:, :], DX[:, :], basis, STRRidge(1e-1), maxiter = 100, normalize = true)
65+
# Simply use any optimizer you would use for SINDy
66+
Ψ = ISINDy(X[:, :], DX[:, :], basis, STRRidge(1e-1), maxiter = 100, normalize = true)
6767

6868

6969
# Transform into ODE System

examples/ISInDy_Examples2.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ basis= Basis(polys, u, iv = t)
6363
# Simply use any optimizer you would use for sindy
6464
λ = exp10.(-4:0.1:-1)
6565
g(x) = norm([1e-3; 10.0] .* x, 2)
66-
Ψ = ISInDy(X[:,:], DX[:, :], basis, λ, STRRidge(), maxiter = 100, normalize = false, t = solution.t, g = g)
66+
Ψ = ISINDy(X[:,:], DX[:, :], basis, λ, STRRidge(), maxiter = 100, normalize = false, t = solution.t, g = g)
6767
println(Ψ)
6868
print_equations(Ψ, show_parameter = true)
6969

examples/SInDy_Examples.jl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,13 @@ println(basis)
5050
# than assumptions, converges fast but fails sometimes with too much noise
5151
opt = STRRidge(1e-2)
5252
# Enforce all 100 iterations
53-
Ψ = SInDy(sol[:,1:25], DX[:, 1:25], basis, p = [1.0; 1.0], maxiter = 100, opt = opt, convergence_error = 1e-5)
53+
Ψ = SINDy(sol[:,1:25], DX[:, 1:25], basis, opt, p = [1.0; 1.0], maxiter = 100, convergence_error = 1e-5)
5454
println(Ψ)
55+
print_equations(Ψ)
5556

5657
# Lasso as ADMM, typically needs more information, more tuning
5758
opt = ADMM(1e-2, 1.0)
58-
Ψ = SInDy(sol[:,1:50], DX[:, 1:50], basis, p = [1.0; 1.0], maxiter = 5000, opt = opt, convergence_error = 1e-3)
59+
Ψ = SINDy(sol[:,1:50], DX[:, 1:50], basis, opt, p = [1.0; 1.0], maxiter = 5000, convergence_error = 1e-3)
5960
println(Ψ)
6061
print_equations(Ψ)
6162

@@ -64,7 +65,7 @@ parameters(Ψ)
6465

6566
# SR3, works good with lesser data and tuning
6667
opt = SR3(1e-2, 1.0)
67-
Ψ = SInDy(sol[:,1:end], DX[:, 1:end], basis, p = [0.5; 0.5], maxiter = 5000, opt = opt, convergence_error = 1e-5)
68+
Ψ = SINDy(sol[:,1:end], DX[:, 1:end], basis, opt, p = [0.5; 0.5], maxiter = 5000, convergence_error = 1e-5)
6869
println(Ψ)
6970
print_equations(Ψ, show_parameter = true)
7071

@@ -73,8 +74,9 @@ print_equations(Ψ, show_parameter = true)
7374
λs = exp10.(-5:0.1:-1)
7475
# Use SR3 with high relaxation (allows the solution to diverge from LTSQ) and high iterations
7576
opt = SR3(1e-2, 5.0)
76-
Ψ = SInDy(sol[:,1:10], DX[:, 1:10], basis, λs, p = [1.0; 1.0], maxiter = 15000, opt = opt)
77+
Ψ = SINDy(sol[:,1:10], DX[:, 1:10], basis, λs, opt, p = [1.0; 1.0], maxiter = 15000)
7778
println(Ψ)
79+
print_equations(Ψ)
7880

7981
# Transform into ODE System
8082
sys = ODESystem(Ψ)

examples/SInDy_Examples2.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ basis = Basis(h, u)
7171

7272
# Get the reduced basis via the sparse regression
7373
opt = STRRidge(0.1)
74-
Ψ = SInDy(X_cropped, DX_sg, basis, maxiter = 100, opt = opt, normalize = false)
74+
Ψ = SINDy(X_cropped, DX_sg, basis, opt, maxiter = 100, normalize = false)
7575
print(Ψ)
7676
print_equations(Ψ)
7777
print_equations(Ψ, show_parameter = true)
@@ -84,5 +84,5 @@ X_noisy = X + 0.01*randn(seed,size(X))
8484
X_noisy_cropped, DX_sg = savitzky_golay(X_noisy, windowSize, polyOrder, deriv=1, dt=dt)
8585
X_noisy_cropped, X_smoothed = savitzky_golay(X_noisy, windowSize, polyOrder, deriv=0, dt=dt)
8686

87-
Ψ = SInDy(X_smoothed, DX_sg, basis, maxiter = 100, opt = opt, normalize = false)
88-
print_equations(Ψ)
87+
Ψ = SINDy(X_smoothed, DX_sg, basis, opt, maxiter = 100, normalize = false)
88+
print_equations, show_parameter = true)

0 commit comments

Comments
 (0)