nirs4all.operators.models.sklearn.oklmpls module
Online Koopman Latent-Mode PLS (OKLM-PLS) regressor for nirs4all.
A sklearn-compatible implementation of OKLM-PLS that combines Koopman operator theory with PLS for time-series regression. The model learns latent dynamics T_{t+1} ≈ F @ T_t while simultaneously fitting Y_t ≈ T_t @ B.
This is useful for spectral data collected over time where temporal coherence provides additional information for prediction.
Supports both NumPy (CPU) and JAX (GPU/TPU) backends.
References
Mathematical formulation
Let
X ∈ ℝ^{n×p} be the input matrix of n samples and p features (e.g. NIRS spectra), ordered in time t = 1,…,n.
Y ∈ ℝ^{n×q} be the corresponding response matrix.
An optional feature map ψ : ℝ^p → ℝ^d is applied to each row x_t, giving Z ∈ ℝ^{n×d} with rows
z_t = ψ(x_t), t = 1,…,n.
OKLM-PLS seeks:
a loading matrix W ∈ ℝ^{d×r} (r latent components),
a latent dynamic matrix F ∈ ℝ^{r×r},
a regression matrix B ∈ ℝ^{r×q},
such that the latent scores
T = Z W ∈ ℝ^{n×r}, t_t = row t of T,
(1) capture covariance between X (via Z) and Y in the PLS sense, (2) follow a simple linear dynamics in latent space, and (3) predict Y linearly.
The latent dynamics is modeled as a Koopman-like linear evolution:
t_{t+1} ≈ F t_t, for t = 1,…,n−1.
The regression in latent space is
y_t ≈ Bᵀ t_t, for t = 1,…,n,
or in matrix form
Y ≈ T B.
A simple joint objective is
- L(W, F, B)
- = λ_dyn ∑_{t=1}^{n−1} ‖ t_{t+1} − F t_t ‖₂²
λ_reg ∑_{t=1}^{n} ‖ y_t − Bᵀ t_t ‖₂²,
subject to PLS-style constraints on W and T (e.g. orthonormal scores and components ordered by decreasing covariance with Y):
(1/n) Tᵀ T = I_r, components sorted by cov(T_j, Y).
In practice, the objective is optimized by alternating updates:
Latent scores Given W, we form Z and T = Z W.
Dynamics update Given T, we estimate F as the least-squares solution of
F = argmin_F ∑_{t=1}^{n−1} ‖ t_{t+1} − F t_t ‖₂²,
which has the closed form
F = (∑_{t} t_{t+1} t_tᵀ) (∑_{t} t_t t_tᵀ)^{-1}.
Regression update Given T, we estimate B as the least-squares solution of
B = argmin_B ‖ Y − T B ‖_F²,
giving
B = (Tᵀ T)^{-1} Tᵀ Y.
PLS-like loading update W is initialized from a standard PLS on (Z, Y), and can optionally be refined by (approximate) gradient-based updates on L(W, F, B), while enforcing column normalization and PLS-style orthogonality constraints.
The fitted model predicts Y for new inputs X* by:
applying the same preprocessing and feature map ψ to obtain Z*,
computing scores T* = Z* W,
returning Ŷ* = T* B (with inverse scaling if standardization is used).
The term “online” refers to the fact that, for streaming data, F and B can be updated recursively as new scores t_t arrive, while W is kept fixed or updated more infrequently.
- class nirs4all.operators.models.sklearn.oklmpls.IdentityFeaturizer[source]
Bases:
BaseEstimator,TransformerMixinIdentity featurizer: ψ(x) = x.
This is the default featurizer for OKLMPLS when no nonlinear transformation is needed.
- fit(X, y=None)[source]
Fit the featurizer (no-op for identity).
- Parameters:
X (array-like of shape (n_samples, n_features)) – Training data.
y (ignored)
- Returns:
self
- Return type:
- transform(X)[source]
Transform X (identity).
- Parameters:
X (array-like of shape (n_samples, n_features)) – Data to transform.
- Returns:
X – Same as input.
- Return type:
ndarray of shape (n_samples, n_features)
- class nirs4all.operators.models.sklearn.oklmpls.OKLMPLS(n_components: int = 5, featurizer: TransformerMixin | None = None, lambda_dyn: float = 1.0, lambda_reg_y: float = 1.0, max_iter: int = 50, tol: float = 0.0001, warm_start_pls: bool = True, standardize: bool = True, backend: str = 'numpy', random_state: int | None = None)[source]
Bases:
BaseEstimator,RegressorMixinOnline Koopman Latent-Mode Partial Least Squares (OKLM-PLS).
OKLM-PLS combines Koopman operator theory with PLS for time-series regression. It learns latent scores T = ψ(X) @ W and simultaneously: - Enforces dynamic coherence: T_{t+1} ≈ F @ T_t - Learns regression: Y_t ≈ T_t @ B
This is useful for spectral data collected over time where temporal coherence provides additional predictive information.
- Parameters:
n_components (int, default=5) – Number of latent components.
featurizer (TransformerMixin, optional) – Feature map ψ: X -> Z. If None, identity is used. Options include PolynomialFeaturizer and RBFFeaturizer.
lambda_dyn (float, default=1.0) – Weight for dynamic consistency loss ||T_{t+1} - F @ T_t||². Higher values enforce stronger temporal coherence.
lambda_reg_y (float, default=1.0) – Weight for regression loss ||Y - T @ B||².
max_iter (int, default=50) – Maximum alternating optimization iterations.
tol (float, default=1e-4) – Convergence tolerance on the objective function.
warm_start_pls (bool, default=True) – If True, initialize W/B from a standard PLSRegression fit.
standardize (bool, default=True) – Whether to standardize X and Y before fitting.
backend (str, default='numpy') – Computational backend: - ‘numpy’: NumPy backend (CPU only). - ‘jax’: JAX backend (supports GPU/TPU).
random_state (int, optional) – Random seed for initialization.
- W_
Projection weights (in featurized space).
- Type:
ndarray of shape (n_features_z, n_components_)
- F_
Dynamics matrix for latent scores.
- Type:
ndarray of shape (n_components_, n_components_)
- B_
Regression coefficients.
- Type:
ndarray of shape (n_components_, n_targets)
Examples
>>> from nirs4all.operators.models.sklearn.oklmpls import OKLMPLS >>> import numpy as np >>> # Generate time-series data >>> np.random.seed(42) >>> X = np.random.randn(100, 50) >>> y = X[:, :5].sum(axis=1) + 0.1 * np.random.randn(100) >>> # Fit OKLM-PLS >>> model = OKLMPLS(n_components=10, lambda_dyn=1.0, lambda_reg_y=1.0) >>> model.fit(X, y) OKLMPLS(...) >>> predictions = model.predict(X) >>> # Use with polynomial featurizer for nonlinearity >>> from nirs4all.operators.models.sklearn.oklmpls import PolynomialFeaturizer >>> model_poly = OKLMPLS(n_components=10, featurizer=PolynomialFeaturizer(degree=2)) >>> model_poly.fit(X, y)
Notes
OKLM-PLS is designed for temporally-ordered data where samples are sequential in time. The dynamics constraint helps capture temporal patterns and can improve prediction when the underlying process has smooth temporal evolution.
For non-temporal data, set lambda_dyn=0 to disable the dynamics constraint (equivalent to standard PLS with optional featurization).
See also
SIMPLSStandard PLS without dynamics.
RecursivePLSOnline PLS with forgetting factor.
- fit(X: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str], y: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str]) OKLMPLS[source]
Fit the OKLM-PLS model.
- Parameters:
- Returns:
self – Fitted estimator.
- Return type:
- Raises:
ValueError – If backend is invalid.
ImportError – If JAX backend requested but not installed.
- predict(X: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str]) ndarray[tuple[Any, ...], dtype[floating]][source]
Predict using the OKLM-PLS model.
- predict_dynamic(X: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str], n_steps: int = 1) ndarray[tuple[Any, ...], dtype[floating]][source]
Predict using dynamics model for future timesteps.
Given the last sample’s latent scores, predict future values using the learned dynamics T_{t+1} = F @ T_t.
- set_score_request(*, sample_weight: bool | None | str = '$UNCHANGED$') OKLMPLS
Configure whether metadata should be requested to be passed to the
scoremethod.Note that this method is only relevant when this estimator is used as a sub-estimator within a meta-estimator and metadata routing is enabled with
enable_metadata_routing=True(seesklearn.set_config()). Please check the User Guide on how the routing mechanism works.The options for each parameter are:
True: metadata is requested, and passed toscoreif provided. The request is ignored if metadata is not provided.False: metadata is not requested and the meta-estimator will not pass it toscore.None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.str: metadata should be passed to the meta-estimator with this given alias instead of the original name.
The default (
sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.Added in version 1.3.
- transform(X: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | complex | bytes | str | _NestedSequence[complex | bytes | str]) ndarray[tuple[Any, ...], dtype[floating]][source]
Transform X to latent score space.
- Parameters:
X (array-like of shape (n_samples, n_features)) – Samples to transform.
- Returns:
T – Latent scores.
- Return type:
ndarray of shape (n_samples, n_components_)
- class nirs4all.operators.models.sklearn.oklmpls.PolynomialFeaturizer(degree: int = 2, include_original: bool = True)[source]
Bases:
BaseEstimator,TransformerMixinPolynomial featurizer for OKLM-PLS.
Creates polynomial features up to specified degree without interaction terms (for efficiency with high-dimensional spectral data).
- Parameters:
- fit(X, y=None)[source]
Fit the featurizer.
- Parameters:
X (array-like of shape (n_samples, n_features)) – Training data.
y (ignored)
- Returns:
self
- Return type:
- class nirs4all.operators.models.sklearn.oklmpls.RBFFeaturizer(n_components: int = 100, gamma: float | None = None, random_state: int | None = None)[source]
Bases:
BaseEstimator,TransformerMixinRandom Fourier Features (RBF approximation) featurizer for OKLM-PLS.
Approximates the RBF kernel using random Fourier features, which is useful for adding nonlinearity to the Koopman embedding.
- Parameters:
- fit(X, y=None)[source]
Fit the featurizer by sampling random frequencies.
- Parameters:
X (array-like of shape (n_samples, n_features)) – Training data.
y (ignored)
- Returns:
self
- Return type:
- transform(X)[source]
Transform X to random Fourier features.
- Parameters:
X (array-like of shape (n_samples, n_features)) – Data to transform.
- Returns:
X_rff – Random Fourier features.
- Return type:
ndarray of shape (n_samples, n_components)