Koopman pipeline

Since the Koopman regression problem operates on timeseries data, it has additional requirements that preclude the use of scikit-learn sklearn.pipeline.Pipeline objects:

  1. The original state must be kept at the beginning of the lifted state.

  2. The input-dependent lifted states must be kept at the end of the lifted state.

  3. The number of input-independent and input-dependent lifting functions must be tracked throughout the pipeline.

  4. Samples must not be reordered or subsampled (this would corrupt delay-based lifting functions).

  5. Concatenated data from different training epidodes must not be mixed (even though the states are adjacent in the array, they may not be sequential in time).

To meet these requirements, each lifting function, described by the pykoop.KoopmanLiftingFn interface, supports a feature that indicates which episode each sample belongs to. Furthermore, each lifting function stores the number of input-dependent and input-independent features at its input and output.

The data matrices provided to fit() (as well as transform() and inverse_transform()) must obey the following format:

  1. If episode_feature is true, the first feature must indicate which episode each timestep belongs to. The episode feature must contain positive integers only.

  2. The last n_inputs features must be exogenous inputs.

  3. The remaining features are considered to be states (input-independent).

Consider an example data matrix where the fit() parameters are episode_feature=True and n_inputs=1:

Episode

State 0

State 1

Input 0

0.0

0.1

-0.1

0.2

0.0

0.2

-0.2

0.3

0.0

0.3

-0.3

0.4

1.0

-0.1

0.1

0.3

1.0

-0.2

0.2

0.4

1.0

-0.3

0.3

0.5

2.0

0.3

-0.1

0.3

2.0

0.2

-0.2

0.4

In the above matrix, there are three distinct episodes with different numbers of timesteps. The last feature is an input, so the remaining two features must be states.

If n_inputs=0, the same matrix is interpreted as:

Episode

State 0

State 1

State 2

0.0

0.1

-0.1

0.2

0.0

0.2

-0.2

0.3

0.0

0.3

-0.3

0.4

1.0

-0.1

0.1

0.3

1.0

-0.2

0.2

0.4

1.0

-0.3

0.3

0.5

2.0

0.3

-0.1

0.3

2.0

0.2

-0.2

0.4

If episode_feature=False and the first feature is omitted, the matrix is interpreted as:

State 0

State 1

State 2

0.1

-0.1

0.2

0.2

-0.2

0.3

0.3

-0.3

0.4

-0.1

0.1

0.3

-0.2

0.2

0.4

-0.3

0.3

0.5

0.3

-0.1

0.3

0.2

-0.2

0.4

In the above case, each timestep is assumed to belong to the same episode.

Important

The episode feature must contain positive integers only!

Koopman regressors, which implement the interface defined in pykoop.KoopmanRegressor are distinct from scikit-learn regressors in that they support the episode feature and state tracking attributes used by the lifting function objects. Koopman regressors also support being fit with a single data matrix, which they will split and time-shift according to the episode feature.

If the input is a pandas.DataFrame, then pykoop will store the column names in feature_names_in_ upon fitting. This applies to both pykoop.KoopmanLiftingFn and pykoop.KoopmanRegressor. If these feature names are specified, calling get_feature_names_in() will return them. If they do not exist, this function will return auto-generated ones. For instances of pykoop.KoopmanLiftingFn, calling get_feature_names_out() will generate the feature names of the lifted states. Note that pandas.DataFrame instances are converted to numpy.ndarray instances as soon as they are processed by pykoop. You can recreate them using something like pandas.DataFrame(X_lifted, columns=lf.get_feature_names_out()).

The following class and function implementations are located in pykoop.koopman_pipeline, but have been imported into the pykoop namespace for convenience.

pykoop.KoopmanPipeline([lifting_functions, ...])

Meta-estimator for chaining lifting functions with an estimator.

pykoop.SplitPipeline([...])

Meta-estimator for lifting states and inputs separately.

pykoop.combine_episodes(episodes[, ...])

Combine episodes into a data matrix.

pykoop.extract_initial_conditions(X[, ...])

Extract initial conditions from each episode.

pykoop.extract_input(X[, n_inputs, ...])

Extract input from a data matrix.

pykoop.score_trajectory(X_predicted, X_expected)

Score a predicted data matrix compared to an expected data matrix.

pykoop.shift_episodes(X[, n_inputs, ...])

Shift episodes and truncate shifted inputs.

pykoop.split_episodes(X[, episode_feature])

Split a data matrix into episodes.

pykoop.strip_initial_conditions(X[, ...])

Strip initial conditions from each episode.

Lifting functions

All of the lifting functions included in this module adhere to the interface defined in pykoop.KoopmanLiftingFn.

The following class and function implementations are located in pykoop.lifting_functions, but have been imported into the pykoop namespace for convenience.

pykoop.BilinearInputLiftingFn()

Lifting function to generate bilinear products of the state and input.

pykoop.ConstantLiftingFn()

Lifting function that appends a constant term to the input features.

pykoop.DelayLiftingFn([n_delays_state, ...])

Lifting function to generate delay coordinates for state and input.

pykoop.KernelApproxLiftingFn([kernel_approx])

Lifting function using random kernel approximation.

pykoop.PolynomialLiftingFn([order, ...])

Lifting function to generate all monomials of the input features.

pykoop.RbfLiftingFn([rbf, centers, shape, ...])

Lifting function using radial basis function (RBF) features.

pykoop.SkLearnLiftingFn([transformer])

Lifting function that wraps a scikit-learn transformer.

Regressors

All of the lifting functions included in this module adhere to the interface defined in pykoop.KoopmanRegressor.

The following class and function implementations are located in pykoop.regressors, but have been imported into the pykoop namespace for convenience.

The pykoop.DataRegressor regressor is a dummy regressor if you want to force the Koopman matrix to take on a specific value (maybe you know what it should be, or you got it from another library).

pykoop.Dmd([mode_type, tsvd])

Dynamic Mode Decomposition.

pykoop.Dmdc([mode_type, tsvd_unshifted, ...])

Dynamic Mode Decomposition with control.

pykoop.Edmd([alpha])

Extended Dynamic Mode Decomposition with Tikhonov regularization.

pykoop.EdmdMeta([regressor])

Extended Dynamic Mode Decomposition with scikit-learn regressor.

pykoop.DataRegressor([coef])

Create a KoopmanRegressor object from a NumPy array.

Kernel approximation methods

The following classes are used to generate random feature maps from kernels for kernel approximation lifting functions (i.e., random Fourier feature lifting functions).

pykoop.RandomBinningKernelApprox([...])

Kernel approximation with random binning.

pykoop.RandomFourierKernelApprox([...])

Kernel approximation with random Fourier features.

Radial basis function centers

The following classes are used to generate centers for radial basis function (RBF) lifting functions.

pykoop.ClusterCenters([estimator])

Centers generated from a clustering algorithm.

pykoop.DataCenters([centers])

Centers taken from raw data upon instantiation or fit.

pykoop.GaussianRandomCenters([n_centers, ...])

Centers sampled from a Gaussian distribution.

pykoop.GaussianMixtureRandomCenters([...])

Centers generated from sampling a Gaussian mixture model.

pykoop.GridCenters([n_points_per_feature, ...])

Centers generated on a uniform grid.

pykoop.QmcCenters([n_centers, ...])

Centers generated with Quasi-Monte Carlo sampling.

pykoop.UniformRandomCenters([n_centers, ...])

Centers sampled from a uniform distribution.

Truncated SVD

The following class and function implementations are located in pykoop.tsvd, but have been imported into the pykoop namespace for convenience.

pykoop.Tsvd([truncation, truncation_param])

Truncated singular value decomposition.

Utilities

The following class and function implementations are located in pykoop.util, but have been imported into the pykoop namespace for convenience.

pykoop.AnglePreprocessor([angle_features, ...])

Preprocessor used to replace angles with their cosines and sines.

pykoop.example_data_duffing()

Get example Duffing oscillator data.

pykoop.example_data_msd()

Get example mass-spring-damper data.

pykoop.example_data_pendulum()

Get example pendulum data.

pykoop.example_data_vdp()

Get example Van der Pol oscillator data.

pykoop.random_input(t_range, t_step, low, ...)

Generate a smooth random input.

pykoop.random_state(low, high[, rng])

Generate a random initial state.

LMI regressors

Experimental LMI-based Koopman regressors from [DF21] and [DF22].

Warning

Importing this module has side effects! When imported, the module creates a temporary directory with the prefix pykoop_, which is used to memoize long computations that may be repreated frequently. It also catches SIGINT so that long regressions can be stopped politely.

The following class and function implementations are located in pykoop.lmi_regressors, which must be imported separately.

pykoop.lmi_regressors.LmiEdmd([alpha, ...])

LMI-based EDMD with regularization.

pykoop.lmi_regressors.LmiEdmdDissipativityConstr([...])

LMI-based EDMD with dissipativity constraint.

pykoop.lmi_regressors.LmiEdmdHinfReg([...])

LMI-based EDMD with H-infinity norm regularization.

pykoop.lmi_regressors.LmiEdmdSpectralRadiusConstr([...])

LMI-based EDMD with spectral radius constraint.

pykoop.lmi_regressors.LmiDmdc([alpha, ...])

LMI-based DMDc with regularization.

pykoop.lmi_regressors.LmiDmdcHinfReg([...])

LMI-based DMDc with H-infinity norm regularization.

pykoop.lmi_regressors.LmiDmdcSpectralRadiusConstr([...])

LMI-based Dmdc with spectral radius constraint.

pykoop.lmi_regressors.LmiHinfZpkMeta([...])

Meta-estimator where H-infinity weight is specified in ZPK format.

Dynamic models

The following class and function implementations are located in pykoop.dynamic_models, which must be imported separately.

pykoop.dynamic_models.DiscreteVanDerPol(...)

Van der Pol oscillator.

pykoop.dynamic_models.DuffingOscillator([...])

Duffing oscillator model.

pykoop.dynamic_models.MassSpringDamper(mass, ...)

Mass-spring-damper model.

pykoop.dynamic_models.Pendulum(mass, length)

Point-mass pendulum with optional damping.

Configuration

The following functions allow the user to interact with pykoop’s global configuration.

pykoop.get_config()

Retrieve current values for configuration set by set_config().

pykoop.set_config([skip_validation])

Set global configuration.

pykoop.config_context(*[, skip_validation])

Context manager for global configuration.

Extending pykoop

The abstract classes from all of pykoop’s modules have been grouped here. If you want to write your own lifting functions or regressor, this is the place to look!

The following abstract class implementations are spread across pykoop.koopman_pipeline, pykoop.dynamic_models, pykoop.centers, and pykoop.lmi_regressors. The most commonly used ones have been imported into the pykoop namespace.

pykoop.Centers()

Base class for all center generation estimators.

pykoop.EpisodeDependentLiftingFn()

Base class for Koopman lifting functions that are episode-dependent.

pykoop.EpisodeIndependentLiftingFn()

Base class for Koopman lifting functions that are episode-independent.

pykoop.KernelApproximation()

Base class for all kernel approximations.

pykoop.KoopmanLiftingFn()

Base class for Koopman lifting functions.

pykoop.KoopmanRegressor()

Base class for Koopman regressors.

pykoop.dynamic_models.ContinuousDynamicModel()

Continuous-time dynamic model.

pykoop.dynamic_models.DiscreteDynamicModel()

Discrete-time dynamic model.

pykoop.lmi_regressors.LmiRegressor()

Base class for LMI regressors.