pls4all 0.97.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. pls4all-0.97.0/LICENSE +52 -0
  2. pls4all-0.97.0/MANIFEST.in +7 -0
  3. pls4all-0.97.0/PKG-INFO +89 -0
  4. pls4all-0.97.0/README.md +58 -0
  5. pls4all-0.97.0/pyproject.toml +60 -0
  6. pls4all-0.97.0/setup.cfg +4 -0
  7. pls4all-0.97.0/setup.py +41 -0
  8. pls4all-0.97.0/src/pls4all/__init__.py +212 -0
  9. pls4all-0.97.0/src/pls4all/_aom.py +428 -0
  10. pls4all-0.97.0/src/pls4all/_config.py +154 -0
  11. pls4all-0.97.0/src/pls4all/_context.py +99 -0
  12. pls4all-0.97.0/src/pls4all/_errors.py +24 -0
  13. pls4all-0.97.0/src/pls4all/_ffi.py +431 -0
  14. pls4all-0.97.0/src/pls4all/_methods.py +2235 -0
  15. pls4all-0.97.0/src/pls4all/_model.py +299 -0
  16. pls4all-0.97.0/src/pls4all/_types.py +79 -0
  17. pls4all-0.97.0/src/pls4all/sklearn/__init__.py +188 -0
  18. pls4all-0.97.0/src/pls4all/sklearn/_base.py +228 -0
  19. pls4all-0.97.0/src/pls4all/sklearn/_classification.py +166 -0
  20. pls4all-0.97.0/src/pls4all/sklearn/_classifiers_extras.py +326 -0
  21. pls4all-0.97.0/src/pls4all/sklearn/_diagnostics.py +277 -0
  22. pls4all-0.97.0/src/pls4all/sklearn/_in_sample.py +471 -0
  23. pls4all-0.97.0/src/pls4all/sklearn/_method_result.py +115 -0
  24. pls4all-0.97.0/src/pls4all/sklearn/_method_result_regressors.py +436 -0
  25. pls4all-0.97.0/src/pls4all/sklearn/_regression.py +334 -0
  26. pls4all-0.97.0/src/pls4all/sklearn/_selection.py +1170 -0
  27. pls4all-0.97.0/src/pls4all/sklearn/_transformers.py +115 -0
  28. pls4all-0.97.0/src/pls4all.egg-info/PKG-INFO +89 -0
  29. pls4all-0.97.0/src/pls4all.egg-info/SOURCES.txt +33 -0
  30. pls4all-0.97.0/src/pls4all.egg-info/dependency_links.txt +1 -0
  31. pls4all-0.97.0/src/pls4all.egg-info/requires.txt +1 -0
  32. pls4all-0.97.0/src/pls4all.egg-info/top_level.txt +1 -0
  33. pls4all-0.97.0/tests/test_sklearn_regressors.py +247 -0
  34. pls4all-0.97.0/tests/test_sklearn_selectors.py +172 -0
  35. pls4all-0.97.0/tests/test_sklearn_slice.py +333 -0
pls4all-0.97.0/LICENSE ADDED
@@ -0,0 +1,52 @@
1
+ CeCILL FREE SOFTWARE LICENSE AGREEMENT
2
+ Version 2.1 dated 2013-06-21
3
+
4
+ Copyright (c) 2026 Grégory Beurier and contributors.
5
+
6
+ Notice
7
+
8
+ This Agreement is a Free Software license agreement that is the result
9
+ of discussions between its authors in order to ensure compliance with
10
+ the two main principles guiding its drafting:
11
+
12
+ * firstly, compliance with the principles governing the distribution
13
+ of Free Software: access to source code, broad rights granted to users,
14
+ * secondly, the election of a governing law, French law, with which it
15
+ is conformant, both as regards the law of torts and intellectual
16
+ property law, and the protection that it offers to both authors and
17
+ holders of the economic rights over software.
18
+
19
+ The authors of the CeCILL (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre])
20
+ license are:
21
+
22
+ Commissariat à l'Energie Atomique et aux Energies Alternatives - CEA,
23
+ a public scientific, technical and industrial research establishment,
24
+ having its principal place of business at 25 rue Leblanc, immeuble Le
25
+ Ponant D, 75015 Paris, France.
26
+
27
+ Centre National de la Recherche Scientifique - CNRS, a public scientific
28
+ and technological establishment, having its principal place of business
29
+ at 3 rue Michel-Ange, 75794 Paris cedex 16, France.
30
+
31
+ Institut National de Recherche en Informatique et en Automatique - Inria,
32
+ a public scientific and technological establishment, having its principal
33
+ place of business at Domaine de Voluceau, Rocquencourt, BP 105, 78153 Le
34
+ Chesnay cedex, France.
35
+
36
+ Full license text: https://cecill.info/licences/Licence_CeCILL_V2.1-en.txt
37
+ (or https://cecill.info/licences/Licence_CeCILL_V2.1-fr.txt for the French version)
38
+
39
+ This software is provided AS IS, without warranty of any kind, express or
40
+ implied. See the CeCILL-2.1 text for the complete legal terms.
41
+
42
+ ---
43
+
44
+ Vendored third-party components (test-only or build-only - not linked
45
+ into the public `libp4a` shared library):
46
+
47
+ * doctest (cpp/tests/doctest/doctest.h) - MIT License
48
+ Copyright (c) 2016-2024 Viktor Kirilov
49
+ Used to compile the test executable. Not part of any shipped artefact.
50
+
51
+ Additional vendored components will be listed here as they are added,
52
+ each with their license and the path within the repository.
@@ -0,0 +1,7 @@
1
+ # Include the native shared library so the sdist + wheel ship libp4a
2
+ # even though setuptools' default discovery skips arch-specific files.
3
+ recursive-include src/pls4all/lib *
4
+
5
+ # Include the package README and local license notice.
6
+ include README.md
7
+ include LICENSE
@@ -0,0 +1,89 @@
1
+ Metadata-Version: 2.4
2
+ Name: pls4all
3
+ Version: 0.97.0
4
+ Summary: Python binding for the pls4all C ABI — a portable PLS / NIRS engine.
5
+ Author: Grégory Beurier and contributors
6
+ License-Expression: CECILL-2.1
7
+ Project-URL: Homepage, https://github.com/GBeurier/pls4all
8
+ Project-URL: Repository, https://github.com/GBeurier/pls4all
9
+ Project-URL: Issues, https://github.com/GBeurier/pls4all/issues
10
+ Project-URL: Documentation, https://gbeurier.github.io/pls4all/
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Operating System :: Microsoft :: Windows
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: Implementation :: CPython
23
+ Classifier: Topic :: Scientific/Engineering
24
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
25
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
26
+ Requires-Python: >=3.10
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: numpy>=1.21
30
+ Dynamic: license-file
31
+
32
+ # Python binding
33
+
34
+ The current Python package ships a minimal **ctypes-only** binding that:
35
+
36
+ - loads `libp4a.{so,dll,dylib}` via `ctypes.CDLL`,
37
+ - exposes the version / status / dtype / backend introspection queries,
38
+ - exposes a Pythonic `Context` and `Config` wrapper for the lifecycle calls,
39
+ - raises a typed exception (`Pls4allError`) on any non-OK status, capturing the per-context `last_error` message.
40
+
41
+ A real wheel with NumPy zero-copy `p4a_matrix_view_t` conversion + a
42
+ sklearn-compatible estimator lands in Phase 2 on top of the live C core.
43
+
44
+ ## Build the underlying library
45
+
46
+ ```bash
47
+ cmake --preset dev-release
48
+ cmake --build --preset dev-release --parallel
49
+ ```
50
+
51
+ This produces `build/dev-release/cpp/src/libp4a.so` (or `.dylib` / `.dll`).
52
+ The binding looks for it on disk via the loader rules below.
53
+
54
+ ## Loader rules
55
+
56
+ In order:
57
+
58
+ 1. `$PLS4ALL_LIB_PATH` — explicit path to `libp4a` (most direct).
59
+ 2. `pls4all/lib/libp4a*` next to the installed package (wheel layout).
60
+ 3. `<repo-root>/build/dev-release/cpp/src/libp4a*` (developer convenience).
61
+ 4. The standard system search path (`LD_LIBRARY_PATH`, macOS rpath, Windows `PATH`).
62
+
63
+ ## Smoke test
64
+
65
+ ```python
66
+ import pls4all
67
+ print(pls4all.version()) # "0.96.0+abi.1.13.0"
68
+ print(pls4all.abi_version()) # (1, 13, 0)
69
+
70
+ with pls4all.Context() as ctx:
71
+ ctx.seed = 42
72
+ print(ctx.last_error) # ""
73
+ try:
74
+ ctx.backend = pls4all.Backend.CUDA
75
+ except pls4all.Pls4allError as e:
76
+ print(e) # 'backend 5 is not compiled into this build of libp4a'
77
+
78
+ with pls4all.Config() as cfg:
79
+ cfg.algorithm = pls4all.Algorithm.PCR
80
+ cfg.solver = pls4all.Solver.SVD
81
+ cfg.deflation = pls4all.Deflation.REGRESSION
82
+ assert cfg.algorithm == pls4all.Algorithm.PCR
83
+ assert cfg.solver == pls4all.Solver.SVD
84
+ assert cfg.deflation == pls4all.Deflation.REGRESSION
85
+ cfg.algorithm = pls4all.Algorithm.PLS_SVD
86
+ cfg.deflation = pls4all.Deflation.CANONICAL
87
+ assert cfg.algorithm == pls4all.Algorithm.PLS_SVD
88
+ assert cfg.deflation == pls4all.Deflation.CANONICAL
89
+ ```
@@ -0,0 +1,58 @@
1
+ # Python binding
2
+
3
+ The current Python package ships a minimal **ctypes-only** binding that:
4
+
5
+ - loads `libp4a.{so,dll,dylib}` via `ctypes.CDLL`,
6
+ - exposes the version / status / dtype / backend introspection queries,
7
+ - exposes a Pythonic `Context` and `Config` wrapper for the lifecycle calls,
8
+ - raises a typed exception (`Pls4allError`) on any non-OK status, capturing the per-context `last_error` message.
9
+
10
+ A real wheel with NumPy zero-copy `p4a_matrix_view_t` conversion + a
11
+ sklearn-compatible estimator lands in Phase 2 on top of the live C core.
12
+
13
+ ## Build the underlying library
14
+
15
+ ```bash
16
+ cmake --preset dev-release
17
+ cmake --build --preset dev-release --parallel
18
+ ```
19
+
20
+ This produces `build/dev-release/cpp/src/libp4a.so` (or `.dylib` / `.dll`).
21
+ The binding looks for it on disk via the loader rules below.
22
+
23
+ ## Loader rules
24
+
25
+ In order:
26
+
27
+ 1. `$PLS4ALL_LIB_PATH` — explicit path to `libp4a` (most direct).
28
+ 2. `pls4all/lib/libp4a*` next to the installed package (wheel layout).
29
+ 3. `<repo-root>/build/dev-release/cpp/src/libp4a*` (developer convenience).
30
+ 4. The standard system search path (`LD_LIBRARY_PATH`, macOS rpath, Windows `PATH`).
31
+
32
+ ## Smoke test
33
+
34
+ ```python
35
+ import pls4all
36
+ print(pls4all.version()) # "0.96.0+abi.1.13.0"
37
+ print(pls4all.abi_version()) # (1, 13, 0)
38
+
39
+ with pls4all.Context() as ctx:
40
+ ctx.seed = 42
41
+ print(ctx.last_error) # ""
42
+ try:
43
+ ctx.backend = pls4all.Backend.CUDA
44
+ except pls4all.Pls4allError as e:
45
+ print(e) # 'backend 5 is not compiled into this build of libp4a'
46
+
47
+ with pls4all.Config() as cfg:
48
+ cfg.algorithm = pls4all.Algorithm.PCR
49
+ cfg.solver = pls4all.Solver.SVD
50
+ cfg.deflation = pls4all.Deflation.REGRESSION
51
+ assert cfg.algorithm == pls4all.Algorithm.PCR
52
+ assert cfg.solver == pls4all.Solver.SVD
53
+ assert cfg.deflation == pls4all.Deflation.REGRESSION
54
+ cfg.algorithm = pls4all.Algorithm.PLS_SVD
55
+ cfg.deflation = pls4all.Deflation.CANONICAL
56
+ assert cfg.algorithm == pls4all.Algorithm.PLS_SVD
57
+ assert cfg.deflation == pls4all.Deflation.CANONICAL
58
+ ```
@@ -0,0 +1,60 @@
1
+ [build-system]
2
+ # setuptools >= 77 implements PEP 639 SPDX license validation. It loads
3
+ # packaging.licenses, which exists only in packaging >= 24.2 — older
4
+ # packaging makes setuptools 77 crash with
5
+ # `ImportError: Cannot import 'packaging.licenses'.
6
+ # Setuptools>=77.0.0 requires "packaging>=24.2" to work properly.`
7
+ requires = ["setuptools>=77", "wheel", "packaging>=24.2"]
8
+ build-backend = "setuptools.build_meta"
9
+
10
+ [project]
11
+ name = "pls4all"
12
+ version = "0.97.0"
13
+ description = "Python binding for the pls4all C ABI — a portable PLS / NIRS engine."
14
+ authors = [{ name = "Grégory Beurier and contributors" }]
15
+ license = "CECILL-2.1"
16
+ license-files = ["LICENSE"]
17
+ readme = "README.md"
18
+ requires-python = ">=3.10"
19
+ dependencies = ["numpy>=1.21"]
20
+ classifiers = [
21
+ "Development Status :: 4 - Beta",
22
+ "Intended Audience :: Science/Research",
23
+ "Intended Audience :: Developers",
24
+ "Operating System :: POSIX :: Linux",
25
+ "Operating System :: MacOS",
26
+ "Operating System :: Microsoft :: Windows",
27
+ "Programming Language :: Python :: 3",
28
+ "Programming Language :: Python :: 3.10",
29
+ "Programming Language :: Python :: 3.11",
30
+ "Programming Language :: Python :: 3.12",
31
+ "Programming Language :: Python :: 3.13",
32
+ "Programming Language :: Python :: Implementation :: CPython",
33
+ "Topic :: Scientific/Engineering",
34
+ "Topic :: Scientific/Engineering :: Chemistry",
35
+ "Topic :: Scientific/Engineering :: Mathematics",
36
+ ]
37
+
38
+ [project.urls]
39
+ Homepage = "https://github.com/GBeurier/pls4all"
40
+ Repository = "https://github.com/GBeurier/pls4all"
41
+ Issues = "https://github.com/GBeurier/pls4all/issues"
42
+ Documentation = "https://gbeurier.github.io/pls4all/"
43
+
44
+ [tool.setuptools.packages.find]
45
+ where = ["src"]
46
+
47
+ # Bundle the native shared library shipped inside the wheel.
48
+ # cibuildwheel's build_libp4a_in_wheel.sh stages libp4a into
49
+ # src/pls4all/lib/ before the sdist/wheel is built; the patterns below
50
+ # tell setuptools to ship every variant (Linux .so, macOS .dylib,
51
+ # Windows .dll, the auditwheel-renamed forms, and the SONAME symlinks).
52
+ [tool.setuptools.package-data]
53
+ pls4all = [
54
+ "lib/libp4a*",
55
+ "lib/p4a*",
56
+ "lib/*.so",
57
+ "lib/*.so.*",
58
+ "lib/*.dylib",
59
+ "lib/*.dll",
60
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,41 @@
1
+ # SPDX-License-Identifier: CECILL-2.1
2
+ #
3
+ # setup.py shim — declares the package as **non-pure** so cibuildwheel
4
+ # and setuptools treat it as platform-specific.
5
+ #
6
+ # This binding is **ctypes-only**: there is no CPython extension to
7
+ # compile. By default, setuptools detects this absence and produces a
8
+ # pure-Python wheel tagged ``py3-none-any``. That tag is wrong for us
9
+ # because:
10
+ #
11
+ # 1. The wheel embeds a native shared library (libp4a) shipped under
12
+ # ``pls4all/lib/``, so the artefact is platform-specific.
13
+ # 2. cibuildwheel skips its ``CIBW_REPAIR_WHEEL_COMMAND_*`` step on
14
+ # pure wheels — auditwheel / delocate / delvewheel never runs and
15
+ # the manylinux policy is not enforced.
16
+ #
17
+ # Overriding ``Distribution.has_ext_modules`` flips both behaviours:
18
+ # setuptools generates a platform-tagged wheel
19
+ # (``cp3X-cp3X-manylinux_2_31_x86_64`` initially), and cibuildwheel
20
+ # runs the repair pass. scripts/retag_wheels.py then rewrites the
21
+ # filename + ``WHEEL`` metadata + ``RECORD`` to the canonical
22
+ # ``py3-none-${platform}`` form so the wheel applies to every
23
+ # CPython supported by ``requires-python``.
24
+
25
+ from setuptools import Distribution, setup
26
+ from setuptools.dist import Distribution as _Distribution
27
+
28
+
29
+ def _has_native_payload(self: _Distribution) -> bool:
30
+ return True
31
+
32
+
33
+ # Force the platform-tagged wheel: setuptools.bdist_wheel checks both
34
+ # ``Distribution.has_ext_modules()`` and ``Distribution.is_pure()``.
35
+ Distribution.has_ext_modules = _has_native_payload # type: ignore[method-assign]
36
+ Distribution.is_pure = lambda self: False # type: ignore[method-assign]
37
+
38
+ # All other metadata (name, version, license, dependencies, package_data, …)
39
+ # comes from pyproject.toml. Calling setup() with no kwargs lets the
40
+ # declarative metadata drive the build.
41
+ setup()
@@ -0,0 +1,212 @@
1
+ """pls4all — Python binding for the pls4all C ABI.
2
+
3
+ Current scope: version / status / dtype / backend introspection, a Context
4
+ and a Config wrapper, an OperatorBank wrapper, a ValidationPlan wrapper, and
5
+ the AOM global / POP per-component selection result handles
6
+ (`aom_global_select`, `aom_per_component_select`). Full Python model
7
+ wrappers (NIPALS, orthogonal-scores, SIMPLS, kernel, wide-kernel, SVD,
8
+ power, randomized-SVD, PLSCanonical, PLSSVD, PLS-DA, OPLS/OPLS-DA, PCR and
9
+ the cross-validation / metrics / variable-selection surface) land in
10
+ Phase 2 on top of the live C core.
11
+ """
12
+
13
+ from ._ffi import lib # noqa: F401 — eagerly loads libp4a
14
+ from ._errors import Pls4allError
15
+ from ._types import Algorithm, Backend, Deflation, Dtype, Solver, Status
16
+ from ._context import Context
17
+ from ._config import Config
18
+ from ._aom import (
19
+ AomGlobalResult,
20
+ AomPerComponentResult,
21
+ OperatorBank,
22
+ OperatorKind,
23
+ ValidationPlan,
24
+ aom_global_select,
25
+ aom_per_component_select,
26
+ )
27
+ from ._model import Model, ModelArrayKind
28
+ from ._methods import (
29
+ MethodResult,
30
+ sparse_simpls_fit,
31
+ di_pls_fit,
32
+ recursive_pls_run,
33
+ cppls_fit,
34
+ weighted_pls_fit,
35
+ robust_pls_fit,
36
+ ridge_pls_fit,
37
+ continuum_regression_fit,
38
+ n_pls_fit,
39
+ kernel_pls_fit,
40
+ o2pls_fit,
41
+ approximate_press_compute,
42
+ pls_diagnostics_compute,
43
+ sparse_pls_da_fit,
44
+ group_sparse_pls_fit,
45
+ fused_sparse_pls_fit,
46
+ pds_fit,
47
+ ds_fit,
48
+ mir_pls_fit,
49
+ missing_aware_nipals_fit,
50
+ pls_glm_fit,
51
+ pls_qda_fit,
52
+ pls_cox_fit,
53
+ bagging_pls_fit,
54
+ gpr_pls_fit,
55
+ boosting_pls_fit,
56
+ random_subspace_pls_fit,
57
+ so_pls_fit,
58
+ on_pls_fit,
59
+ rosa_fit,
60
+ pls_monitoring_run,
61
+ one_se_rule_compute,
62
+ # §17 fit shims
63
+ mb_pls_fit,
64
+ lw_pls_fit,
65
+ pls_lda_fit,
66
+ pls_logistic_fit,
67
+ aom_preprocess_fit,
68
+ # §18 selector shims
69
+ variable_select_rank,
70
+ interval_select,
71
+ stability_select,
72
+ uve_select,
73
+ spa_select,
74
+ cars_select,
75
+ random_frog_select,
76
+ scars_select,
77
+ ga_select,
78
+ pso_select,
79
+ vissa_select,
80
+ shaving_select,
81
+ bve_select,
82
+ t2_select,
83
+ wvc_select,
84
+ wvc_threshold_select,
85
+ emcuve_select,
86
+ randomization_select,
87
+ bipls_select,
88
+ sipls_select,
89
+ rep_select,
90
+ ipw_select,
91
+ st_select,
92
+ # §19 Phase 50+ numerical methods
93
+ ecr_fit,
94
+ iriv_select,
95
+ irf_select,
96
+ vip_spa_select,
97
+ )
98
+
99
+
100
+ def version() -> str:
101
+ """Return the runtime library version string, e.g. 'X.Y.Z+abi.A.B.C'."""
102
+ return lib.p4a_get_version_string().decode("utf-8")
103
+
104
+
105
+ def abi_version() -> tuple[int, int, int]:
106
+ return (
107
+ int(lib.p4a_get_abi_version_major()),
108
+ int(lib.p4a_get_abi_version_minor()),
109
+ int(lib.p4a_get_abi_version_patch()),
110
+ )
111
+
112
+
113
+ def build_info() -> str:
114
+ return lib.p4a_get_build_info().decode("utf-8")
115
+
116
+
117
+ __all__ = [
118
+ "Pls4allError",
119
+ "Backend",
120
+ "Dtype",
121
+ "Deflation",
122
+ "Algorithm",
123
+ "Status",
124
+ "Solver",
125
+ "Context",
126
+ "Config",
127
+ "OperatorKind",
128
+ "OperatorBank",
129
+ "ValidationPlan",
130
+ "AomGlobalResult",
131
+ "AomPerComponentResult",
132
+ "aom_global_select",
133
+ "aom_per_component_select",
134
+ "Model",
135
+ "ModelArrayKind",
136
+ "MethodResult",
137
+ "sparse_simpls_fit",
138
+ "di_pls_fit",
139
+ "recursive_pls_run",
140
+ "cppls_fit",
141
+ "weighted_pls_fit",
142
+ "robust_pls_fit",
143
+ "ridge_pls_fit",
144
+ "continuum_regression_fit",
145
+ "n_pls_fit",
146
+ "kernel_pls_fit",
147
+ "o2pls_fit",
148
+ "approximate_press_compute",
149
+ "pls_diagnostics_compute",
150
+ "sparse_pls_da_fit",
151
+ "group_sparse_pls_fit",
152
+ "fused_sparse_pls_fit",
153
+ "pds_fit",
154
+ "ds_fit",
155
+ "mir_pls_fit",
156
+ "missing_aware_nipals_fit",
157
+ "pls_glm_fit",
158
+ "pls_qda_fit",
159
+ "pls_cox_fit",
160
+ "bagging_pls_fit",
161
+ "gpr_pls_fit",
162
+ "boosting_pls_fit",
163
+ "random_subspace_pls_fit",
164
+ "so_pls_fit",
165
+ "on_pls_fit",
166
+ "rosa_fit",
167
+ "pls_monitoring_run",
168
+ "one_se_rule_compute",
169
+ # §17 fit shims
170
+ "mb_pls_fit",
171
+ "lw_pls_fit",
172
+ "pls_lda_fit",
173
+ "pls_logistic_fit",
174
+ "aom_preprocess_fit",
175
+ # §18 selector shims
176
+ "variable_select_rank",
177
+ "interval_select",
178
+ "stability_select",
179
+ "uve_select",
180
+ "spa_select",
181
+ "cars_select",
182
+ "random_frog_select",
183
+ "scars_select",
184
+ "ga_select",
185
+ "pso_select",
186
+ "vissa_select",
187
+ "shaving_select",
188
+ "bve_select",
189
+ "t2_select",
190
+ "wvc_select",
191
+ "wvc_threshold_select",
192
+ "emcuve_select",
193
+ "randomization_select",
194
+ "bipls_select",
195
+ "sipls_select",
196
+ "rep_select",
197
+ "ipw_select",
198
+ "st_select",
199
+ # §19 Phase 50+ numerical methods
200
+ "ecr_fit",
201
+ "iriv_select",
202
+ "irf_select",
203
+ "vip_spa_select",
204
+ "version",
205
+ "abi_version",
206
+ "build_info",
207
+ ]
208
+
209
+ # Derive __version__ from the loaded native library so it stays in sync with
210
+ # the C ABI version_string without any manual maintenance. The expected shape
211
+ # of version() is "X.Y.Z+abi.A.B.C" — we strip the "+abi.*" suffix.
212
+ __version__ = version().split("+", 1)[0]