nirs4all model → n4m/pls4all estimator mapping (G3 prerequisite)¶
Status: authoritative mapping table for map-model-classes
(RELEASE_READINESS.md G3, line 401). This is the prerequisite the release
audit names for G3 — drop-in integration of n4m into nirs4all.
Scope: every model class exported by
nirs4all/nirs4all/operators/models/sklearn/__init__.py __all__, mapped to
its pls4all.sklearn (or full-n4m) counterpart, with a per-class verdict.
Every verdict below is backed by code that was read; file:line citations are
inline. Citations against nirs4all-methods are repo-relative to that repo;
citations against nirs4all are repo-relative to the nirs4all repo.
1. Dependency surface¶
There are two distinct PyPI distributions under nirs4all-methods, and
they do not overlap in what they expose:
dist (PyPI) |
import package |
what it exposes |
model fits? |
|---|---|---|---|
|
|
the PLS models, classifiers, selectors, AOM/POP selection, calibration transfer, diagnostics |
YES — all PLS |
|
|
preprocessing, filters, augmenters, splitters, interval generators, transfer (DS/PDS) |
NO model fits |
All the model estimators (
PLSRegression,OPLSRegression,PCR,SparsePLSRegression,PLSDAClassifier,MBPLSRegression,DIPLSRegression, the_in_sample.pyfamily, …) live inpls4all, notn4m. They are wired inpls4all/sklearn/__init__.py:50-148and the fit FFI lives inpls4all/_methods.pyandpls4all/_model.py.The full
n4mpackage’s FFI (bindings/python/src/n4m/_ffi_decls.py) declares onlyn4m_pp_*(preprocessing),n4m_filter_*,n4m_interval_generator_*, and augmenter*_fit/*_is_fittedsymbols. It has non4m_method_result_*, non4m_pls_fit/n4m_model_*, and no per-model*_fit.n4m/python.pyis entirely preprocessing/filter/augmenter transforms (n4m/python.py:134transform(),:441,:479,:631, …).
⚠️ LOAD-BEARING FLAG: the FULL
n4mpackage has no method-result FFI plumbingA “swap nirs4all models onto the full
n4mpackage” is not possible today, even for the drop-in rows below. The PLS model surface (MethodResult,Model,*_fit) exists only in the slimpls4allpackage. The fulln4mPython binding would need the method-result / model FFI subsystem built first before any model class could be served from it. Every “drop-in”/”adapter-needed” verdict below therefore assumes the target ispls4all.sklearn. If the integration is intended to ride on the fulln4mpackage (so preprocessing + models come from one import), that FFI port is an additional, currently-missing prerequisite. This is consistent with RELEASE_READINESS.md G3consolidate-pypi-packaging(line 415): “models live in distpls4all, preprocessing in distnirs4all-methods; a clean drop-in needs both”.
nirs4all currently has ZERO n4m/pls4all references (greenfield). Verified
by grepping the whole nirs4all tree for import n4m / from n4m /
import pls4all / from pls4all: 0 matches (the only pls4all-shaped
hits are the unrelated internal pls4all_core token). So the swap is
config/instance-level (nirs4all resolves models by class-path string or
instance), not a rewrite — matching RELEASE_READINESS.md G3 line 397-398.
2. Reproducibility caveat (stochastic methods are NOT bit-reproducible)¶
n4m’s stochastic kernels (bagging, boosting, random-subspace, GPR, and the
randomized selectors CARS/RandomFrog/GA/PSO/VISSA/IRIV/…) seed a PCG64
stream with explicit integer seeds. nirs4all seeds the numpy global RNG.
These are different generators, so any stochastic swap will not bit-reproduce
nirs4all’s previous outputs — only statistical equivalence can be claimed.
Verified: PRODUCTION_AUDIT.md:372 (“nirs4all’s numpy RNG ≠ n4m’s PCG64”) and
benchmarks/cross_binding/donor_ops.py declares value_parity=False
(“stochastic — nirs4all RNG differs”) for these. RELEASE_READINESS.md
seeding-reproducibility-note (line 428-430) tracks the same caveat. The
deterministic PLS/PCR/SIMPLS/OPLS/sparse rows are unaffected (they match the
oracle at ~1e-15).
3. Recommended integration mechanism¶
Two mechanisms are viable; the delegating shim is recommended for the drop-in / adapter-needed rows.
(A) Delegating shim (recommended). Keep each nirs4all model class at its
existing import path (nirs4all.operators.models.sklearn.<Name>) and have its
fit/predict delegate to the pls4all.sklearn estimator. The shim must:
preserve the import path (controllers and
.n4a/webapp configs resolve models by class-path string);preserve
get_paramsarg names — nirs4all usesn_components,scale/center,backend,solver-free constructors, whilepls4allusescenter_x/scale_x/center_y/scale_y/solver. The shim’s__init__keeps the nirs4all names and translates insidefit(see the per-row “constructor-arg mapping” column);preserve
_estimator_type(declared on 17 of the 19 model files;StackingRegressor/StackingClassifierrely on it) and_webapp_meta(the studio UI catalog reads it — 19 model files carry one).
This is adapter-shim-or-repoint in RELEASE_READINESS.md G3 (line 406-409),
which explicitly notes “a naive import-path swap won’t work” because the API
signatures differ.
(B) Config references. Alternatively reference pls4all.sklearn.PLSRegression
(etc.) directly in pipeline configs and drop the nirs4all wrapper. Simpler, but
loses the nirs4all get_params names, _webapp_meta, and any nirs4all-specific
predict_proba/decode behaviour; not recommended where the studio UI or
StackingRegressor introspection matters.
4. Mapping table (one row per nirs4all model class)¶
Constructor-arg mapping uses the convention nirs4all → n4m/pls4all. “—” = no equivalent arg / not applicable.
nirs4all class |
n4m/pls4all target |
constructor-arg mapping (nirs4all → n4m) |
verdict |
notes |
|---|---|---|---|---|
PLSDA |
|
|
adapter-needed |
nirs4all |
IKPLS |
|
|
adapter-needed |
IKPLS is the Dayal–MacGregor fast kernel; n4m has no |
OPLS |
|
|
adapter-needed |
RELEASE_READINESS lists OPLS as “1:1 confirmed” (line 402). Verdict adapter-needed because nirs4all splits |
OPLSDA |
|
|
adapter-needed |
nirs4all |
PCR |
|
|
drop-in |
nirs4all PCR = |
MBPLS |
|
|
adapter-needed |
RELEASE_READINESS “1:1 confirmed” (line 402). Adapter-needed because n4m |
DiPLS |
no equivalent (semantic mismatch) → |
|
keep-native |
Name collision / wrong method. nirs4all |
SparsePLS |
|
|
adapter-needed |
RELEASE_READINESS “1:1 confirmed” (line 402). nirs4all |
LWPLS |
|
|
blocked |
Genuinely local. n4m |
SIMPLS |
|
|
adapter-needed |
Mappable-by-param (RELEASE_READINESS line 403). |
IntervalPLS |
no regressor equivalent (n4m |
|
keep-native |
nirs4all |
RobustPLS |
|
|
adapter-needed |
NOT blocked. The C ABI |
RecursivePLS |
|
|
blocked |
Different algorithm + no coef. nirs4all |
KOPLS |
no equivalent (n4m has KernelPLS, not Kernel-OPLS) |
|
keep-native |
n4m |
KernelPLS (from |
|
|
blocked |
Mappable-by-param (RELEASE_READINESS line 403) but in-sample-only: n4m |
NLPLS |
|
same as KernelPLS above ( |
blocked |
NLPLS and KernelPLS are the same |
KPLS (alias from |
|
same as KernelPLS |
blocked |
Alias of the |
OKLMPLS |
no equivalent |
|
keep-native |
Koopman-latent-map dynamic PLS with a pluggable |
FCKPLS |
no equivalent |
|
keep-native |
Fractional convolutional-kernel PLS ( |
AOMPLSRegressor |
no equivalent (paper companion) |
bank/operator args → ✗ |
keep-native |
Vendored |
POPPLSRegressor |
no equivalent (paper companion) |
bank args → ✗ |
keep-native |
Vendored |
AOMPLSAomlibRegressor |
no equivalent |
|
keep-native |
Wraps the external |
AOMPLSClassifier |
no equivalent (paper companion) |
— |
keep-native |
Vendored |
POPPLSClassifier |
no equivalent (paper companion) |
— |
keep-native |
Vendored |
AOMRidge family ( |
partial equivalent: native |
— |
keep-native except covered blender subset |
Vendored |
FastAOM family ( |
no equivalent (paper companion) |
— |
keep-native |
All vendored |
TabPFNNIRSRegressor |
no equivalent |
|
keep-native |
Foundation-model regressor ( |
The model
__init__.py__all__also exports a large set of AOM operators and banks (LinearSpectralOperator,SavitzkyGolayOperator,WhittakerOperator,ComposedOperator,default_bank,compact_bank, …) and FastAOM internals. These are not estimators (nofit/predictmodel contract) — they are preprocessing operators / config helpers and are out of scope for the model mapping. (Some have n4m preprocessing counterparts in then4mpackage, but that is the preprocessing-parity surface, not model G3.)
5. Verdict counts¶
Tally over the 42 estimator names in nirs4all’s
operators/models/sklearn/__init__.py __all__ (operators/banks excluded;
KernelPLS/NLPLS/KPLS are one underlying method exported under 3 names; the
AOMRidge family is 10 names, FastAOM 6 names):
verdict |
count |
classes |
|---|---|---|
drop-in |
1 |
PCR |
adapter-needed |
8 |
PLSDA, IKPLS, OPLS, OPLSDA, MBPLS, SparsePLS, SIMPLS, RobustPLS |
blocked |
5 |
LWPLS, RecursivePLS, KernelPLS, NLPLS, KPLS |
keep-native |
28 |
DiPLS, IntervalPLS, KOPLS, OKLMPLS, FCKPLS (+ |
(1 + 8 + 5 + 28 = 42, matching the estimator names in __all__; the additional
__all__ entries beyond these 42 are AOM operators/banks, not
estimators — see the note under the table.)
RobustPLS is in adapter-needed, not blocked, because its C ABI already
exports coefficients (pls.h:773-777) — the only work is a pure-Python wrapper
change (RELEASE_READINESS.md in-sample-predict-coef-export, line 410-414).
6. nirs4all classes with NO n4m equivalent (keep-native)¶
DiPLS (dynamic/time-series — not the domain-invariant
DIPLSRegression; name collision)IntervalPLS (as a regressor; n4m only has the selector)
KOPLS (Kernel-OPLS)
OKLMPLS (Koopman-latent dynamic PLS)
FCKPLS (+
FractionalPLS,FractionalConvFeaturizer)AOMPLSRegressor, POPPLSRegressor, AOMPLSAomlibRegressor, AOMPLSClassifier, POPPLSClassifier
AOMRidge family (10 classes)
FastAOM family (6 classes)
TabPFNNIRSRegressor
7. Concrete follow-up tasks (per class)¶
7a. Requires NEW n4m / C-ABI work (cannot be done in pure Python)¶
LWPLS — needs a C-ABI predict-on-new-X.
lw_pls_fitis local (neighbor_indices+ per-sample fits, no global coef,pls.h:1342-1346). Add a C entry-point that takes (X_train, y_train, X_new) and runs the per-query local fit at predict time. (in-sample-predict-coef-export/ the “genuinely local” half of RELEASE_READINESS line 413.)KernelPLS / NLPLS / KPLS — needs kernel-centering export.
kernel_pls_fitexportsalpha+y_meanbut not the training-row/global-mean kernel-centering statistics (pls.h:869-873, wrapper_method_result_regressors.py:367-380). Add those to the C result soK(X_new, X_train)can be centered consistently.RecursivePLS — needs a forgetting-factor RPLS that exports a global coef (n4m’s
recursive_pls_runis window-based and prediction-only,pls.h:728-731). Different algorithm; either port nirs4all’s EW-RPLS into the C core or keep native.PLSDA / OPLSDA —
predict_probaon the n4m classifier wrappers. n4mPLSDAClassifier/OPLSDAClassifierexpose onlydecision_function/predict(_classification.py:119-132,:135). nirs4all exposespredict_proba(plsda.py:55,oplsda.py:159). Either addpredict_proba+classes_-proba to the n4m wrapper (pure-Python, no C work — it’s a column-stack/raw-score transform) or have the shim synthesize it. (RELEASE_READINESSplsda-predict-proba, line 421.) This one is pure-Python despite living in this subsection because it touches the n4m wrapper.
7b. Pure-Python shim work (no n4m engine change)¶
RobustPLS — read coefficients in the wrapper.
n4m_robust_pls_fitalready exportscoefficients/x_mean/y_mean(pls.h:773-777); promoteRobustPLSRegressionfrom the in-sample contract (_in_sample.py:146) to a_MethodResultRegressor(the pattern in_method_result_regressors.py). Also fix the stale docstring at_method_result_regressors.py:12-16that wrongly saysrobust_pls_fit/ridge_pls_fit/continuum_regression_fit“expose only predictions” — the C ABI exports coefficients for all three (pls.h:773-804). RidgePLS and ContinuumRegression are the same pure-Python fix (not in nirs4all’s__all__, but relevant if added).PCR — straight delegating shim. Translate
n_componentsonly; pass through. (drop-in.)SIMPLS — arg-name shim. Map
center→center_x/center_y,scale→scale_x/scale_y; targetPLSRegression(solver='simpls').OPLS / OPLSDA — two-knob→one-knob shim. Collapse
n_components(ortho)+pls_components(predictive) into the single n4mn_components; choose a convention and parity-check.MBPLS — derive
block_sizes. nirs4all accepts list-of-blocks X; the n4m target mandatesblock_sizes(_method_result_regressors.py:157-167). Shim computes block sizes from the block list / config.SparsePLS — translate
alpha→sparsity_lambdaand parity-check (scales differ).IKPLS — map
center/scaleand pick a solver ('kernel'/'wide-kernel'); document thatalgorithm=1/2andbackend='jax'have no n4m analogue.
7c. Packaging / cross-cutting¶
Build the method-result FFI in the full
n4mpackage (prerequisite for any swap onton4mrather thanpls4all) — see §1 flag. Until then, all drop-in/adapter targets must import frompls4all.sklearn.Packaging decision (
consolidate-pypi-packaging, RELEASE_READINESS line 415): one dist re-exporting bothn4m+pls4all, or two pinned dists — each bundles its own libn4m; verified not a double-free risk, only doubled payload.Reproducibility note + statistical-equivalence test for stochastic swaps (PCG64 vs numpy global RNG; §2).
X_val/y_valplumbing note: onlyAOMPLSAomlibRegressorcarriesX_val/y_valinfittoday (and ignores them); n4m modelfit(X, y)has no such kwarg and would silently drop them. No action needed unless a mapped class starts consuming them (fit-val-kwarg-support, RELEASE_READINESS line 431)._estimator_typegap: PCR and TabPFNNIRSRegressor (and OPLSDA on the nirs4all side) don’t declare_estimator_type; the delegating shim should set it explicitly to keep StackingRegressor/StackingClassifier introspection working.
8. Correctness concerns surfaced while building this map¶
DiPLSis a name collision, not a mapping. RELEASE_READINESS.md line 402 lists “DiPLS” under “1:1 confirmed”, but nirs4allDiPLSis Dynamic PLS (timelags, viatrendfitter,dipls.py:73) while pls4allDIPLSRegressionis Domain-Invariant PLS (Nikzad-Langerodi, needsX_target,_method_result_regressors.py:101). Mapping them would silently swap one algorithm for an unrelated one. → keep-native (or rename to avoid future confusion).Stale wrapper docstring overstates the in-sample limitation.
pls4all/sklearn/_method_result_regressors.py:12-16claimsrobust_pls_fit,ridge_pls_fit, andcontinuum_regression_fit“expose only predictions … not coefficients”. The C ABI contradicts this: all three exportcoefficients/x_mean/y_mean(pls.h:773-804). The corresponding_in_sample.pywrappers are therefore unnecessarily blocked; this directly affects the RobustPLS verdict (adapter-needed, not blocked).The full
n4mpackage cannot serve any model today (no method-result FFI). Every drop-in/adapter target resolves topls4all.sklearn. A single-import “preprocessing + models fromn4m” story needs the FFI port first. (§1 flag.)