GitHub

Python API Reference

Complete reference for the koopman_dmd Python module (PyO3 bindings)

Installation

The Python bindings are built with PyO3 and packaged using maturin. To build and install from source:

# Install the build tool
pip install maturin

# Navigate to the Python binding directory
cd koopman-dmd-py

# Build and install in the current environment
maturin develop --release

After installation, the module is available as koopman_dmd:

import koopman_dmd
Prerequisite. You need a working Rust toolchain (rustc, cargo) installed on your system. See rustup.rs for installation instructions.

DMD

The primary class for Dynamic Mode Decomposition. Supports standard DMD, centering, and Extended DMD via lifting functions.

Constructor

class DMD(rank=None, center=False, lifting=None, lifting_param=None)
Parameter Type Default Description
rank int | None None SVD truncation rank. None selects rank automatically via singular-value threshold.
center bool False Subtract the temporal mean from each row before decomposition. The mean is stored and added back during reconstruction.
lifting str | None None Lifting function type. One of "polynomial", "trigonometric", or "delay".
lifting_param int | None None Parameter for the lifting function. Interpreted as polynomial degree, number of harmonics, or number of delays depending on the lifting type.
from koopman_dmd import DMD

# Auto-rank, no centering
dmd = DMD()

# Explicit rank with centering
dmd = DMD(rank=4, center=True)

# Extended DMD with polynomial lifting (degree 3)
dmd = DMD(lifting="polynomial", lifting_param=3)

# Extended DMD with trigonometric lifting (2 harmonics)
dmd = DMD(lifting="trigonometric", lifting_param=2)

# Extended DMD with delay embedding (5 delays)
dmd = DMD(lifting="delay", lifting_param=5)

Methods

fit(data)

Fit the DMD model to a data matrix.

Parameter Type Description
data numpy.ndarray Data matrix of shape (n_vars, n_time). Each column is a snapshot; each row is a state variable.

Returns: self -- the fitted DMD object, enabling method chaining.

import numpy as np
from koopman_dmd import DMD

data = np.random.randn(3, 100)  # 3 variables, 100 time steps
dmd = DMD(rank=2).fit(data)

predict(n_ahead, x0=None)

Predict future states using the spectral decomposition. Each time step is computed independently from eigenvalues, modes, and amplitudes (non-recursive). Suitable for long-range forecasting.

Parameter Type Default Description
n_ahead int Number of future time steps to predict.
x0 numpy.ndarray | None None Initial condition vector. If None, uses the first snapshot from the training data.

Returns: numpy.ndarray of shape (n_vars, n_ahead).

# Predict 50 steps ahead from the training initial condition
forecast = dmd.predict(50)

# Predict from a custom initial condition
x0 = np.array([1.0, 0.0, -0.5])
forecast = dmd.predict(50, x0=x0)

predict_matrix(n_ahead, x0=None)

Predict future states using recursive matrix multiplication. Each step is computed by applying the reconstructed linear operator to the previous state. Faster for short horizons.

Parameter Type Default Description
n_ahead int Number of future time steps to predict.
x0 numpy.ndarray | None None Initial condition vector. If None, uses the first snapshot from the training data.

Returns: numpy.ndarray of shape (n_vars, n_ahead).

forecast_mat = dmd.predict_matrix(50)

reconstruct(n_time, x0=None)

Reconstruct the fitted signal over a given number of time steps. Uses the spectral decomposition to reproduce the training dynamics. If centering was enabled, the stored mean is added back automatically.

Parameter Type Default Description
n_time int Number of time steps to reconstruct.
x0 numpy.ndarray | None None Initial condition. If None, uses the first training snapshot.

Returns: numpy.ndarray of shape (n_vars, n_time).

fitted = dmd.reconstruct(100)

spectrum(dt)

Compute the continuous-time spectral properties of the decomposition. Converts discrete-time eigenvalues to frequencies, growth rates, and stability indicators.

Parameter Type Description
dt float Time step between snapshots (seconds).

Returns: list[dict] -- one dictionary per mode with keys:

Key Type Description
frequency float Oscillation frequency (Hz).
magnitude float Absolute value of the discrete-time eigenvalue.
growth_rate float Continuous-time growth rate (positive = growing, negative = decaying).
damping_ratio float Damping ratio of the mode.
stability str One of "stable", "unstable", or "neutral".
spec = dmd.spectrum(dt=0.01)
for mode in spec:
    print(f"freq={mode['frequency']:.4f} Hz, growth={mode['growth_rate']:.4f}, {mode['stability']}")

stability(tol=1e-6)

Assess the overall stability of the fitted linear system by examining the eigenvalue magnitudes.

Parameter Type Default Description
tol float 1e-6 Tolerance for classifying eigenvalues as neutral (on the unit circle).

Returns: dict with keys:

Key Type Description
spectral_radius float Maximum eigenvalue magnitude.
is_stable bool True if all eigenvalue magnitudes are at most 1 + tol.
is_unstable bool True if any eigenvalue magnitude exceeds 1 + tol.
n_growing int Number of eigenvalues with magnitude greater than 1 + tol.
n_decaying int Number of eigenvalues with magnitude less than 1 - tol.
n_neutral int Number of eigenvalues within tol of the unit circle.
stab = dmd.stability()
print(f"Spectral radius: {stab['spectral_radius']:.6f}")
print(f"Stable: {stab['is_stable']}")
print(f"Growing/Decaying/Neutral: {stab['n_growing']}/{stab['n_decaying']}/{stab['n_neutral']}")

error(data)

Compute reconstruction error metrics by comparing the DMD reconstruction to the original data.

Parameter Type Description
data numpy.ndarray Original data matrix of shape (n_vars, n_time).

Returns: dict with keys:

Key Type Description
rmse float Root mean squared error across all entries.
relative_error float Frobenius norm of residual divided by Frobenius norm of data.
max_error float Maximum absolute pointwise error.
err = dmd.error(data)
print(f"RMSE:     {err['rmse']:.6f}")
print(f"Relative: {err['relative_error']:.6f}")
print(f"Max:      {err['max_error']:.6f}")

dominant_modes(criterion, n)

Extract the most significant modes according to a ranking criterion.

Parameter Type Description
criterion str Ranking criterion: "amplitude" (by amplitude magnitude) or "energy" (by mode energy contribution).
n int Number of top modes to return.

Returns: list[dict] -- one dictionary per mode, sorted by the chosen criterion in descending order.

top = dmd.dominant_modes(criterion="energy", n=3)
for m in top:
    print(m)

residual()

Compute per-mode residual norms, measuring how well each individual mode satisfies the DMD eigenvalue equation.

Returns: dict with keys:

Key Type Description
mode_residuals list[float] Residual norm for each mode.
max_residual float Maximum residual across all modes.
mean_residual float Mean residual across all modes.
res = dmd.residual()
print(f"Max residual:  {res['max_residual']:.2e}")
print(f"Mean residual: {res['mean_residual']:.2e}")

Properties

Property Type Description
eigenvalues list[complex] Discrete-time DMD eigenvalues.
modes numpy.ndarray (complex) DMD mode matrix of shape (n_vars, rank). Each column is a mode.
amplitudes list[complex] Mode amplitudes (weights for the initial condition).
rank int Effective rank used in the decomposition.
print("Eigenvalues:", dmd.eigenvalues)
print("Modes shape:", dmd.modes.shape)
print("Amplitudes: ", dmd.amplitudes)
print("Rank:       ", dmd.rank)

HankelDMD

DMD with time-delay embedding via Hankel matrices. Captures higher-order dynamics from scalar or low-dimensional measurements by augmenting the state space with lagged copies of the data.

Constructor

class HankelDMD(delays=None, rank=None, dt=1.0)
Parameter Type Default Description
delays int | None None Number of time-delay embeddings. None selects automatically.
rank int | None None SVD truncation rank. None selects automatically.
dt float 1.0 Time step between snapshots.
from koopman_dmd import HankelDMD

hdmd = HankelDMD(delays=10, rank=4, dt=0.01)

Methods

fit(data)

Fit the Hankel-DMD model. Internally constructs the Hankel matrix from the input data, then performs standard DMD on the augmented state.

Parameter Type Description
data numpy.ndarray Data matrix of shape (n_vars, n_time).

Returns: self

predict(n_ahead)

Predict future states. Output is projected back to the original observation dimension.

Parameter Type Description
n_ahead int Number of future time steps to predict.

Returns: numpy.ndarray of shape (n_vars, n_ahead).

reconstruct()

Reconstruct the training data from the Hankel-DMD model.

Returns: numpy.ndarray of shape (n_vars, n_time).

Properties

Property Type Description
eigenvalues list[complex] Discrete-time eigenvalues of the Hankel-DMD model.
rank int Effective rank used in the decomposition.
import numpy as np
from koopman_dmd import HankelDMD

# Scalar time series with delay embedding
t = np.linspace(0, 10, 500)
signal = np.sin(2.0 * np.pi * t) + 0.5 * np.sin(6.0 * np.pi * t)
data = signal.reshape(1, -1)

hdmd = HankelDMD(delays=20, rank=4).fit(data)

print("Eigenvalues:", hdmd.eigenvalues)
print("Rank:", hdmd.rank)

recon = hdmd.reconstruct()
forecast = hdmd.predict(100)

GLA

Generalized Laplace Analysis for extracting Koopman eigenvalues and eigenfunctions from trajectory data using spectral methods. GLA solves an optimization problem to find the eigenvalues that best explain the observed dynamics.

Constructor

class GLA(n_eigenvalues=2, tol=1e-6, max_iter=None)
Parameter Type Default Description
n_eigenvalues int 2 Number of Koopman eigenvalues to extract.
tol float 1e-6 Convergence tolerance for the iterative solver.
max_iter int | None None Maximum iterations. None uses an internal default.
from koopman_dmd import GLA

gla = GLA(n_eigenvalues=4, tol=1e-8)

Methods

fit(data)

Fit the GLA model to trajectory data.

Parameter Type Description
data numpy.ndarray Data matrix of shape (n_vars, n_time).

Returns: self

predict(n_ahead)

Predict future states using the extracted Koopman eigenvalues.

Parameter Type Description
n_ahead int Number of future time steps.

Returns: numpy.ndarray

reconstruct()

Reconstruct the training data from the GLA model.

Returns: numpy.ndarray

Properties

Property Type Description
eigenvalues list[complex] Extracted Koopman eigenvalues.
convergence_rate float Convergence rate of the iterative solver at termination.
import numpy as np
from koopman_dmd import GLA

t = np.linspace(0, 20, 1000)
signal = np.cos(1.5 * t) + 0.3 * np.cos(3.7 * t)
data = signal.reshape(1, -1)

gla = GLA(n_eigenvalues=4, tol=1e-8).fit(data)

print("Eigenvalues:", gla.eigenvalues)
print("Convergence rate:", gla.convergence_rate)

recon = gla.reconstruct()
forecast = gla.predict(200)

Functions

generate_trajectory

generate_trajectory(ic, map_name, n_steps, params=None)

Generate a trajectory from one of the built-in dynamical maps.

Parameter Type Default Description
ic list Initial condition as a list of floats.
map_name str Name of the dynamical map. One of: "standard", "froeschle", "extended_standard", "henon", "logistic".
n_steps int Number of iteration steps to compute.
params dict | None None Map-specific parameters. Keys depend on the map (see below).

Returns: numpy.ndarray of shape (n_vars, n_steps).

Map parameters:

Map Parameters Default Values
"standard" {"epsilon": float} epsilon = 0.12
"froeschle" {"epsilon": float} epsilon = 0.12
"extended_standard" {"epsilon": float} epsilon = 0.12
"henon" {"a": float, "b": float} a = 1.4, b = 0.3
"logistic" {"r": float} r = 3.9
from koopman_dmd import generate_trajectory

# Standard map trajectory
traj = generate_trajectory(
    ic=[0.1, 0.2],
    map_name="standard",
    n_steps=10000,
    params={"epsilon": 0.12}
)

# Henon map with custom parameters
traj = generate_trajectory(
    ic=[0.0, 0.0],
    map_name="henon",
    n_steps=5000,
    params={"a": 1.4, "b": 0.3}
)

# Logistic map
traj = generate_trajectory(
    ic=[0.5],
    map_name="logistic",
    n_steps=1000,
    params={"r": 3.9}
)

harmonic_time_average

harmonic_time_average(ic, map_name, observable, omega, n_iter, params=None)

Compute the harmonic time average of an observable along a trajectory at a specified frequency. The harmonic time average isolates the Fourier component of the observable at frequency omega, providing a finite-time approximation of the corresponding Koopman eigenfunction.

Parameter Type Default Description
ic list Initial condition.
map_name str Name of the dynamical map.
observable str Observable function. One of: "identity", "sin_pi", "cos_pi", "sin_pi_xy", "quadratic".
omega float Frequency at which to compute the harmonic average.
n_iter int Number of iterations for the time average.
params dict | None None Map-specific parameters.

Returns: dict with keys:

Key Type Description
magnitude float Absolute value of the harmonic time average.
phase float Phase angle (radians).
real float Real part of the harmonic time average.
imag float Imaginary part of the harmonic time average.
from koopman_dmd import harmonic_time_average

result = harmonic_time_average(
    ic=[0.1, 0.2],
    map_name="standard",
    observable="identity",
    omega=0.3,
    n_iter=10000,
    params={"epsilon": 0.12}
)

print(f"Magnitude: {result['magnitude']:.6f}")
print(f"Phase:     {result['phase']:.6f}")
print(f"Real:      {result['real']:.6f}")
print(f"Imag:      {result['imag']:.6f}")

mesochronic_compute

mesochronic_compute(map_name, x_range, y_range, resolution, observable, omega, n_iter, params=None)

Compute the harmonic time average over a grid of initial conditions, producing matrices suitable for mesochronic analysis and visualization. This function evaluates the HTA at each grid point in the specified rectangular domain.

Parameter Type Default Description
map_name str Name of the dynamical map.
x_range tuple Range of x-coordinates as (x_min, x_max).
y_range tuple Range of y-coordinates as (y_min, y_max).
resolution int Number of grid points along each axis.
observable str Observable function name.
omega float Frequency for the harmonic average.
n_iter int Number of iterations per grid point.
params dict | None None Map-specific parameters.

Returns: dict with keys:

Key Type Description
hta_matrix numpy.ndarray HTA magnitude at each grid point, shape (resolution, resolution).
phase_matrix numpy.ndarray HTA phase at each grid point, shape (resolution, resolution).
x_coords numpy.ndarray x-axis coordinate values, shape (resolution,).
y_coords numpy.ndarray y-axis coordinate values, shape (resolution,).
from koopman_dmd import mesochronic_compute

result = mesochronic_compute(
    map_name="standard",
    x_range=(0.0, 1.0),
    y_range=(0.0, 1.0),
    resolution=200,
    observable="identity",
    omega=0.0,
    n_iter=5000,
    params={"epsilon": 0.12}
)

# Visualize with matplotlib
import matplotlib.pyplot as plt
plt.pcolormesh(
    result["x_coords"],
    result["y_coords"],
    result["hta_matrix"],
    cmap="viridis"
)
plt.colorbar(label="HTA magnitude")
plt.xlabel("x")
plt.ylabel("y")
plt.title("Mesochronic plot -- Standard Map")
plt.show()

classify_phase_space

classify_phase_space(ic, map_name, omega, n_iter, params=None)

Classify a single initial condition as belonging to a regular (quasiperiodic) or chaotic region of phase space, based on the convergence behavior of the harmonic time average.

Parameter Type Default Description
ic list Initial condition.
map_name str Name of the dynamical map.
omega float Frequency for the harmonic average.
n_iter int Number of iterations.
params dict | None None Map-specific parameters.

Returns: dict with keys:

Key Type Description
classification str "regular" or "chaotic".
hta_magnitude float Final HTA magnitude.
hta_phase float Final HTA phase (radians).
from koopman_dmd import classify_phase_space

result = classify_phase_space(
    ic=[0.1, 0.2],
    map_name="standard",
    omega=0.0,
    n_iter=50000,
    params={"epsilon": 0.12}
)

print(f"Classification: {result['classification']}")
print(f"HTA magnitude:  {result['hta_magnitude']:.6f}")

hta_convergence

hta_convergence(ic, map_name, observable, omega, max_iter, step, params=None)

Compute the harmonic time average at increasing iteration counts to study its convergence behavior. Returns the HTA value at evenly spaced checkpoints, which can be used to assess whether the average has stabilized or is still fluctuating (indicating chaotic dynamics).

Parameter Type Default Description
ic list Initial condition.
map_name str Name of the dynamical map.
observable str Observable function name.
omega float Frequency for the harmonic average.
max_iter int Maximum number of iterations.
step int Step size between checkpoints.
params dict | None None Map-specific parameters.

Returns: dict -- contains convergence data (iteration counts and corresponding HTA values at each checkpoint).

from koopman_dmd import hta_convergence

conv = hta_convergence(
    ic=[0.1, 0.2],
    map_name="standard",
    observable="identity",
    omega=0.3,
    max_iter=50000,
    step=500,
    params={"epsilon": 0.12}
)

Complete Examples

DMD: Fit, Predict, and Analyze

import numpy as np
from koopman_dmd import DMD

# Generate a multi-component signal
n = 200
dt = 0.02
t = np.arange(n) * dt
x1 = np.sin(2.0 * np.pi * 1.5 * t)
x2 = np.cos(2.0 * np.pi * 1.5 * t) * np.exp(-0.1 * t)
x3 = np.sin(2.0 * np.pi * 3.0 * t) * 0.5
data = np.vstack([x1, x2, x3])  # shape (3, 200)

# Fit the DMD model
dmd = DMD(rank=4, center=True).fit(data)

# Inspect eigenvalues and modes
print("Rank:       ", dmd.rank)
print("Eigenvalues:", dmd.eigenvalues)
print("Modes shape:", dmd.modes.shape)
print("Amplitudes: ", dmd.amplitudes)

# Spectral analysis
spec = dmd.spectrum(dt=dt)
for i, mode in enumerate(spec):
    print(f"Mode {i}: freq={mode['frequency']:.2f} Hz, "
          f"growth={mode['growth_rate']:.4f}, {mode['stability']}")

# Stability check
stab = dmd.stability()
print(f"System stable: {stab['is_stable']}")

# Reconstruction error
err = dmd.error(data)
print(f"RMSE:           {err['rmse']:.6f}")
print(f"Relative error: {err['relative_error']:.6f}")

# Dominant modes
top = dmd.dominant_modes(criterion="energy", n=2)
print("Top 2 modes by energy:", top)

# Mode residuals
res = dmd.residual()
print(f"Max residual:  {res['max_residual']:.2e}")

# Predict 100 steps ahead
forecast = dmd.predict(100)
print(f"Forecast shape: {forecast.shape}")

# Reconstruct the training window
fitted = dmd.reconstruct(n)
print(f"Reconstruction shape: {fitted.shape}")

Extended DMD with Polynomial Lifting

import numpy as np
from koopman_dmd import DMD

# Nonlinear signal: sin^2(t)
n = 300
dt = 0.05
t = np.arange(n) * dt
signal = np.sin(t) ** 2
data = signal.reshape(1, -1)

# Standard DMD (will have high error on nonlinear signal)
dmd_std = DMD().fit(data)
err_std = dmd_std.error(data)
print(f"Standard DMD RMSE: {err_std['rmse']:.6f}")

# Extended DMD with polynomial lifting (degree 2)
dmd_ext = DMD(lifting="polynomial", lifting_param=2).fit(data)
err_ext = dmd_ext.error(data)
print(f"Extended DMD RMSE: {err_ext['rmse']:.6f}")

# Predictions are automatically back-projected to original space
forecast = dmd_ext.predict(100)
print(f"Forecast shape: {forecast.shape}")  # (1, 100)

Hankel-DMD for Scalar Time Series

import numpy as np
from koopman_dmd import HankelDMD

# Two-frequency signal from scalar measurement
t = np.linspace(0, 10, 500)
signal = np.sin(2.0 * np.pi * t) + 0.5 * np.sin(6.0 * np.pi * t)
data = signal.reshape(1, -1)

# Hankel-DMD with time-delay embedding
hdmd = HankelDMD(delays=20, rank=4, dt=t[1] - t[0]).fit(data)

print("Eigenvalues:", hdmd.eigenvalues)
print("Rank:", hdmd.rank)

# Reconstruct and predict
recon = hdmd.reconstruct()
forecast = hdmd.predict(100)

print(f"Reconstruction shape: {recon.shape}")
print(f"Forecast shape:       {forecast.shape}")

GLA for Spectral Extraction

import numpy as np
from koopman_dmd import GLA

# Signal with two distinct frequencies
t = np.linspace(0, 20, 1000)
signal = np.cos(1.5 * t) + 0.3 * np.cos(3.7 * t)
data = signal.reshape(1, -1)

# Extract 4 Koopman eigenvalues via GLA
gla = GLA(n_eigenvalues=4, tol=1e-8).fit(data)

print("Koopman eigenvalues:")
for ev in gla.eigenvalues:
    print(f"  {ev}")
print(f"Convergence rate: {gla.convergence_rate:.2e}")

# Reconstruct and forecast
recon = gla.reconstruct()
forecast = gla.predict(200)
print(f"Forecast shape: {forecast.shape}")

Trajectory Generation and HTA Analysis

import numpy as np
from koopman_dmd import (
    generate_trajectory,
    harmonic_time_average,
    classify_phase_space,
    hta_convergence,
    mesochronic_compute,
)

# Generate a trajectory on the standard map
traj = generate_trajectory(
    ic=[0.1, 0.2],
    map_name="standard",
    n_steps=10000,
    params={"epsilon": 0.12}
)
print(f"Trajectory shape: {traj.shape}")  # (2, 10000)

# Compute harmonic time average
hta = harmonic_time_average(
    ic=[0.1, 0.2],
    map_name="standard",
    observable="identity",
    omega=0.3,
    n_iter=10000,
    params={"epsilon": 0.12}
)
print(f"HTA magnitude: {hta['magnitude']:.6f}")
print(f"HTA phase:     {hta['phase']:.6f}")

# Classify the initial condition
cls = classify_phase_space(
    ic=[0.1, 0.2],
    map_name="standard",
    omega=0.0,
    n_iter=50000,
    params={"epsilon": 0.12}
)
print(f"Region: {cls['classification']}")

# Study convergence of the HTA
conv = hta_convergence(
    ic=[0.1, 0.2],
    map_name="standard",
    observable="identity",
    omega=0.3,
    max_iter=50000,
    step=500,
    params={"epsilon": 0.12}
)

# Compute mesochronic plot data
meso = mesochronic_compute(
    map_name="standard",
    x_range=(0.0, 1.0),
    y_range=(0.0, 1.0),
    resolution=100,
    observable="identity",
    omega=0.0,
    n_iter=5000,
    params={"epsilon": 0.12}
)
print(f"HTA matrix shape:   {meso['hta_matrix'].shape}")
print(f"Phase matrix shape: {meso['phase_matrix'].shape}")