Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/src/libs/datadrivendmd/koopman.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ A similar result holds for time continuous systems in the form of the Koopman ge

## [Algorithms](@id koopman_algorithms)

The abstract algorithm below is a developer interface for extending the Koopman
solver. Users should generally select one of the concrete algorithms.

```@docs
DataDrivenDMD.AbstractKoopmanAlgorithm
DMDPINV
DMDSVD
TOTALDMD
Expand Down
14 changes: 14 additions & 0 deletions docs/src/libs/datadrivenlux/dag_sr.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@
DataDrivenLux provides differentiable directed-acyclic-graph structure search
for discovering governing equations.

## Developer API

`AbstractDAGSRAlgorithm` is the extension interface for implementing another
search algorithm. Application code should use the concrete algorithms below.

```@docs
DataDrivenLux.AbstractDAGSRAlgorithm
DataDrivenLux.CommonAlgOptions
DataDrivenLux.init_model
DataDrivenLux.init_cache
DataDrivenLux.update_parameters!
DataDrivenLux.convert_to_basis
```

## Error Models

```@docs
Expand Down
15 changes: 15 additions & 0 deletions docs/src/libs/datadrivensparse/sparse_regression.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,18 @@ Where the matrix of evaluated basis elements $\varPhi_y \in \mathbb R^{\lvert \v

## [Algorithms](@id sparse_algorithms)

The abstract algorithm and proximal operator entries below are developer
interfaces for extending `DataDrivenSparse`. Application code should generally
use the concrete algorithms and operators.

```@docs
DataDrivenSparse.AbstractSparseRegressionAlgorithm
DataDrivenSparse.AbstractSparseRegressionCache
DataDrivenSparse.get_thresholds
DataDrivenSparse.get_relaxation
DataDrivenSparse.get_proximal
DataDrivenSparse.init_cache
DataDrivenSparse.step!
STLSQ
ADMM
SR3
Expand All @@ -60,7 +70,12 @@ SparseLinearSolver

## [Proximal Operators](@id proximal_operators)

Custom proximal operators should subtype `AbstractProximalOperator` and
implement the documented callable and active-set methods.

```@docs
DataDrivenSparse.AbstractProximalOperator
DataDrivenSparse.active_set!
SoftThreshold
HardThreshold
ClippedAbsoluteDeviation
Expand Down
4 changes: 4 additions & 0 deletions docs/src/solvers/common.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ DataDrivenCommonOptions

After defining a [`problem`](@ref problem), we choose a method to [`solve`](@ref solve) it. Depending on the input arguments and the type of problem, the function will return a result derived from the algorithm of choice. Different options can be provided, depending on the inference method, for options like rounding, normalization, or the progress bar. An optional [`Basis`](@ref) can be used for lifting the measurements.

The exported `solve` name follows the generic `CommonSolve` interface, which is
documented by its owning package. The examples below describe its use with
DataDrivenDiffEq problems and algorithms.

```julia
solution = solve(DataDrivenProblem, [basis], solver; kwargs...)
```
Expand Down
6 changes: 5 additions & 1 deletion lib/DataDrivenDMD/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
StatsAPI = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
SciMLPublic = "431bcebd-1456-4ced-9d72-93c2757fff0b"

[sources]
DataDrivenDiffEq = {path = "../.."}
Expand All @@ -28,6 +29,8 @@ SafeTestsets = "0.1"
StableRNGs = "1"
Statistics = "1.10"
StatsAPI = "1"
SciMLPublic = "1"
SciMLBase = "2.155, 3"
Symbolics = "7.18.1"
Test = "1.10"
SciMLTesting = "2.10"
Expand All @@ -39,9 +42,10 @@ OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
OrdinaryDiffEqFunctionMap = "d3585ca7-f5d3-4ba6-8057-292ed1abd90f"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3"
Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Pkg", "Test", "OrdinaryDiffEq", "OrdinaryDiffEqFunctionMap", "StableRNGs", "SafeTestsets", "Symbolics"]
test = ["Pkg", "Test", "OrdinaryDiffEq", "OrdinaryDiffEqFunctionMap", "SciMLBase", "StableRNGs", "SafeTestsets", "Symbolics"]
55 changes: 55 additions & 0 deletions lib/DataDrivenDMD/src/DataDrivenDMD.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,68 @@ using DocStringExtensions: FIELDS, SIGNATURES, TYPEDEF
using Parameters: @unpack
using Statistics: mean
using StatsAPI: StatsAPI, r2
using SciMLPublic: @public

using LinearAlgebra: Diagonal, Eigen, eigen, svd

const _EMPTY_MATRIX = Matrix(undef, 0, 0)

"""
AbstractKoopmanAlgorithm

Developer interface for algorithms that estimate a Koopman operator or generator.
This interface is intended for DataDrivenDiffEq solver packages and advanced
extensions, not ordinary application code.

# Interface

A subtype must implement `alg(X, Y) -> (K, B)`, where `X` and `Y` are lifted data
matrices with one observation per column, `K` is an operator representation
convertible by `Matrix`, and `B` is the input map or an empty matrix when no
controls are used. A controlled implementation may additionally implement
`alg(X, Y, U) -> (K, B)`. The generic four-argument forms support a supplied
input map or `nothing` and are provided by this package.

To participate in the common `solve` workflow, the subtype must be usable by the
generic `DataDrivenDiffEq.get_fit_targets` and `CommonSolve.solve!` methods for
[`InternalDataDrivenProblem`](@ref). The two-argument method is required; the
three-argument method is required when the basis contains controls. The returned
`K` must represent a square operator on the lifted state space, and `B` must have
the corresponding output-by-control shape. A custom algorithm should preserve
these dimensions so that the result can be converted back to a
[`DataDrivenDiffEq.Basis`](@ref).

# Arguments

- `X::AbstractArray`: lifted input data, with features in rows and observations in
columns.
- `Y::AbstractArray`: lifted target data with the same number of columns as `X`.
- `U::AbstractArray`: optional control data with one column per observation.
- `B::AbstractArray` or `nothing`: an optional input map supplied by the common
four-argument adapter.

# Returns

Return `(K, B)`. `K` is an operator representation accepted by the result
constructor, and `B` is an input map or an empty matrix when the fit is
uncontrolled.

# Example

```julia
using LinearAlgebra

struct MyKoopman <: DataDrivenDMD.AbstractKoopmanAlgorithm end

function (::MyKoopman)(X, Y)
return eigen(Y / X), zeros(eltype(X), size(Y, 1), 0)
end
```
"""
abstract type AbstractKoopmanAlgorithm <: AbstractDataDrivenAlgorithm end

@public AbstractKoopmanAlgorithm

# Results
include("./result.jl")
export KoopmanResult
Expand Down
81 changes: 81 additions & 0 deletions lib/DataDrivenDMD/src/algorithms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,33 @@ K = Y / X

where `Y` and `X` are data matrices. Returns a `Eigen` factorization of the operator.

# Arguments

- `X::AbstractArray`: lifted state data, with one observation per column.
- `Y::AbstractArray`: lifted next-state data with the same number of columns as `X`.
- `U::AbstractArray`: optional control data for the controlled DMDc form.

# Returns

Return `(K, B)`, where `K` is an `Eigen` factorization and `B` is an empty matrix
for the uncontrolled form or the learned input map for the controlled form.

# Fields

$(FIELDS)

# Signatures

$(SIGNATURES)

# Example

```julia
X = [1.0 2.0; 2.0 4.0]
Y = [2.0 4.0; 4.0 8.0]
K, B = DMDPINV()(X, Y)
isempty(B)
```
"""
mutable struct DMDPINV <: AbstractKoopmanAlgorithm end;

Expand Down Expand Up @@ -89,13 +109,32 @@ where `Y` and `X = U*Σ*V'` are data matrices. The singular value decomposition
the `truncation` parameter, which can either be an `Int` indicating an index-based truncation or a `Real`
indicating a tolerance-based truncation. Returns a `Eigen` factorization of the operator.

# Arguments

- `X::AbstractArray`: lifted state data, with one observation per column.
- `Y::AbstractArray`: lifted next-state data with the same number of columns as `X`.
- `U::AbstractArray`: optional control data for the controlled DMDc form.
- `truncation`: an integer rank or a real-valued relative singular-value tolerance.

# Returns

Return `(K, B)`, where `K` is an `Eigen` factorization and `B` is the learned input
map or an empty matrix when controls are absent.

# Fields

$(FIELDS)

# Signatures

$(SIGNATURES)

# Example

```julia
K, B = DMDSVD(1)([1.0 2.0; 2.0 4.0], [2.0 4.0; 4.0 8.0])
size(Matrix(K)) == (1, 1)
```
"""
mutable struct DMDSVD{T} <: AbstractKoopmanAlgorithm where {T <: Number}
"""Indicates the truncation"""
Expand Down Expand Up @@ -159,13 +198,34 @@ If `rtol` ∈ (0, 1) is given, the singular value decomposition is reduced to in
entries bigger than `rtol*maximum(Σ)`. If `rtol` is an integer, the reduced SVD up to `rtol` is used
for computation.

# Arguments

- `X::AbstractArray`: lifted input data, with one observation per column.
- `Y::AbstractArray`: lifted target data with the same number of columns as `X`.
- `U::AbstractArray`: optional control data.
- `truncation`: rank or relative singular-value tolerance used for the joint SVD.
- `alg::AbstractKoopmanAlgorithm`: algorithm applied after the joint reduction.

# Returns

Return `(K, B)` from `alg` after the data are projected onto the retained singular
subspace.

# Fields

$(FIELDS)

# Signatures

$(SIGNATURES)

# Example

```julia
alg = TOTALDMD(1, DMDPINV())
K, B = alg([1.0 2.0; 2.0 4.0], [2.0 4.0; 4.0 8.0])
isempty(B)
```
"""
mutable struct TOTALDMD{R, A} <:
AbstractKoopmanAlgorithm where {R <: Number, A <: AbstractKoopmanAlgorithm}
Expand Down Expand Up @@ -204,13 +264,34 @@ It is assumed that `K = sqrt(K₁*inv(K₂))`, where `K₁` is the approximation
If `truncation` ∈ (0, 1) is given, the singular value decomposition is reduced to include only
entries bigger than `truncation*maximum(Σ)`. If `truncation` is an integer, the reduced SVD up to `truncation` is used for computation.

# Arguments

- `X::AbstractArray`: lifted input data, with one observation per column.
- `Y::AbstractArray`: lifted target data with the same number of columns as `X`.
- `U::AbstractArray`: optional control data. This form delegates to the wrapped
`DMDSVD` algorithm.
- `truncation`: rank or relative singular-value tolerance used by the wrapped
`DMDSVD` algorithm.

# Returns

Return `(K, B)`, where `K` is an `Eigen` factorization and `B` is an empty matrix
for uncontrolled data or the learned input map for controlled data.

# Fields

$(FIELDS)

# Signatures

$(SIGNATURES)

# Example

```julia
K, B = FBDMD(1)([1.0 2.0; 2.0 4.0], [2.0 4.0; 4.0 8.0])
isempty(B)
```
"""
mutable struct FBDMD{R} <: AbstractKoopmanAlgorithm where {R <: Number}
alg::DMDSVD{R}
Expand Down
10 changes: 10 additions & 0 deletions lib/DataDrivenDMD/src/result.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ Result returned by DataDrivenDMD solvers.
# Fields

$(FIELDS)

The `k`, `b`, and `c` fields represent the learned operator, input map, and
output map. `q` and `p` retain update matrices used by the online formulation;
they are developer state and should not be edited by callers. The remaining
fields implement the `StatsAPI.StatisticalModel` interface.

# Returns

The constructor returns a result whose operator and maps are compatible with
`get_operator`, `get_inputmap`, and `get_outputmap`.
"""
struct KoopmanResult{K, B, C, Q, P, T} <: AbstractDataDrivenResult
"""Matrix representation of the operator / generator"""
Expand Down
39 changes: 39 additions & 0 deletions lib/DataDrivenDMD/test/Core/interface.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using DataDrivenDMD
using LinearAlgebra: I, eigen
using Test

struct InterfaceKoopman <: DataDrivenDMD.AbstractKoopmanAlgorithm end

function (::InterfaceKoopman)(X::AbstractMatrix, Y::AbstractMatrix)
return eigen(Y / X), zeros(eltype(X), size(Y, 1), 0)
end

function (::InterfaceKoopman)(
X::AbstractMatrix, Y::AbstractMatrix, U::AbstractMatrix
)
return eigen(Y / X), zeros(eltype(X), size(Y, 1), size(U, 1))
end

@testset "Generic Koopman algorithm interface" begin
X = Matrix{Float64}(I, 2, 2)
Y = [2.0 0.0; 0.0 3.0]
U = zeros(1, 2)
B = zeros(2, 1)
algorithm = InterfaceKoopman()

K, B0 = algorithm(X, Y)
@test Matrix(K) == Y
@test isempty(B0)

K, B1 = algorithm(X, Y, U)
@test Matrix(K) == Y
@test size(B1) == (2, 1)

K, B2 = algorithm(X, Y, U, B)
@test Matrix(K) == Y
@test B2 === B

K, B3 = algorithm(X, Y, U, nothing)
@test Matrix(K) == Y
@test size(B3) == (2, 1)
end
1 change: 1 addition & 0 deletions lib/DataDrivenDMD/test/Core/nonlinear_autonomous.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ using StatsAPI: loglikelihood, r2, rss
using StableRNGs
using OrdinaryDiffEq
using OrdinaryDiffEqFunctionMap
using SciMLBase: DiscreteProblem
using Symbolics: @variables

rng = StableRNG(42)
Expand Down
1 change: 1 addition & 0 deletions lib/DataDrivenDMD/test/Core/nonlinear_forced.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ using StatsAPI: dof, r2
using StableRNGs
using OrdinaryDiffEq
using OrdinaryDiffEqFunctionMap
using SciMLBase: DiscreteProblem
using Symbolics: @variables

rng = StableRNG(42)
Expand Down
3 changes: 3 additions & 0 deletions lib/DataDrivenDMD/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ end
@safetestset "Nonlinear forced" begin
include("./Core/nonlinear_forced.jl")
end
@safetestset "Interface" begin
include("./Core/interface.jl")
end
end

if GROUP == "QA"
Expand Down
Loading
Loading