scm2cpp-lasso 0.13.0__tar.gz → 0.13.1__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 (66) hide show
  1. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/PKG-INFO +104 -4
  2. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/README.md +103 -3
  3. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/pyproject.toml +1 -1
  4. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/setup.py +24 -1
  5. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/__init__.py +1 -1
  6. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/scm2cpp.hpp +31 -0
  7. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso.egg-info/PKG-INFO +104 -4
  8. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/setup.cfg +0 -0
  9. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/__init__.py +0 -0
  10. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/_loader.py +0 -0
  11. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/_loader_accel_cublas.py +0 -0
  12. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/_loader_classifiers.py +0 -0
  13. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/_loader_derived_blas.py +0 -0
  14. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/_loader_derived_cublas.py +0 -0
  15. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/_loader_derived_loops.py +0 -0
  16. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/_loader_gpu_cuda.py +0 -0
  17. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/_loader_gramops_blas.py +0 -0
  18. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/_loader_gramops_loops.py +0 -0
  19. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/_loader_nmf_blas.py +0 -0
  20. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/_loader_nmf_cublas.py +0 -0
  21. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/_loader_nmf_loops.py +0 -0
  22. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/accel_cublas/accel.cpp +0 -0
  23. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/accel_cublas/accel.hpp +0 -0
  24. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/accel_cublas/accel_capi.cpp +0 -0
  25. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/classifiers.cpp +0 -0
  26. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/classifiers.hpp +0 -0
  27. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/classifiers_capi.cpp +0 -0
  28. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/derived_blas/lasso_design.cpp +0 -0
  29. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/derived_blas/lasso_design.hpp +0 -0
  30. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/derived_blas/lasso_design_capi.cpp +0 -0
  31. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/derived_cublas/lasso_design.cpp +0 -0
  32. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/derived_cublas/lasso_design.hpp +0 -0
  33. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/derived_cublas/lasso_design_capi.cpp +0 -0
  34. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/derived_loops/lasso_design.cpp +0 -0
  35. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/derived_loops/lasso_design.hpp +0 -0
  36. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/derived_loops/lasso_design_capi.cpp +0 -0
  37. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/gpu_cuda/gpu.cpp +0 -0
  38. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/gpu_cuda/gpu.hpp +0 -0
  39. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/gpu_cuda/gpu_capi.cpp +0 -0
  40. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/gramops_blas/gramops.cpp +0 -0
  41. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/gramops_blas/gramops.hpp +0 -0
  42. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/gramops_blas/gramops_capi.cpp +0 -0
  43. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/gramops_loops/gramops.cpp +0 -0
  44. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/gramops_loops/gramops.hpp +0 -0
  45. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/gramops_loops/gramops_capi.cpp +0 -0
  46. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/lasso_cov.cpp +0 -0
  47. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/lasso_cov.hpp +0 -0
  48. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/lasso_cov_capi.cpp +0 -0
  49. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/nmf_blas/nmf_cd.cpp +0 -0
  50. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/nmf_blas/nmf_cd.hpp +0 -0
  51. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/nmf_blas/nmf_cd_capi.cpp +0 -0
  52. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/nmf_cublas/nmf_cd.cpp +0 -0
  53. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/nmf_cublas/nmf_cd.hpp +0 -0
  54. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/nmf_cublas/nmf_cd_capi.cpp +0 -0
  55. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/nmf_loops/nmf_cd.cpp +0 -0
  56. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/nmf_loops/nmf_cd.hpp +0 -0
  57. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/nmf_loops/nmf_cd_capi.cpp +0 -0
  58. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/scm2cpp-blas.hpp +0 -0
  59. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/scm2cpp-cublas.hpp +0 -0
  60. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_generated/scm2cpp-cuda.hpp +0 -0
  61. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/_libfind.py +0 -0
  62. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso/batch_capi.cu +0 -0
  63. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso.egg-info/SOURCES.txt +0 -0
  64. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso.egg-info/dependency_links.txt +0 -0
  65. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso.egg-info/requires.txt +0 -0
  66. {scm2cpp_lasso-0.13.0 → scm2cpp_lasso-0.13.1}/src/scm2cpp_lasso.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scm2cpp-lasso
3
- Version: 0.13.0
3
+ Version: 0.13.1
4
4
  Summary: scikit-learn estimators (lasso, elastic net, ridge, ARD, SVM, SVR, logistic regression, GLMs, Huber, Theil-Sen, F-score selection, RFE, NMF, Gaussian mixture, FastICA, PCA, graphical lasso, RANSAC, SGD, OMP, LARS) translated from Scheme to C++ by scm2cpp; GPU batch solver and CUDA kernels included
5
5
  Author: Hirotaka Niitsuma
6
6
  License: MIT
@@ -28,9 +28,19 @@ path. The solver is Scheme, translated to C++ by
28
28
  $ pip install scm2cpp-lasso
29
29
  ```
30
30
 
31
- Installing needs a C++17 compiler and nothing else. If `nvcc` is on
32
- the path, the batched GPU solver is built as well; if it is not, the
33
- package installs and works exactly the same, minus that one method.
31
+ On Linux x86_64 and macOS arm64 (macOS 14 or later) this installs a wheel: compiled, with
32
+ OpenBLAS inside, nothing to build. Elsewhere pip builds the sdist,
33
+ which needs a C++17 compiler and nothing else. The wheels carry no
34
+ GPU code (a wheel cannot know the device); for the GPU builds, install
35
+ from the sdist on a machine with `nvcc` on the path:
36
+
37
+ ```console
38
+ $ pip install --no-binary scm2cpp-lasso scm2cpp-lasso
39
+ ```
40
+
41
+ The batched GPU solver, the cuBLAS variants and the `--cuda`
42
+ estimators are then built as well; without `nvcc` the package installs
43
+ and works exactly the same, minus those.
34
44
 
35
45
  Three of a hundred candidate columns carry the signal; the solver is
36
46
  asked which, and answers with them and nothing else:
@@ -89,6 +99,63 @@ at zero, and `lambda_grid()` walks down from it -- the construction
89
99
  scikit-learn uses. If your columns differ wildly in scale, standardize
90
100
  them first; this solver does not do it for you.
91
101
 
102
+ ## Estimators at a glance
103
+
104
+ Every estimator, the scikit-learn estimator it reproduces, the Scheme
105
+ kernel under `examples/kernel-only/` it is translated from, the builds
106
+ it runs on, and whether Intel's sklearnex accelerates the scikit-learn
107
+ side. Builds: *loops* is the plain translation, *BLAS* / *cuBLAS* the
108
+ products handed to (cu)BLAS, *--cuda* the translator's GPU kernels
109
+ (`gpu_cuda/`), *GPU batch* the hand-written CUDA batch solver; the
110
+ package picks the fastest one present. None of the classes derives
111
+ from scikit-learn's `BaseEstimator` yet, so `clone`, `GridSearchCV`
112
+ and `check_estimator` do not apply to them.
113
+
114
+ | class | scikit-learn | Scheme kernel | builds | sklearnex accelerates |
115
+ |---|---|---|---|---|
116
+ | `CovLasso` | Lasso, ElasticNet, lasso_path(precompute=True) | `lasso-cov.scm` | CPU; GPU batch (fit_path_batch, bootstrap) | no |
117
+ | `CovLassoCV` | LassoCV, ElasticNetCV | `lasso-cov.scm` | CPU; GPU batch | no |
118
+ | `DerivedLasso` | lasso_path, from the design | `lasso-design.scm (--derive)` | loops / BLAS / cuBLAS | no |
119
+ | `CovMultiTaskLasso` | MultiTaskLasso, MultiTaskElasticNet | `lasso-cov.scm` | CPU | no |
120
+ | `CovMultiTaskLassoCV` | MultiTaskLassoCV, MultiTaskElasticNetCV | `lasso-cov.scm` | CPU; GPU batch | no |
121
+ | `CovGroupLasso` | (none; skglm GroupLasso) | `lasso-cov.scm` | CPU | - |
122
+ | `CovLogistic` | LogisticRegression(penalty='l1'), by majorization | `lasso-cov.scm` | CPU | no |
123
+ | `CovRidge` | Ridge(fit_intercept=False), a whole alpha path | `lasso-cov.scm` | CPU | Ridge |
124
+ | `CovBayesianRidge` | BayesianRidge | `lasso-cov.scm` | CPU | no |
125
+ | `CovARDRegression` | ARDRegression (n >= p) | `ard.scm` | loops / BLAS | no |
126
+ | `GramLars` | Lars | `lars.scm` | CPU | no |
127
+ | `GramLassoLars` | LassoLars | `lars.scm` | CPU | no |
128
+ | `GramLassoLarsIC` | LassoLarsIC | `lars.scm` | CPU; device Gram | no |
129
+ | `GramLarsCV` | LarsCV | `lars.scm` | CPU | no |
130
+ | `GramLassoLarsCV` | LassoLarsCV | `lars.scm` | CPU; device Gram | no |
131
+ | `GramOMP` | OrthogonalMatchingPursuit | `omp.scm` | loops / BLAS | no |
132
+ | `GramOMPCV` | OrthogonalMatchingPursuitCV | `omp.scm` | loops / BLAS | no |
133
+ | `LBFGSLogisticRegression` | LogisticRegression(solver='lbfgs') | `logistic-lbfgs.scm, lbfgs.scm` | loops / BLAS / --cuda | LogisticRegression |
134
+ | `LBFGSLogisticRegressionCV` | LogisticRegressionCV | `logistic-lbfgs.scm, lbfgs.scm` | loops / BLAS / --cuda | no |
135
+ | `LBFGSHuberRegressor` | HuberRegressor | `huber.scm, lbfgs.scm` | loops / BLAS / --cuda | no |
136
+ | `LBFGSPoissonRegressor` | PoissonRegressor | `glm.scm, lbfgs.scm` | loops / BLAS / --cuda | no |
137
+ | `LBFGSGammaRegressor` | GammaRegressor | `glm.scm, lbfgs.scm` | loops / BLAS / --cuda | no |
138
+ | `LBFGSTweedieRegressor` | TweedieRegressor | `glm.scm, lbfgs.scm` | loops / BLAS / --cuda | no |
139
+ | `LinearSVM` | LinearSVC(loss='hinge') | `svm-liblinear.scm` | CPU | no |
140
+ | `L1LinearSVM` | LinearSVC(penalty='l1', dual=False) | `svm-l1.scm` | CPU | no |
141
+ | `KernelSVM` | SVC | `svm-smo.scm` | CPU | SVC |
142
+ | `LinearSVR` | LinearSVR | `svr-liblinear.scm` | CPU | no |
143
+ | `KernelSVR` | SVR | `svm-smo.scm` | CPU | SVR |
144
+ | `L1LogisticRegression` | LogisticRegression(penalty='l1', solver='liblinear') | `logistic-l1.scm` | CPU | no |
145
+ | `SGDLinearClassifier` | SGDClassifier(shuffle=False) | `sgd.scm` | CPU | no |
146
+ | `SGDLinearRegressor` | SGDRegressor(shuffle=False) | `sgd.scm` | CPU | no |
147
+ | `SGDPerceptron` | Perceptron(shuffle=False) | `sgd.scm` | CPU | no |
148
+ | `LinearRANSAC` | RANSACRegressor (given subsets) | `ransac.scm` | CPU | no |
149
+ | `WeiszfeldTheilSen` | TheilSenRegressor | `theilsen.scm` | CPU | no |
150
+ | `CDNMF` | NMF(solver='cd') | `nmf-cd.scm (--derive)` | loops / BLAS / cuBLAS | no |
151
+ | `CDGraphicalLasso` | GraphicalLasso | `graphical-lasso.scm` | loops / BLAS | no |
152
+ | `EMGaussianMixture` | GaussianMixture(covariance_type='full') | `gmm.scm` | loops / BLAS / --cuda | no |
153
+ | `ParallelFastICA` | FastICA | `fastica.scm` | loops / BLAS / --cuda | no |
154
+ | `CovPCA` | PCA(svd_solver='covariance_eigh') | `pca.scm` | loops / BLAS / cuBLAS | PCA |
155
+ | `f_regression, f_classif` | f_regression, f_classif | `fscore.scm` | loops / BLAS / cuBLAS | no |
156
+ | `SelectKBest, SelectPercentile` | SelectKBest, SelectPercentile | `fscore.scm` | loops / BLAS / cuBLAS | no |
157
+ | `GramRFE` | RFE(Ridge), RFE(LinearRegression) | `rfe.scm` | loops / BLAS / cuBLAS | inside: Ridge |
158
+
92
159
  ## Two kernels: `CovLasso` and `DerivedLasso`
93
160
 
94
161
  Everything above, and the cross-validation, bootstrap and GPU methods
@@ -733,6 +800,39 @@ estimators (`GramLassoLarsIC`, `GramLarsCV`, `GramLassoLarsCV`,
733
800
  `GramLars`, `GramLassoLars`, `CovPCA`) form a large X'X on the device
734
801
  when one answers. `SCM2CPP_NO_DEVICE=1` keeps everything on the CPU.
735
802
 
803
+ ## Against Intel's sklearnex
804
+
805
+ sklearnex (scikit-learn-intelex 2026.2, oneDAL underneath) replaces
806
+ some scikit-learn estimators with its own. `bench/sklearnex-compare.py`
807
+ times the ones with a counterpart here, sklearnex's estimators
808
+ imported directly (not patched in) so that scikit-learn itself is
809
+ timed in the same process. The ratios are scikit-learn's or
810
+ sklearnex's time over the package's best (its CPU build, or the device
811
+ build where one answers); the load average was 8 to 11, so the times
812
+ move by some tens of percent.
813
+
814
+ | estimator, n x p | iterations (sk / sklearnex / ours) | vs scikit-learn, one core | vs sklearnex, one core | vs sklearnex, 20 cores |
815
+ |---|---|---|---|---|
816
+ | LogisticRegression, 200,000 x 100 | 6 / 12 / 6 | 10.6x (device) | 8.4x (device) | 4.4x (device); on the CPU 0.5x |
817
+ | Ridge (alpha 1), 200,000 x 200 | - | 2.1x | 1.2x | 0.3x |
818
+ | PCA (10 components), 100,000 x 50 | - | 1.5x | 2.4x | 1.7x |
819
+ | PCA (10 components), 50,000 x 500 | - | 1.1x | 1.4x | 0.5x |
820
+ | SVC (rbf), 5,000 x 20 | 4,858 / 7 / 4,882 | 1.2x | 1.4x | 0.5x |
821
+ | SVR (rbf), 5,000 x 20 | 12,821 / 10 / 12,824 | 1.3x | 1.4x | 0.4x |
822
+ | RFE(Ridge), 20 of 200 kept, 20,000 x 200 | - | 216x | 128x | 78x |
823
+
824
+ On one core the package is ahead everywhere. sklearnex uses every
825
+ core by default, and the package's kernels are single-threaded, so
826
+ with 20 cores sklearnex is faster on Ridge, the larger PCA and the
827
+ SVMs; what stays ahead is the device build (logistic regression) and
828
+ an algorithm that does less work (RFE: the Gram once, the Cholesky
829
+ factor downdated per drop, where scikit-learn refits). sklearnex is
830
+ not scikit-learn's arithmetic: its logistic regression takes 12
831
+ iterations to scikit-learn's 6 and its coefficients differ from
832
+ scikit-learn's by 6e-3, where the package's agree to 4e-15; its SVMs
833
+ report a different iteration count. This sklearnex accelerates no
834
+ lasso, so the lasso tables above stand against scikit-learn alone.
835
+
736
836
  ## The GPU build
737
837
 
738
838
  Where the CUDA toolkit is at install time, `ParallelFastICA`,
@@ -10,9 +10,19 @@ path. The solver is Scheme, translated to C++ by
10
10
  $ pip install scm2cpp-lasso
11
11
  ```
12
12
 
13
- Installing needs a C++17 compiler and nothing else. If `nvcc` is on
14
- the path, the batched GPU solver is built as well; if it is not, the
15
- package installs and works exactly the same, minus that one method.
13
+ On Linux x86_64 and macOS arm64 (macOS 14 or later) this installs a wheel: compiled, with
14
+ OpenBLAS inside, nothing to build. Elsewhere pip builds the sdist,
15
+ which needs a C++17 compiler and nothing else. The wheels carry no
16
+ GPU code (a wheel cannot know the device); for the GPU builds, install
17
+ from the sdist on a machine with `nvcc` on the path:
18
+
19
+ ```console
20
+ $ pip install --no-binary scm2cpp-lasso scm2cpp-lasso
21
+ ```
22
+
23
+ The batched GPU solver, the cuBLAS variants and the `--cuda`
24
+ estimators are then built as well; without `nvcc` the package installs
25
+ and works exactly the same, minus those.
16
26
 
17
27
  Three of a hundred candidate columns carry the signal; the solver is
18
28
  asked which, and answers with them and nothing else:
@@ -71,6 +81,63 @@ at zero, and `lambda_grid()` walks down from it -- the construction
71
81
  scikit-learn uses. If your columns differ wildly in scale, standardize
72
82
  them first; this solver does not do it for you.
73
83
 
84
+ ## Estimators at a glance
85
+
86
+ Every estimator, the scikit-learn estimator it reproduces, the Scheme
87
+ kernel under `examples/kernel-only/` it is translated from, the builds
88
+ it runs on, and whether Intel's sklearnex accelerates the scikit-learn
89
+ side. Builds: *loops* is the plain translation, *BLAS* / *cuBLAS* the
90
+ products handed to (cu)BLAS, *--cuda* the translator's GPU kernels
91
+ (`gpu_cuda/`), *GPU batch* the hand-written CUDA batch solver; the
92
+ package picks the fastest one present. None of the classes derives
93
+ from scikit-learn's `BaseEstimator` yet, so `clone`, `GridSearchCV`
94
+ and `check_estimator` do not apply to them.
95
+
96
+ | class | scikit-learn | Scheme kernel | builds | sklearnex accelerates |
97
+ |---|---|---|---|---|
98
+ | `CovLasso` | Lasso, ElasticNet, lasso_path(precompute=True) | `lasso-cov.scm` | CPU; GPU batch (fit_path_batch, bootstrap) | no |
99
+ | `CovLassoCV` | LassoCV, ElasticNetCV | `lasso-cov.scm` | CPU; GPU batch | no |
100
+ | `DerivedLasso` | lasso_path, from the design | `lasso-design.scm (--derive)` | loops / BLAS / cuBLAS | no |
101
+ | `CovMultiTaskLasso` | MultiTaskLasso, MultiTaskElasticNet | `lasso-cov.scm` | CPU | no |
102
+ | `CovMultiTaskLassoCV` | MultiTaskLassoCV, MultiTaskElasticNetCV | `lasso-cov.scm` | CPU; GPU batch | no |
103
+ | `CovGroupLasso` | (none; skglm GroupLasso) | `lasso-cov.scm` | CPU | - |
104
+ | `CovLogistic` | LogisticRegression(penalty='l1'), by majorization | `lasso-cov.scm` | CPU | no |
105
+ | `CovRidge` | Ridge(fit_intercept=False), a whole alpha path | `lasso-cov.scm` | CPU | Ridge |
106
+ | `CovBayesianRidge` | BayesianRidge | `lasso-cov.scm` | CPU | no |
107
+ | `CovARDRegression` | ARDRegression (n >= p) | `ard.scm` | loops / BLAS | no |
108
+ | `GramLars` | Lars | `lars.scm` | CPU | no |
109
+ | `GramLassoLars` | LassoLars | `lars.scm` | CPU | no |
110
+ | `GramLassoLarsIC` | LassoLarsIC | `lars.scm` | CPU; device Gram | no |
111
+ | `GramLarsCV` | LarsCV | `lars.scm` | CPU | no |
112
+ | `GramLassoLarsCV` | LassoLarsCV | `lars.scm` | CPU; device Gram | no |
113
+ | `GramOMP` | OrthogonalMatchingPursuit | `omp.scm` | loops / BLAS | no |
114
+ | `GramOMPCV` | OrthogonalMatchingPursuitCV | `omp.scm` | loops / BLAS | no |
115
+ | `LBFGSLogisticRegression` | LogisticRegression(solver='lbfgs') | `logistic-lbfgs.scm, lbfgs.scm` | loops / BLAS / --cuda | LogisticRegression |
116
+ | `LBFGSLogisticRegressionCV` | LogisticRegressionCV | `logistic-lbfgs.scm, lbfgs.scm` | loops / BLAS / --cuda | no |
117
+ | `LBFGSHuberRegressor` | HuberRegressor | `huber.scm, lbfgs.scm` | loops / BLAS / --cuda | no |
118
+ | `LBFGSPoissonRegressor` | PoissonRegressor | `glm.scm, lbfgs.scm` | loops / BLAS / --cuda | no |
119
+ | `LBFGSGammaRegressor` | GammaRegressor | `glm.scm, lbfgs.scm` | loops / BLAS / --cuda | no |
120
+ | `LBFGSTweedieRegressor` | TweedieRegressor | `glm.scm, lbfgs.scm` | loops / BLAS / --cuda | no |
121
+ | `LinearSVM` | LinearSVC(loss='hinge') | `svm-liblinear.scm` | CPU | no |
122
+ | `L1LinearSVM` | LinearSVC(penalty='l1', dual=False) | `svm-l1.scm` | CPU | no |
123
+ | `KernelSVM` | SVC | `svm-smo.scm` | CPU | SVC |
124
+ | `LinearSVR` | LinearSVR | `svr-liblinear.scm` | CPU | no |
125
+ | `KernelSVR` | SVR | `svm-smo.scm` | CPU | SVR |
126
+ | `L1LogisticRegression` | LogisticRegression(penalty='l1', solver='liblinear') | `logistic-l1.scm` | CPU | no |
127
+ | `SGDLinearClassifier` | SGDClassifier(shuffle=False) | `sgd.scm` | CPU | no |
128
+ | `SGDLinearRegressor` | SGDRegressor(shuffle=False) | `sgd.scm` | CPU | no |
129
+ | `SGDPerceptron` | Perceptron(shuffle=False) | `sgd.scm` | CPU | no |
130
+ | `LinearRANSAC` | RANSACRegressor (given subsets) | `ransac.scm` | CPU | no |
131
+ | `WeiszfeldTheilSen` | TheilSenRegressor | `theilsen.scm` | CPU | no |
132
+ | `CDNMF` | NMF(solver='cd') | `nmf-cd.scm (--derive)` | loops / BLAS / cuBLAS | no |
133
+ | `CDGraphicalLasso` | GraphicalLasso | `graphical-lasso.scm` | loops / BLAS | no |
134
+ | `EMGaussianMixture` | GaussianMixture(covariance_type='full') | `gmm.scm` | loops / BLAS / --cuda | no |
135
+ | `ParallelFastICA` | FastICA | `fastica.scm` | loops / BLAS / --cuda | no |
136
+ | `CovPCA` | PCA(svd_solver='covariance_eigh') | `pca.scm` | loops / BLAS / cuBLAS | PCA |
137
+ | `f_regression, f_classif` | f_regression, f_classif | `fscore.scm` | loops / BLAS / cuBLAS | no |
138
+ | `SelectKBest, SelectPercentile` | SelectKBest, SelectPercentile | `fscore.scm` | loops / BLAS / cuBLAS | no |
139
+ | `GramRFE` | RFE(Ridge), RFE(LinearRegression) | `rfe.scm` | loops / BLAS / cuBLAS | inside: Ridge |
140
+
74
141
  ## Two kernels: `CovLasso` and `DerivedLasso`
75
142
 
76
143
  Everything above, and the cross-validation, bootstrap and GPU methods
@@ -715,6 +782,39 @@ estimators (`GramLassoLarsIC`, `GramLarsCV`, `GramLassoLarsCV`,
715
782
  `GramLars`, `GramLassoLars`, `CovPCA`) form a large X'X on the device
716
783
  when one answers. `SCM2CPP_NO_DEVICE=1` keeps everything on the CPU.
717
784
 
785
+ ## Against Intel's sklearnex
786
+
787
+ sklearnex (scikit-learn-intelex 2026.2, oneDAL underneath) replaces
788
+ some scikit-learn estimators with its own. `bench/sklearnex-compare.py`
789
+ times the ones with a counterpart here, sklearnex's estimators
790
+ imported directly (not patched in) so that scikit-learn itself is
791
+ timed in the same process. The ratios are scikit-learn's or
792
+ sklearnex's time over the package's best (its CPU build, or the device
793
+ build where one answers); the load average was 8 to 11, so the times
794
+ move by some tens of percent.
795
+
796
+ | estimator, n x p | iterations (sk / sklearnex / ours) | vs scikit-learn, one core | vs sklearnex, one core | vs sklearnex, 20 cores |
797
+ |---|---|---|---|---|
798
+ | LogisticRegression, 200,000 x 100 | 6 / 12 / 6 | 10.6x (device) | 8.4x (device) | 4.4x (device); on the CPU 0.5x |
799
+ | Ridge (alpha 1), 200,000 x 200 | - | 2.1x | 1.2x | 0.3x |
800
+ | PCA (10 components), 100,000 x 50 | - | 1.5x | 2.4x | 1.7x |
801
+ | PCA (10 components), 50,000 x 500 | - | 1.1x | 1.4x | 0.5x |
802
+ | SVC (rbf), 5,000 x 20 | 4,858 / 7 / 4,882 | 1.2x | 1.4x | 0.5x |
803
+ | SVR (rbf), 5,000 x 20 | 12,821 / 10 / 12,824 | 1.3x | 1.4x | 0.4x |
804
+ | RFE(Ridge), 20 of 200 kept, 20,000 x 200 | - | 216x | 128x | 78x |
805
+
806
+ On one core the package is ahead everywhere. sklearnex uses every
807
+ core by default, and the package's kernels are single-threaded, so
808
+ with 20 cores sklearnex is faster on Ridge, the larger PCA and the
809
+ SVMs; what stays ahead is the device build (logistic regression) and
810
+ an algorithm that does less work (RFE: the Gram once, the Cholesky
811
+ factor downdated per drop, where scikit-learn refits). sklearnex is
812
+ not scikit-learn's arithmetic: its logistic regression takes 12
813
+ iterations to scikit-learn's 6 and its coefficients differ from
814
+ scikit-learn's by 6e-3, where the package's agree to 4e-15; its SVMs
815
+ report a different iteration count. This sklearnex accelerates no
816
+ lasso, so the lasso tables above stand against scikit-learn alone.
817
+
718
818
  ## The GPU build
719
819
 
720
820
  Where the CUDA toolkit is at install time, `ParallelFastICA`,
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "scm2cpp-lasso"
7
- version = "0.13.0"
7
+ version = "0.13.1"
8
8
  description = "scikit-learn estimators (lasso, elastic net, ridge, ARD, SVM, SVR, logistic regression, GLMs, Huber, Theil-Sen, F-score selection, RFE, NMF, Gaussian mixture, FastICA, PCA, graphical lasso, RANSAC, SGD, OMP, LARS) translated from Scheme to C++ by scm2cpp; GPU batch solver and CUDA kernels included"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -242,4 +242,27 @@ if _cuda is not None:
242
242
  extra_compile_args=CXXFLAGS + ["-O3", "-fstrict-overflow"],
243
243
  ))
244
244
 
245
- setup(ext_modules=extensions, cmdclass={"build_ext": BuildWithCuda})
245
+ # A wheel is tagged py3-none-<platform>: the extensions are shared
246
+ # libraries the package loads through ctypes, using no Python API, so
247
+ # one build serves every Python on that platform.
248
+ try:
249
+ from setuptools.command.bdist_wheel import bdist_wheel
250
+ except ImportError: # setuptools before 70.1
251
+ try:
252
+ from wheel.bdist_wheel import bdist_wheel
253
+ except ImportError:
254
+ bdist_wheel = None
255
+
256
+ cmdclass = {"build_ext": BuildWithCuda}
257
+ if bdist_wheel is not None:
258
+ class PlatformWheel(bdist_wheel):
259
+ def finalize_options(self):
260
+ super().finalize_options()
261
+ self.root_is_pure = False
262
+
263
+ def get_tag(self):
264
+ return ("py3", "none", super().get_tag()[2])
265
+
266
+ cmdclass["bdist_wheel"] = PlatformWheel
267
+
268
+ setup(ext_modules=extensions, cmdclass=cmdclass)
@@ -51,7 +51,7 @@ __all__ = ["CovLasso", "CovRidge", "CovBayesianRidge", "CovLogistic", "CovGroupL
51
51
  "SelectPercentile", "GramRFE",
52
52
  "CovMultiTaskLasso", "CovMultiTaskLassoCV", "DerivedLasso",
53
53
  "cuda_available", "derived_backend", "kernel"]
54
- __version__ = "0.13.0"
54
+ __version__ = "0.13.1"
55
55
 
56
56
  _BATCH = load_batch_lib()
57
57
  _DP = ctypes.POINTER(ctypes.c_double)
@@ -3,6 +3,7 @@
3
3
 
4
4
  #include <iostream>
5
5
  #include <vector>
6
+ #include <string>
6
7
  #include <list>
7
8
  #include <map>
8
9
  #include <algorithm>
@@ -1459,6 +1460,36 @@ namespace scm2cpp {
1459
1460
  std::size_t size() const { return n; }
1460
1461
  };
1461
1462
 
1463
+ // A memo for a pure call: (memo-call M E KEY ...) is
1464
+ // memo_get(M, memo_key().add(KEY)...str(), [&]() { return E; })
1465
+ // M a hash table from the key's bytes to E's value (the translator's
1466
+ // (make-hash), an std::unordered_map<std::string, V>), so E runs once
1467
+ // per distinct key. A key is scalars by value and, through
1468
+ // add_range, the first N elements of an array -- the index set a
1469
+ // gather read, say. The bytes are the values' object representation,
1470
+ // so 0.0 and -0.0 are different keys (a miss, never a wrong hit).
1471
+ struct memo_key {
1472
+ std::string b;
1473
+ template<typename T> memo_key& add(const T& v) {
1474
+ b.append(reinterpret_cast<const char*>(&v), sizeof(T));
1475
+ return *this;
1476
+ }
1477
+ template<typename V> memo_key& add_range(const V& v, long n) {
1478
+ add(n);
1479
+ for (long i = 0; i < n; ++i) add(v[i]);
1480
+ return *this;
1481
+ }
1482
+ const std::string& str() const { return b; }
1483
+ };
1484
+ template<typename M, typename F>
1485
+ inline typename M::mapped_type memo_get(M& m, const std::string& k, F f) {
1486
+ auto it = m.find(k);
1487
+ if (it != m.end()) return it->second;
1488
+ typename M::mapped_type v = f();
1489
+ m.emplace(k, v);
1490
+ return v;
1491
+ }
1492
+
1462
1493
  template<typename T, int N> struct integral_image {
1463
1494
  std::vector<T> table; // padded with a zero border: dim[k]+1 per axis
1464
1495
  int dim[N]; // extents of the source array
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scm2cpp-lasso
3
- Version: 0.13.0
3
+ Version: 0.13.1
4
4
  Summary: scikit-learn estimators (lasso, elastic net, ridge, ARD, SVM, SVR, logistic regression, GLMs, Huber, Theil-Sen, F-score selection, RFE, NMF, Gaussian mixture, FastICA, PCA, graphical lasso, RANSAC, SGD, OMP, LARS) translated from Scheme to C++ by scm2cpp; GPU batch solver and CUDA kernels included
5
5
  Author: Hirotaka Niitsuma
6
6
  License: MIT
@@ -28,9 +28,19 @@ path. The solver is Scheme, translated to C++ by
28
28
  $ pip install scm2cpp-lasso
29
29
  ```
30
30
 
31
- Installing needs a C++17 compiler and nothing else. If `nvcc` is on
32
- the path, the batched GPU solver is built as well; if it is not, the
33
- package installs and works exactly the same, minus that one method.
31
+ On Linux x86_64 and macOS arm64 (macOS 14 or later) this installs a wheel: compiled, with
32
+ OpenBLAS inside, nothing to build. Elsewhere pip builds the sdist,
33
+ which needs a C++17 compiler and nothing else. The wheels carry no
34
+ GPU code (a wheel cannot know the device); for the GPU builds, install
35
+ from the sdist on a machine with `nvcc` on the path:
36
+
37
+ ```console
38
+ $ pip install --no-binary scm2cpp-lasso scm2cpp-lasso
39
+ ```
40
+
41
+ The batched GPU solver, the cuBLAS variants and the `--cuda`
42
+ estimators are then built as well; without `nvcc` the package installs
43
+ and works exactly the same, minus those.
34
44
 
35
45
  Three of a hundred candidate columns carry the signal; the solver is
36
46
  asked which, and answers with them and nothing else:
@@ -89,6 +99,63 @@ at zero, and `lambda_grid()` walks down from it -- the construction
89
99
  scikit-learn uses. If your columns differ wildly in scale, standardize
90
100
  them first; this solver does not do it for you.
91
101
 
102
+ ## Estimators at a glance
103
+
104
+ Every estimator, the scikit-learn estimator it reproduces, the Scheme
105
+ kernel under `examples/kernel-only/` it is translated from, the builds
106
+ it runs on, and whether Intel's sklearnex accelerates the scikit-learn
107
+ side. Builds: *loops* is the plain translation, *BLAS* / *cuBLAS* the
108
+ products handed to (cu)BLAS, *--cuda* the translator's GPU kernels
109
+ (`gpu_cuda/`), *GPU batch* the hand-written CUDA batch solver; the
110
+ package picks the fastest one present. None of the classes derives
111
+ from scikit-learn's `BaseEstimator` yet, so `clone`, `GridSearchCV`
112
+ and `check_estimator` do not apply to them.
113
+
114
+ | class | scikit-learn | Scheme kernel | builds | sklearnex accelerates |
115
+ |---|---|---|---|---|
116
+ | `CovLasso` | Lasso, ElasticNet, lasso_path(precompute=True) | `lasso-cov.scm` | CPU; GPU batch (fit_path_batch, bootstrap) | no |
117
+ | `CovLassoCV` | LassoCV, ElasticNetCV | `lasso-cov.scm` | CPU; GPU batch | no |
118
+ | `DerivedLasso` | lasso_path, from the design | `lasso-design.scm (--derive)` | loops / BLAS / cuBLAS | no |
119
+ | `CovMultiTaskLasso` | MultiTaskLasso, MultiTaskElasticNet | `lasso-cov.scm` | CPU | no |
120
+ | `CovMultiTaskLassoCV` | MultiTaskLassoCV, MultiTaskElasticNetCV | `lasso-cov.scm` | CPU; GPU batch | no |
121
+ | `CovGroupLasso` | (none; skglm GroupLasso) | `lasso-cov.scm` | CPU | - |
122
+ | `CovLogistic` | LogisticRegression(penalty='l1'), by majorization | `lasso-cov.scm` | CPU | no |
123
+ | `CovRidge` | Ridge(fit_intercept=False), a whole alpha path | `lasso-cov.scm` | CPU | Ridge |
124
+ | `CovBayesianRidge` | BayesianRidge | `lasso-cov.scm` | CPU | no |
125
+ | `CovARDRegression` | ARDRegression (n >= p) | `ard.scm` | loops / BLAS | no |
126
+ | `GramLars` | Lars | `lars.scm` | CPU | no |
127
+ | `GramLassoLars` | LassoLars | `lars.scm` | CPU | no |
128
+ | `GramLassoLarsIC` | LassoLarsIC | `lars.scm` | CPU; device Gram | no |
129
+ | `GramLarsCV` | LarsCV | `lars.scm` | CPU | no |
130
+ | `GramLassoLarsCV` | LassoLarsCV | `lars.scm` | CPU; device Gram | no |
131
+ | `GramOMP` | OrthogonalMatchingPursuit | `omp.scm` | loops / BLAS | no |
132
+ | `GramOMPCV` | OrthogonalMatchingPursuitCV | `omp.scm` | loops / BLAS | no |
133
+ | `LBFGSLogisticRegression` | LogisticRegression(solver='lbfgs') | `logistic-lbfgs.scm, lbfgs.scm` | loops / BLAS / --cuda | LogisticRegression |
134
+ | `LBFGSLogisticRegressionCV` | LogisticRegressionCV | `logistic-lbfgs.scm, lbfgs.scm` | loops / BLAS / --cuda | no |
135
+ | `LBFGSHuberRegressor` | HuberRegressor | `huber.scm, lbfgs.scm` | loops / BLAS / --cuda | no |
136
+ | `LBFGSPoissonRegressor` | PoissonRegressor | `glm.scm, lbfgs.scm` | loops / BLAS / --cuda | no |
137
+ | `LBFGSGammaRegressor` | GammaRegressor | `glm.scm, lbfgs.scm` | loops / BLAS / --cuda | no |
138
+ | `LBFGSTweedieRegressor` | TweedieRegressor | `glm.scm, lbfgs.scm` | loops / BLAS / --cuda | no |
139
+ | `LinearSVM` | LinearSVC(loss='hinge') | `svm-liblinear.scm` | CPU | no |
140
+ | `L1LinearSVM` | LinearSVC(penalty='l1', dual=False) | `svm-l1.scm` | CPU | no |
141
+ | `KernelSVM` | SVC | `svm-smo.scm` | CPU | SVC |
142
+ | `LinearSVR` | LinearSVR | `svr-liblinear.scm` | CPU | no |
143
+ | `KernelSVR` | SVR | `svm-smo.scm` | CPU | SVR |
144
+ | `L1LogisticRegression` | LogisticRegression(penalty='l1', solver='liblinear') | `logistic-l1.scm` | CPU | no |
145
+ | `SGDLinearClassifier` | SGDClassifier(shuffle=False) | `sgd.scm` | CPU | no |
146
+ | `SGDLinearRegressor` | SGDRegressor(shuffle=False) | `sgd.scm` | CPU | no |
147
+ | `SGDPerceptron` | Perceptron(shuffle=False) | `sgd.scm` | CPU | no |
148
+ | `LinearRANSAC` | RANSACRegressor (given subsets) | `ransac.scm` | CPU | no |
149
+ | `WeiszfeldTheilSen` | TheilSenRegressor | `theilsen.scm` | CPU | no |
150
+ | `CDNMF` | NMF(solver='cd') | `nmf-cd.scm (--derive)` | loops / BLAS / cuBLAS | no |
151
+ | `CDGraphicalLasso` | GraphicalLasso | `graphical-lasso.scm` | loops / BLAS | no |
152
+ | `EMGaussianMixture` | GaussianMixture(covariance_type='full') | `gmm.scm` | loops / BLAS / --cuda | no |
153
+ | `ParallelFastICA` | FastICA | `fastica.scm` | loops / BLAS / --cuda | no |
154
+ | `CovPCA` | PCA(svd_solver='covariance_eigh') | `pca.scm` | loops / BLAS / cuBLAS | PCA |
155
+ | `f_regression, f_classif` | f_regression, f_classif | `fscore.scm` | loops / BLAS / cuBLAS | no |
156
+ | `SelectKBest, SelectPercentile` | SelectKBest, SelectPercentile | `fscore.scm` | loops / BLAS / cuBLAS | no |
157
+ | `GramRFE` | RFE(Ridge), RFE(LinearRegression) | `rfe.scm` | loops / BLAS / cuBLAS | inside: Ridge |
158
+
92
159
  ## Two kernels: `CovLasso` and `DerivedLasso`
93
160
 
94
161
  Everything above, and the cross-validation, bootstrap and GPU methods
@@ -733,6 +800,39 @@ estimators (`GramLassoLarsIC`, `GramLarsCV`, `GramLassoLarsCV`,
733
800
  `GramLars`, `GramLassoLars`, `CovPCA`) form a large X'X on the device
734
801
  when one answers. `SCM2CPP_NO_DEVICE=1` keeps everything on the CPU.
735
802
 
803
+ ## Against Intel's sklearnex
804
+
805
+ sklearnex (scikit-learn-intelex 2026.2, oneDAL underneath) replaces
806
+ some scikit-learn estimators with its own. `bench/sklearnex-compare.py`
807
+ times the ones with a counterpart here, sklearnex's estimators
808
+ imported directly (not patched in) so that scikit-learn itself is
809
+ timed in the same process. The ratios are scikit-learn's or
810
+ sklearnex's time over the package's best (its CPU build, or the device
811
+ build where one answers); the load average was 8 to 11, so the times
812
+ move by some tens of percent.
813
+
814
+ | estimator, n x p | iterations (sk / sklearnex / ours) | vs scikit-learn, one core | vs sklearnex, one core | vs sklearnex, 20 cores |
815
+ |---|---|---|---|---|
816
+ | LogisticRegression, 200,000 x 100 | 6 / 12 / 6 | 10.6x (device) | 8.4x (device) | 4.4x (device); on the CPU 0.5x |
817
+ | Ridge (alpha 1), 200,000 x 200 | - | 2.1x | 1.2x | 0.3x |
818
+ | PCA (10 components), 100,000 x 50 | - | 1.5x | 2.4x | 1.7x |
819
+ | PCA (10 components), 50,000 x 500 | - | 1.1x | 1.4x | 0.5x |
820
+ | SVC (rbf), 5,000 x 20 | 4,858 / 7 / 4,882 | 1.2x | 1.4x | 0.5x |
821
+ | SVR (rbf), 5,000 x 20 | 12,821 / 10 / 12,824 | 1.3x | 1.4x | 0.4x |
822
+ | RFE(Ridge), 20 of 200 kept, 20,000 x 200 | - | 216x | 128x | 78x |
823
+
824
+ On one core the package is ahead everywhere. sklearnex uses every
825
+ core by default, and the package's kernels are single-threaded, so
826
+ with 20 cores sklearnex is faster on Ridge, the larger PCA and the
827
+ SVMs; what stays ahead is the device build (logistic regression) and
828
+ an algorithm that does less work (RFE: the Gram once, the Cholesky
829
+ factor downdated per drop, where scikit-learn refits). sklearnex is
830
+ not scikit-learn's arithmetic: its logistic regression takes 12
831
+ iterations to scikit-learn's 6 and its coefficients differ from
832
+ scikit-learn's by 6e-3, where the package's agree to 4e-15; its SVMs
833
+ report a different iteration count. This sklearnex accelerates no
834
+ lasso, so the lasso tables above stand against scikit-learn alone.
835
+
736
836
  ## The GPU build
737
837
 
738
838
  Where the CUDA toolkit is at install time, `ParallelFastICA`,
File without changes