aom_pls — AOM-PLS (global adaptive operator selection)¶
Group: Adaptive · Registry tolerance: 1e-08
Description¶
AOM-PLS — global adaptive operator selection
Registry note — Global AOMPLS/AOM-PLS selector with the compact strict-linear nirs4all bank: identity, Savitzky-Golay smooth/derivative, detrend and finite-difference operators. Reference is the in-tree nirs4all estimator stack; parity remains qualitative because selection tie-breaking and CV scoring details differ across implementations.
Parameters¶
Name |
Type |
Default |
Notes |
|---|---|---|---|
|
|
|
current public binding signature |
|
|
|
current public binding signature |
|
|
|
current public binding signature |
|
|
|
current public binding signature |
|
|
|
current public binding signature |
|
|
|
current public binding signature |
|
|
|
current public binding signature |
|
|
|
current public binding signature |
|
|
|
current public binding signature |
|
|
|
current public binding signature |
|
|
|
registry benchmark cell value |
Explanations¶
Bibliographic source¶
Beurier, G., Reiter, R., Noûs, C., Rouan, L. & Cornet, D. (2026). Reframing preprocessing selection as model-internal calibration in near-infrared spectroscopy: a large-scale benchmark of operator-adaptive PLS and Ridge models. arXiv:2605.13587. https://arxiv.org/abs/2605.13587.
Mathematical principle¶
AOM-PLS treats spectroscopic preprocessing as a learnable step inside the PLS calibration. Let \(\mathbf{X} \in \mathbb{R}^{n\times p}\) be the centered spectral matrix (rows = samples), \(\mathbf{Y} \in \mathbb{R}^{n\times q}\) the centered response, and \(\{\mathbf{A}_b\}_{b=1}^{B} \subset \mathbb{R}^{p\times p}\) a finite bank of strict-linear spectral operators. An operator is strict-linear when its action \(\mathbf{X}_b = \mathbf{X}\mathbf{A}_b^{\top}\) depends only on the fixed wavelength grid (identity, Savitzky–Golay smoothing and derivatives, finite differences, polynomial detrending, Norris–Williams gap derivatives, Whittaker smoothing); SNV, MSC, EMSC, ASLS and OSC are not strict-linear and are handled as fold-local branches in nirs4all.
Cross-covariance identity (Eq. 2 of the paper). For centered \(\mathbf{X}, \mathbf{Y}\) and any strict-linear \(\mathbf{A}\),
Writing \(\mathbf{S} = \mathbf{X}^{\top}\mathbf{Y}\), every operator can therefore be screened by the cheap left action \(\mathbf{S}_b = \mathbf{A}_b\mathbf{S}\) (\(O(p q)\) per candidate) instead of materializing \(\mathbf{X}_b\) (\(O(n p)\)).
Global selection (the AOM in AOM-PLS). A single operator index \(b^{\star}\) is chosen for all \(K\) components by minimising a selection criterion \(\mathcal{C}\) over \(b\):
The default criterion is K-fold CV-RMSE (criterion='cv'); alternatives include the covariance proxy \(-\lVert\mathbf{A}_b\mathbf{S}\rVert\) (fast prescreen), leverage-corrected approximate PRESS, and a hybrid covariance-then-CV refinement. The optimal prefix length \(k \le K\) is selected jointly when auto_prefix=True.
SIMPLS-covariance engine. With \(\mathbf{S}_b = \mathbf{A}_b\mathbf{S}\) precomputed, SIMPLS extracts component \(a\) from the leading left singular vector \(\mathbf{r}_a = \mathbf{u}_1(\mathbf{S}_b)\) and maps it back to the original wavelength grid through the operator’s adjoint:
Stacking \(\mathbf{Z} = [\mathbf{z}_1\;\cdots\;\mathbf{z}_K]\), with original-space loadings \(\mathbf{P} = \mathbf{X}^{\top}\mathbf{T}\,\operatorname{diag}(1/\lVert\mathbf{t}_a\rVert^{2})\) and \(\mathbf{Q} = \mathbf{Y}^{\top}\mathbf{T}\,\operatorname{diag}(1/\lVert\mathbf{t}_a\rVert^{2})\), the recovered coefficient matrix is
Because \(\mathbf{B}\) lives in the original feature space, the fitted model is a single linear calibration on the raw wavelength grid: there is no preprocessing stage to replay at predict time — the operator has been absorbed into the coefficients (paper §3.2). Computationally the bank exploration cost is roughly that of a single SIMPLS fit on \(\mathbf{S}\) plus \(B\) tiny left actions, which is the algorithmic gain that makes AOM-PLS comparable to vanilla PLS even with a \(\sim\)77-operator default bank.
Appropriate uses¶
Joint automated choice of preprocessing and PLS complexity for a defined calibration task.
Limits and validation¶
The selected pipeline is conditional on the candidate grid and validation split and needs an untouched external assessment.
Implementation¶
n4m_model_selection_aom_pls_select via the Python/R/MATLAB dispatchers. The compact strict-linear bank shipped by pls4all (compact_bank: identity, Savitzky–Golay smooth/derivative, polynomial detrending, finite differences) mirrors the default_bank used in the paper experiments. Reference: git-pinned oracle nirs4all.operators.models.sklearn.aom_pls.AOMPLSRegressor (sanctioned exception).
Sources and provenance¶
Current implementation: cpp/src/core/aom_preprocessing.cpp.
API and bindings¶
C ABI (ABI 2): n4m_model_selection_aom_pls_result_destroy · n4m_model_selection_aom_pls_result_get_best_score · n4m_model_selection_aom_pls_result_get_coefficients · n4m_model_selection_aom_pls_result_get_input_coefficients · n4m_model_selection_aom_pls_result_get_intercept · n4m_model_selection_aom_pls_result_get_max_components · n4m_model_selection_aom_pls_result_get_n_operators · n4m_model_selection_aom_pls_result_get_operator_kinds · n4m_model_selection_aom_pls_result_get_operator_scores · n4m_model_selection_aom_pls_result_get_predictions · n4m_model_selection_aom_pls_result_get_rmse_curves · n4m_model_selection_aom_pls_result_get_selected_n_components · n4m_model_selection_aom_pls_result_get_selected_operator_index · n4m_model_selection_aom_pls_select. Use the linked public header for the exact signature, configuration, and result handles.
Python (verified public re-export):
from n4m.model_selection.aom_search import aom_global_select
result = aom_global_select(X, y)
Source signature: aom_global_select(X, y, *, max_components: int = 3, operators = None, cv: int = 3, fold_ids = None, center_x: bool | None = None, scale_x: bool | None = None, center_y: bool | None = None, scale_y: bool | None = None) (n4m/_impl/native.py).
R (source-verified): aom_pls(X, Y, max_components = 3L, n_operators = 9L, cv = 3L).
library(n4m)
result <- aom_pls(X, Y)
MATLAB / Octave (source-verified): aom_pls(X, Y, max_components, n_operators, cv).
The source signature has additional required inputs, so no example call is fabricated.
Registry parity references 📐
📐
nirs4all(python · python) —nirs4allin-tree · strict (rmse_rel ≤ 1e-08) — In-tree nirs4all AOM/POP estimator stack (sanctioned reference). The pls4all ABI uses the same compact strict-linear bank and contiguous folds for cross-binding determinism; nirs4all remains the qualitative algorithmic reference.
Benchmarks¶
Archived measurement identity. Backend labels in this table are the raw IDs recorded when the benchmark ran (including historical pls4all.* IDs). They preserve measurement provenance and do not describe a current public Python, R, or MATLAB binding; use the source-verified API and bindings section above for current entry points.
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 = archived binding-harness result 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-08).
Rows tagged with 📐 are the canonical parity references for this method (declared in parity_timing.registry). C++ and external rows show reference parity; archived language-harness rows show binding parity against the C++ backend. Hover the icon for role and tolerance band.
| Backend | Parity | 200×40 (ms) |
|---|---|---|
| C++ native · libn4m | ||
pls4all.cpp.blas+omp | ✓ ref 6e-16 | 4.20 ms |
| Python · archived pls4all benchmark | ||
pls4all.python | ✓ bind | 4.16 ms |
pls4all.sklearn | ✓ bind | 4.15 ms🏆 |
| R · archived pls4all benchmark | ||
pls4all.R | ✓ 6e-15 | 7.84 ms |
pls4all.R.formula | ✓ 6e-15 | 9.43 ms |
pls4all.R.mdatools | ✓ 6e-15 | 17.6 ms |
pls4all.R.pls | ✓ 6e-15 | 9.87 ms |
| Python · external | ||
📐nirs4all | source | 41.9 ms |
| Backend | Parity | 200×40 (ms) |
|---|---|---|
| C++ native · libn4m | ||
pls4all.cpp.blas+omp | ✓ ref 6e-16 | 14.6 ms |
| Python · archived pls4all benchmark | ||
pls4all.python | ✓ bind | 8.48 ms🏆 |
pls4all.sklearn | ✓ bind | 14.7 ms |
| R · archived pls4all benchmark | ||
pls4all.R | ✓ 6e-15 | 27.0 ms |
pls4all.R.formula | ✓ 6e-15 | 40.4 ms |
pls4all.R.mdatools | ✓ 6e-15 | 50.0 ms |
pls4all.R.pls | ✓ 6e-15 | 47.9 ms |
| Python · external | ||
📐nirs4all | source | 59.7 ms |
| Backend | Parity | 200×40 (ms) |
|---|---|---|
| C++ native · libn4m | ||
pls4all.cpp.blas+omp | ✓ ref 6e-16 | 23.7 ms |
| Python · archived pls4all benchmark | ||
pls4all.python | ✓ bind | 23.7 ms |
pls4all.sklearn | ✓ bind | 15.5 ms |
| R · archived pls4all benchmark | ||
pls4all.R | ✓ 6e-15 | 13.4 ms |
pls4all.R.formula | ✓ 6e-15 | 16.4 ms |
pls4all.R.mdatools | ✓ 6e-15 | 12.9 ms |
pls4all.R.pls | ✓ 6e-15 | 12.4 ms🏆 |
| Python · external | ||
📐nirs4all | source | 16.6 ms |
See also: benchmark overview · methods index · interactive dashboard