Roadmap¶
⚠️ LEGACY (pre-rename). This is the original phase log from before the
pls4all/p4a→nirs4all-methods/n4mrename (it still sayspls4all, referencesDirection_Technique.md/Overview.md, and stops at phase-49 / v0.97). It is kept as a historical record of the shipped algorithm phases. The authoritative, current plan isREFACTOR_PLAN.md(phases A–F); do not treat anything below as current.
pls4all is built in deliberate phases. Each delivered phase has a
self-contained note under roadmap/. The canonical technical
spec is Direction_Technique.md. The full algorithm
taxonomy is in Overview.md.
The project rule remains:
C++17 internal core.
Public stable C ABI only.
No mandatory runtime dependency beyond libc/libstdc++/libgcc.
Thin bindings over the C ABI.
Every numerical method must have a deterministic parity gate against the best available R or Python reference.
GitHub Actions are parked for now to save runner quota; the authoritative gate is the local parity/build/test/sanitizer run.
Current Checkpoint - 2026-05-16¶
Latest local tag: phase-49-vissa-pls (0.97.0+abi.1.14.0).
Last green local gate:
97 deterministic parity fixtures.
256 C++ ABI/core tests (unchanged at 256; Batch 1 promotes existing internal kernels to public ABI rather than adding new ones).
pls4all_cli --selfcheck.pls4all_cli --benchsmoke for every shipped PLS solver.Python ctypes smoke:
bindings/python/smoke_aom_pop.pyexercises every AOM/POP fixture through the public C ABI.Python model smoke:
pls4all.Model.fit / predict / get_arraysucceeds.ABI symbol diff against
cpp/abi/expected_symbols_linux.txt: 159 symbols, allp4a_*prefixed.ldddependency audit: only libc/libstdc++/libgcc/libm/loader.UBSAN.
ASAN+UBSAN.
Benchmarks:
python benchmarks/run.py --checkpasses for every shipped suite (aom_global, pls_regression, matrix).Parity gate (
benchmarks/parity_timing/runner.py): 13 PASS (all external refs), 24 paper-only (smoke-only), 0 numpy-mirror.
Current git notes:
mainis ahead oforigin/mainby ~25 local commits.Untracked files intentionally left out of commits:
Backlog.md,docs/_bench/.AOM-PLS parity oracle is
nirs4all/bench/AOM_v0/aompls, not the packagednirs4alllibrary surface.
Status Summary¶
Done¶
Phase 0 — ABI / build foundation, fixture schema, CLI selfcheck.
Phase 1 — Dependency-free NIPALS PLS regression with fit / predict / transform, fitted-array accessors, serialization.
Phase 3 (a–r) — Preprocessing pipeline (identity, center, autoscale, Pareto, SNV, MSC, EMSC, Detrend, Savitzky-Golay, Norris-Williams, ASLS, Haar wavelet, OSC, EPO), regression + classification metrics + calibration, splitters (k-fold, LOO, holdout, external, repeated, Monte-Carlo, Kennard-Stone, SPXY), cross-validation engine, VIP and selectivity ratio, component-prefix coefficients.
Phase 4 (a–s) — SIMPLS / SVD / PCR / linear kernel / wide-kernel / orthogonal-scores / power / randomized-SVD solvers; PLSCanonical (NIPALS + SVD); PLSSVD; PLS-DA; OPLS / OPLS-DA / multi-response shared predictive score; SIMPLS component-count CV; PLS-LDA; PLS-logistic; MB-PLS; LW-PLS.
Phase 5 (a–u) — Variable-selection family: rangers (VIP, coefficient, selectivity ratio), interval (moving-window, biPLS, siPLS), stability (Monte-Carlo, UVE, EMCUVE, randomization), wrappers (SPA, CARS, Random Frog, SCARS, GA, Shaving, REP, IPW, ST, BVE, T², WVC, WVC threshold).
Phase 6 (a–f) — AOM-PLS / POP-PLS: strict-linear operator family (identity, polynomial detrend, zero-padded Savitzky-Golay, finite difference, Norris-Williams, Whittaker, FCK); global AOM-SIMPLS CV selection; POP-PLS per-component covariance selection; public C ABI for AOM/POP including the
p4a_validation_plan_topaque handle.Phase 7a — Benchmark foundation:
benchmarks/directory, Python ctypes driver, AOM-PLS global benchmark vsnirs4all/bench/AOM_v0/aomplsoracle, gated accuracy CSV + informational timing CSV.Phase 7f (partial shipped) — Native AOM robust-HPO compact/wide preprocessing screen through
n4m_aom_robust_hpo_fit; broader source-free portfolio/gating objects remain Python/reference layer until separately hardened.
Phase 7 - Benchmark Foundation - shipped through 7e¶
Phase 7b: Python ctypes
Modelwrapper + NumPy zero-copyMatrixViewconstructor.Model.fit/predict/transform/get_arraycover all 9 shipped PLS regression solvers. Newbenchmarks/runners/pls_regression.pyrunner (smoke gated; full matrix on demand).Phase 7c: minimum-viable R package at
bindings/r/pls4all/with.Callgateway and fit/predict wrappers for the same solver list. No Rcpp dependency. Documentation for build / install / smoke. R toolchain not installed on this host — package builds against the C ABI on a future host; matrix runner skips R rows gracefully whenRscriptis absent.Phase 7d:
pls4all_cli --benchsubcommand that times native C++ fit + predict per algo on a deterministic synthetic dataset and prints a CSV-parseable row.Phase 7e:
benchmarks/runners/matrix.pyorchestrator. For each (algo, n_samples, n_features) cell it times native C++ / pls4all- Python / pls4all-R / sklearn reference, writes a gated accuracy CSV and an informational timing CSV + summary markdown + metadata JSON. Smoke set is committed; full matrix (9 algos × 5 n × 3 p = 135 cells) is parameterized — re-run under varying CPU pinning when the host is free.Phase 7f (partial shipped): AOM robust-HPO integration. The native compact/wide strict-linear screen is catalogued and wrapped in Python. The broader portfolio/gating layer remains reference-only until it gets native ABI, timing and catalog contracts.
Phase 8 - 15 - PLS extensions (§7, §11, §12, §13, §17, §18, §19)¶
Internal C++ kernels. Public C ABI exposure deferred to the binding tranche.
Phase 8 (§7): Sparse SIMPLS via soft-thresholding;
cfg.sparsity_lambda,P4A_ALGO_SPARSE_PLSdispatch.Phase 9 (§17): T² Hotelling, Q-residuals (SPE), DModX in
cpp/src/core/pls_diagnostics.{hpp,cpp}.Phase 10 (§18): one-SE rule on top of the existing component-count CV.
Phase 11 (§19): empirical percentile thresholds for T² and Q-residuals (
pls_monitoring_fit/pls_monitoring_evaluate).Phase 13 (§13): domain-invariant PLS (
fit_di_pls).Phase 14 (§11): just-in-time PLS — covered by the existing
fit_predict_lw_pls(documented).Phase 15 (§12): recursive PLS — moving-window SIMPLS refit (
cpp/src/core/recursive_pls.{hpp,cpp}).
Phase 16 - 30 - Overview taxonomy completion¶
Closes every remaining Overview section. All internal C++ kernels.
Phase 16 (§8): O2PLS — bi-directional OPLS with predictive + X-orthogonal + Y-orthogonal components.
Phase 17 (§8): SO-PLS — sequential + orthogonalized PLS for B X-blocks predicting Y.
Phase 18 (§8): OnPLS — joint + per-block unique components.
Phase 19 (§8): ROSA — Response-Oriented Sequential Alternation (per-component best-block selection).
Phase 20 (§7): sPLS-DA via dummy encoding + sparse SIMPLS.
Phase 21 (§7): group sparse and fused sparse PLS variants.
Phase 22 (§9): N-PLS (Bro’s algorithm for 3-way tensors).
Phase 24 (§10.2): non-linear kernel PLS (RBF / polynomial / sigmoid) via Gram-matrix dual NIPALS.
Phase 25 (§1): CPPLS / powered PLS (continuous gamma).
Phase 26: weighted PLS, robust PLS (Huber IRLS), ridge PLS, continuum regression.
Phase 27: PLS-GLM, PLS-QDA, PLS-Cox.
Phase 28: PDS (window-LS) and DS calibration transfer, MIR-PLS, missing-aware NIPALS.
Phase 29 (§18): approximate-PRESS with leverage-inflated residual PRESS and component selection.
Phase 30 (§20): bagging-PLS, boosting-PLS, random-subspace PLS.
All shipped as internal kernels in
cpp/src/core/{multiblock_extensions,tensor_pls,kernel_pls,extra_pls}.{hpp,cpp}.
Next (Phase 6 continuation)¶
Phase 6g — POP holdout / approximate-PRESS / one-SE variants; AOM-NIPALS materialized engine; AOM/POP covariance and adjoint fast paths; soft / sparse / superblock AOM selection fixtures; per-block and per-target AOM plans.
Phase 6h — Integrate the locally developed AOM-PLS and FCL-PLS work as first-class pls4all methods.
Later (binding + algorithm tracks)¶
Binding roadmap (after Phase 7c): MATLAB MEX, JS / WebAssembly, Julia, Java / Android, plus secondary targets (C#, Rust, Go, Swift).
Algorithm taxonomy backlog — see the Remaining Algorithm Taxonomy section below.
Next Agent Prompt¶
Continue from /home/delete/nirs4all/pls4all on main, currently tagged
phase-49-vissa-pls (0.97.0+abi.1.14.0). Do not use
GitHub Actions for now. Keep using the local gate: pinned fixture generator,
dev-release build, C++ tests, CLI selfcheck (pls4all_cli --selfcheck),
CLI bench smoke (pls4all_cli --bench --algo pls_simpls --samples 200 --features 100), Python smoke (bindings/python/smoke_aom_pop.py plus
the version/context/config snippet in bindings/python/README.md plus
pls4all.Model.fit/predict round-trip), ABI symbol diff, ldd,
git diff --check, UBSAN and ASAN+UBSAN, and python benchmarks/run.py --check for the benchmark gate. Also run the new external-reference
parity gate PYTHONPATH=bindings/python/src \ parity/python_generator/.venv/bin/python -m benchmarks.parity_timing.runner
and re-commit benchmarks/results/parity_gate/. Leave untracked
Backlog.md and docs/_bench/ alone. For AOM/POP parity, use
/home/delete/nirs4all/nirs4all/bench/AOM_v0/aompls as the oracle.
Algorithm development is now complete for every Overview section. The next agent should:
Public C ABI exposure for the new internal kernels shipped in
pls4all::core::over phases 6g – 30. Plan one MINOR ABI bump per batch (the §6g AOM/POP policy work, the §17/§18/§19 diagnostics and monitoring, the §8/§9/§10.2/§13 algorithm batches, and the §20 ensembles), each additive only. Updatecpp/abi/expected_symbols_linux.txtandcpp/include/pls4all/p4a.h.Python ctypes bindings for the newly exposed surface; extend
bindings/python/src/pls4all/*and the smoke driver.R
.Callgateway for the same surface inbindings/r/pls4all/.MATLAB MEX, JS-WASM, Julia, Java — pick up the remaining binding targets once the Python / R coverage is solid.
Benchmark matrix expansion: add new columns to
benchmarks/runners/matrix.pyfor every shipped method. Runpython benchmarks/run.py --benchmark matrix --scale full --repeats 5underOMP_NUM_THREADS=1|5|10when the host is free.Then start the Acceleration Roadmap (BLAS / OpenMP / CUDA).
For AOM/POP parity, use
/home/delete/nirs4all/nirs4all/bench/AOM_v0/aompls as the oracle. The
pinned parity/python_generator/ venv (sklearn 1.4.2) is still broken
on this host’s Python 3.13; new fixture work should either restore that
venv or migrate to a numpy-only reference path.
Shipped Core (phase log)¶
Phase 0 - ABI and Build Foundation - shipped¶
CMake project, presets, warning/sanitizer options.
Public
p4a.hABI with opaque handles, matrix views, enums, status codes and version/ABI compatibility functions.Context/config/matrix/operator-bank/gating/pipeline/model/array lifecycle.
Shared and static
libp4a; symbol snapshot gate.CLI selfcheck.
Fixture schema and parity infrastructure.
Minimal ctypes Python lifecycle/config binding.
README skeletons for R, MATLAB, JS/WASM and Android.
Phase 1 - PLS CPU Reference - shipped¶
Dependency-free NIPALS PLS1/PLS2 regression.
Fit/predict/transform.
Center/scale learned on train and applied leak-free.
Fitted-array accessors.
Component-prefix coefficients.
Binary export/import with versioned serialization.
Phase 3 - Preprocessing, Validation and Metrics - shipped through 3r¶
Pipeline fit/transform.
Identity, center, autoscale, Pareto, SNV.
MSC, EMSC.
Polynomial detrend.
Savitzky-Golay smoothing and first/second derivatives.
ASLS baseline.
Norris-Williams derivatives.
Haar wavelet denoising.
OSC and EPO.
Regression metrics: RMSE, MAE, bias, R2/Q2, slope/intercept, RPD, RPIQ.
Splitters: k-fold, LOO, holdout, external folds, repeated k-fold, Monte-Carlo, Kennard-Stone, SPXY.
Cross-validation engine.
Binary and multiclass classification metrics plus calibration bins.
VIP and selectivity ratio.
Original-scale component-prefix coefficients.
Phase 4 - Advanced PLS Variants - shipped through 4s¶
SIMPLS.
SVD PLS.
PCR.
Linear kernel-algorithm PLS.
Wide-kernel PLS.
Orthogonal-scores PLS.
Power-method PLS.
Randomized-SVD PLS.
PLSCanonical with NIPALS/SVD.
PLSSVD direct cross-covariance scores.
PLS-DA.
OPLS and OPLS-DA.
Multiclass OPLS-DA/common predictive score model.
SIMPLS component-count CV.
PLS-LDA.
PLS-logistic.
MB-PLS.
LW-PLS.
Phase 5 - Variable Selection - shipped through 5u¶
VIP, coefficient-magnitude and selectivity-ratio rankers.
Moving-window / contiguous interval CV.
biPLS backward interval selection.
siPLS interval-combination search.
Monte-Carlo coefficient stability.
UVE with artificial variables.
SPA-PLS.
CARS-PLS.
Random Frog PLS.
SCARS-PLS.
GA-PLS.
Shaving-PLS.
REP-PLS.
IPW-PLS.
ST-PLS.
BVE-PLS.
T2-PLS.
WVC-PLS numeric selection.
WVC threshold/factor rules.
EMCUVE.
PLS randomization-test selection.
Phase 6 - AOM-PLS and POP-PLS - shipped through 6f¶
Phase 6a: internal soft/hard AOM preprocessing-bank transform primitive.
Phase 6b: internal global AOM-SIMPLS CV selection against
nirs4all/bench/AOM_v0/aomplsfor identity/detrend.Phase 6c: strict-linear AOM kernels for zero-padded Savitzky-Golay, finite difference and Norris-Williams, plus wider global-selection parity.
Phase 6d: strict-linear Whittaker and FCK AOM operators, with direct transform parity and global-selection parity.
Phase 6e: internal POP-PLS per-component SIMPLS covariance selector, with selected operator sequence, component candidate scores, prefix scores, full-fit predictions and bench-compatible CV scoring semantics.
Phase 6f: public C ABI for validation plans, AOM global and POP per- component selection (opaque result handles, typed accessors, hardened fold validation). Python ctypes smoke that drives every shipped AOM/POP fixture through the new surface.
Phase 7 - Benchmark Foundation - shipped through 7a¶
Phase 7a:
benchmarks/directory with a deterministic Python driver. First runner compares the public C ABIp4a_aom_global_selectagainstnirs4all/bench/AOM_v0/aomplson 4 cases (identity-favoured and detrend-favoured). Numerical deltas are gated bypython benchmarks/run.py --check; wall-clock timings are recorded separately and informational.
Active Track¶
Phase 6g - POP/AOM Policy Expansion¶
Goal: complete the policy variants already present in the bench oracle.
Deliverables:
POP holdout / approximate-PRESS / one-SE variants.
AOM-NIPALS materialized path.
AOM/POP covariance and adjoint fast paths where the strict-linear operator contract permits it.
Soft, sparse and superblock AOM selection fixtures.
Per-block and per-target AOM plans.
Reference:
nirs4all/bench/AOM_v0/aompls/selection.pynirs4all/bench/AOM_v0/aompls/simpls.pynirs4all/bench/AOM_v0/aompls/nipals.pynirs4all/bench/AOM_v0/aompls/banks.py
Phase 6h - Local AOM/FCL Integration¶
Goal: integrate the locally developed AOM-PLS and FCL-PLS work as first-class pls4all methods.
Deliverables:
Identify the local source-of-truth implementations and freeze parity fixtures before porting.
Port FCL-PLS kernels behind internal C++ APIs.
Add fixtures for AOM/FCL edge cases, not only synthetic happy paths.
Document numerical conventions and deviations from the local prototypes.
Binding Roadmap¶
Phase 2 is intentionally delayed until the core stabilizes. It is now active in parallel with Phase 7 because the benchmark matrix needs every shipped language binding.
Python (Phase 7b — in progress)¶
Expand ctypes binding beyond lifecycle/config and AOM/POP.
NumPy zero-copy matrix views (
p4a_matrix_view_tfromnumpy.ndarray.ctypes.data, ascontiguousarray fallback).Fit/predict/transform wrappers for every shipped solver (NIPALS, orthogonal-scores, SIMPLS, kernel, wide-kernel, SVD, power, randomized-SVD, plus PCR).
sklearn-compatible
BaseEstimator/RegressorMixin/TransformerMixinclasses (deferred to a later Python tranche).Fixture-driven Python parity tests.
R (Phase 7c — in progress)¶
.Callgateway over the C ABI.R matrices to
p4a_matrix_view_twithout unnecessary copies when possible (viaREAL()on a contiguous matrix).Parity against R
pls,ropls,mixOmics,plsVarSelwhere applicable.CRAN-compatible package skeleton and smoke tests.
MATLAB¶
MEX gateway.
Class wrapper for model handles.
Model import/export.
Prediction-first examples.
JavaScript / WebAssembly¶
Emscripten build of the C ABI.
Predict-first npm package.
TypedArray matrix views.
Model load/predict smoke tests.
Julia¶
Thin
ccallwrapper.Matrix view helpers.
Parity fixture runner.
Java / Android¶
JNI bridge and Kotlin API.
Android AAR.
Predict-first model loading.
On-device smoke tests.
Other bindings to consider after these are stable: C#, Rust, Go and Swift.
Benchmark Roadmap¶
Benchmarks ship under benchmarks/ and follow a strict split:
Accuracy CSVs are committed and gated by
python benchmarks/run.py --check. They contain only deterministic numerical deltas (operator match, component-count match, RMSE / coefficient deltas).Timing CSVs are committed for traceability but NOT gated. They are platform-dependent, recorded with explicit host info (Python version, platform, processor, logical cores, environment variables such as
OMP_NUM_THREADS/OPENBLAS_NUM_THREADS).Summary markdown is regenerated on every run with a status table.
Shipped¶
7a — AOM-PLS global selection vs
nirs4all/bench/AOM_v0/aompls, 4 cases (9x6, 12x8, 16x10, 14x9), Python ctypes path.
Active¶
7b — PLS regression benchmark matrix: 9 solvers (NIPALS / orthogonal-scores / SIMPLS / kernel-algorithm / wide-kernel / SVD / power / randomized-SVD / PCR) × 5 sample sizes (200, 500, 1000, 2000, 10000) × 3 feature counts (100, 1000, 10000), pls4all-Python vs scikit-learn
PLSRegression. CPU-count parameterization through environment variables (run later under 1 / 5 / 10 cores when the host machine is free).7c — R-side numbers added to the same matrix via the minimum R binding shipped in the binding roadmap.
7d — Native C++ baseline column via
pls4all_cli --bench.7e — Orchestrator that runs the full matrix end-to-end, splits outputs per language and per core count, regenerates the summary markdown.
7f (partial shipped) — Native AOM robust-HPO compact/wide screen shipped; broader portfolio/gating layer remains reference-only.
Later¶
POP per-component benchmark column.
AOM-PLS benchmark expanded to the wider operator bank (SG, Norris- Williams, Whittaker, FCK).
Preprocessing throughput vs NumPy / SciPy references.
Variable-selection runtime vs Python / R references.
LW-PLS and MB-PLS scaling.
Batch CV, bootstrap, CARS / MCUVE / AOM sweeps.
Memory footprint and dependency audit per build.
GPU / OpenMP / BLAS backend self-parity once the Acceleration Roadmap starts.
Benchmark outputs live under benchmarks/results/; only curated CSVs and
summaries are committed.
Acceleration Roadmap¶
Acceleration remains optional and must not change the C ABI.
BLAS backend.
OpenMP for fold/bootstrap/operator scans.
Batch APIs for large validation and selection workloads.
CUDA backend in a separate optional shared library.
Later: WebGPU/WASM SIMD if the JS target needs it.
Every accelerated backend needs self-parity against reference CPU.
Remaining Algorithm Taxonomy¶
These are not yet first-class shipped methods and should be split into small reviewed phases with parity references:
CPPLS / powered PLS.
Weighted and sample-weighted PLS.
Robust PLS.
Ridge/regularized/penalized PLS.
Continuum regression.
MIR-PLS.
Sparse PLS and sparse PLS-DA.
PLS-QDA, PLS-GLM, PLS-Cox and survival PLS.
O2PLS, DOSC-PLS, OnPLS.
Missing-aware NIPALS.
Calibration transfer and domain adaptation: PDS, DS, di-PLS and related methods.
Multiway/tensor PLS: N-PLS, Tri-PLS, PARAFAC-PLS, Tucker-PLS.
Dynamic, recursive and online PLS.
Ensemble PLS methods: bagging, boosting and random-subspace PLS.
Release Discipline¶
For every phase:
Add or update a focused
roadmap/phase-*.md.Implement the smallest coherent tranche.
Generate parity fixtures with the pinned generator venv.
Build with
/home/delete/.venv/bin/cmake --build --preset dev-release --parallel.Run
/home/delete/.venv/bin/ctest --preset dev-release --output-on-failure.Run
build/dev-release/cpp/tests/pls4all_tests.Run CLI selfcheck and Python smoke.
Run ABI symbol diff and
ldddependency audit.Run
git diff --check.Run UBSAN and ASAN+UBSAN local builds.
For benchmark phases, run
python benchmarks/run.py --check.Commit as
release(phase-X): version-topic.Tag as
phase-X-topic.
Do not push unless explicitly asked.