continuum_regression — Continuum Regression (Stone & Brooks 1990)

Group: Nonlinear / local · Registry tolerance: 1e-06

Description

Continuum regression (interpolates PLS / OLS)

From the pls4all.sklearn.ContinuumRegression docstring:

Continuum regression τ ∈ [0, 1] interpolates PLS (1) / OLS (0).

Registry note — Canonical Stone & Brooks (1990) continuum regression. Python ContinuumPyReference is a paper-faithful NumPy implementation (no widely installable Python port exists); the pls4all C++ kernel uses the same algorithm and matches bit-for-bit. The optional R JICO::continuum adapter uses a different (lambda, gamma, om) parameterization and is kept as a qualitative cross-check.

Parameters

Name

Type

Default

Notes

n_components

int

2

Number of latent components extracted (k).

tau

float

0.5

Continuum mixing parameter in [0, 1]; 0 ≈ PLS, 1 ≈ whitened OLS / PCR-like.

Explanations

Bibliographic source

Stone, M. & Brooks, R. J. (1990). Continuum regression: cross-validated sequentially constructed prediction embracing ordinary least squares, partial least squares and principal components regression. JRSS B 52(2), 237–269.

Mathematical principle

Continuum regression introduces a single shape parameter \(\tau \in [0, 1]\) that selects the loading-weight criterion: \(\mathbf{w} \propto \operatorname{Cov}(\mathbf{X}\mathbf{w}, \mathbf{y})^{\tau} \cdot \operatorname{Var}(\mathbf{X}\mathbf{w})^{1-\tau}\). Special cases: \(\tau = 0\) gives PCR (variance-maximising), \(\tau = 1/2\) gives PLS (covariance-maximising), \(\tau = 1\) gives OLS (correlation-maximising, in the appropriate limit).

Cross-validating \(\tau\) on a fine grid often improves RMSE over the discrete PLS / PCR choices — the optimum is rarely exactly at \(\tau = 0.5\). Stone & Brooks’ original treatment also cross-validates the number of components \(k\) jointly with \(\tau\), producing a 2-D grid.

Implementation note: numerically stable continuum regression uses the parameterised power method on the matrix \(\mathbf{X}^{\top}\mathbf{y}\mathbf{y}^{\top}\mathbf{X} / (\mathbf{X}^{\top}\mathbf{X})^{1-\tau}\), which avoids forming the rank-1 outer product explicitly and is what pls4all uses.

Implementation

n4m_continuum_regression_fit (in-sample only).

MATLAB header (bindings/matlab/+pls4all/ContinuumRegression.m):

pls4all.ContinuumRegression  Continuum regression (tau ∈ [0, 1]).

Usage

Direct n4m Python helper:

import n4m

res = n4m.continuum_regression(X, y, n_components=4, tau=0.25)
y_hat = res["predictions"]
coef = res["coefficients"]

Reusable sklearn-style wrapper:

from n4m.sklearn import NativeContinuumRegressionRegressor

model = NativeContinuumRegressionRegressor(
    n_components=4,
    tau=0.25,
).fit(X, y)
y_hat = model.predict(X_test)

Every pls4all binding tab dispatches into the same C kernel; the external libraries listed at the bottom of the page are the parity references registered in benchmarks.parity_timing.registry. Switch tabs to read the same fit in your language. The R package now ships drop-in-compatible facades for the CRAN pls package (plsr, pcr, mvr) and for the mdatools::pls(x, y, ...) matrix idiom — those tabs appear only on the methods that have a meaningful equivalence.

pls4all bindings

/* C ABI — libn4m */
n4m_context_t* ctx = n4m_context_create();
n4m_config_t*  cfg = n4m_config_create();
n4m_method_result_t* res = NULL;
n4m_continuum_regression_fit(ctx, cfg, &x_view, &y_view, /* hyperparams */, &res);
/* … read coefficients / mask / scores via */
/* n4m_method_result_get_double_matrix / vector / scalar … */
n4m_method_result_destroy(res);
n4m_config_destroy(cfg);
n4m_context_destroy(ctx);
import pls4all
from pls4all._methods import continuum_regression_fit
with pls4all.Context() as ctx, pls4all.Config() as cfg:
    res = continuum_regression_fit(ctx, cfg, X, y, n_components=4)
# then: res.matrix("predictions"), res.matrix("coefficients"),
# res.vector("mask"), res.scalar("intercept"), …
from pls4all.sklearn import ContinuumRegression
mdl = ContinuumRegression(n_components=2, tau=0.5)
mdl.fit(X, y)
y_hat = mdl.predict(X_test)
library(pls4all)
# Unified low-level dispatcher (May 2026 R cleanup):
res <- pls4all_method("continuum_regression", X, y,
                      n_components = 4L, params = list(tau = 0.5))
# res is a named list with MethodResult arrays/scalars.
# selected_indices / top_k_intervals are 1-based.
res = pls4all.continuum_regression(X, y, 4);
% see header of bindings/matlab/+pls4all/continuum_regression.m for full
% parameter surface:
%   res = continuum_regression(X, Y, n_components, tau)
yhat = predict(res, Xtest);
mdl  = pls4all.fit("continuum_regression", X, y, "NumComponents", 4);
yhat = predict(mdl, Xtest);

Registry parity references 📐

  • 📐 ref.python_stone_brooks_1990_py (python · python) — stone-brooks-1990-py 1.0 · strict (rmse_rel ≤ 1e-06) — NumPy reference for Stone & Brooks (1990) continuum regression. First-component weight is (X’X)^{-tau} X’y, computed via the centered-X SVD; subsequent components use SIMPLS basis-v deflation of the modified cross-product matrix.

  • 📐 ref.r_jico (R · r) — JICO 0.1 · strict (rmse_rel ≤ 1e-06) — R JICO::continuum (Stone & Brooks 1990). Different parameterization than pls4all — JICO uses (lambda, gamma, om) while pls4all maps a single τ. Predictions are reconstructed by regressing Y on JICO’s latent scores.

Benchmarks

Adaptive wall-clock per cell measured against full_matrix.csv. Only backends that implement this method are listed; libraries without the method are omitted.

Verdict  ·  ✓ ref / ≈ ref / ~ shape mark a reference-gate pass at strict / relaxed / qualitative tolerance  ·  ✓ bind = pls4all binding agrees with the C++ baseline  ·  ⇄ cross-check = documented by-design selector/RNG/model, noncanonical API/facade convention, or secondary oracle  ·  ✗ divergent  ·  ⚠ error  ·  — not run. The fastest backend per column is marked 🏆.

Reference gate: strict — numeric equivalence (rmse_rel_tol 1e-06).

Rows tagged with 📐 are the canonical parity references for this method (declared in parity_timing.registry). C++ and external rows show reference parity; pls4all language bindings show binding parity against the C++ backend. Hover the icon for role and tolerance band.

BackendParity200×50 (ms)
C++ native · libn4m
pls4all.cpp.blas+omp✓ ref 1e-142.73 ms
Python · pls4all
pls4all.python✓ bind2.63 ms
pls4all.sklearn✓ bind2.77 ms
R · pls4all
pls4all.R✓ bind5.55 ms
pls4all.R.formula✓ bind6.02 ms
pls4all.R.mdatools✓ bind6.62 ms
pls4all.R.pls✓ bind6.62 ms
Python · external
📐ref.python_stone_brooks_1990_pysource2.27 ms🏆
R · external
📐ref.r_jico⇄ +8e-0331.6 ms
BackendParity200×50 (ms)
C++ native · libn4m
pls4all.cpp.blas+omp✓ ref 1e-142.70 ms
Python · pls4all
pls4all.python✓ bind2.65 ms
pls4all.sklearn✓ bind2.83 ms
R · pls4all
pls4all.R✓ bind5.33 ms
pls4all.R.formula✓ bind6.41 ms
pls4all.R.mdatools✓ bind6.49 ms
pls4all.R.pls✓ bind6.57 ms
Python · external
📐ref.python_stone_brooks_1990_pysource2.39 ms🏆
R · external
📐ref.r_jico⇄ +8e-0331.6 ms
BackendParity200×50 (ms)
C++ native · libn4m
pls4all.cpp.blas+omp✓ ref 1e-142.70 ms🏆
Python · pls4all
pls4all.python✓ bind2.76 ms
pls4all.sklearn✓ bind2.93 ms
R · pls4all
pls4all.R✓ bind6.21 ms
pls4all.R.formula✓ bind6.41 ms
pls4all.R.mdatools✓ bind7.10 ms
pls4all.R.pls✓ bind6.57 ms
Python · external
📐ref.python_stone_brooks_1990_pysource4.06 ms
R · external
📐ref.r_jico⇄ +8e-0349.0 ms

See also: benchmark overview · methods index · interactive dashboard