scm2cpp-learn 0.18.0__py3-none-win_amd64.whl

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 (72) hide show
  1. scm2cpp_learn/__init__.py +3836 -0
  2. scm2cpp_learn/_classifiers.cp312-win_amd64.pyd +0 -0
  3. scm2cpp_learn/_compat.py +284 -0
  4. scm2cpp_learn/_generated/__init__.py +0 -0
  5. scm2cpp_learn/_generated/_loader.py +41 -0
  6. scm2cpp_learn/_generated/_loader_accel_cublas.py +173 -0
  7. scm2cpp_learn/_generated/_loader_classifiers.py +308 -0
  8. scm2cpp_learn/_generated/_loader_derived_blas.py +29 -0
  9. scm2cpp_learn/_generated/_loader_derived_cublas.py +29 -0
  10. scm2cpp_learn/_generated/_loader_derived_loops.py +29 -0
  11. scm2cpp_learn/_generated/_loader_gpu_cuda.py +313 -0
  12. scm2cpp_learn/_generated/_loader_gramops_blas.py +692 -0
  13. scm2cpp_learn/_generated/_loader_gramops_loops.py +711 -0
  14. scm2cpp_learn/_generated/_loader_nmf_blas.py +29 -0
  15. scm2cpp_learn/_generated/_loader_nmf_cublas.py +29 -0
  16. scm2cpp_learn/_generated/_loader_nmf_loops.py +29 -0
  17. scm2cpp_learn/_generated/accel_cublas/accel.cpp +24 -0
  18. scm2cpp_learn/_generated/accel_cublas/accel.hpp +1178 -0
  19. scm2cpp_learn/_generated/accel_cublas/accel_capi.cpp +76 -0
  20. scm2cpp_learn/_generated/classifiers.cpp +37 -0
  21. scm2cpp_learn/_generated/classifiers.hpp +3745 -0
  22. scm2cpp_learn/_generated/classifiers_capi.cpp +115 -0
  23. scm2cpp_learn/_generated/derived_blas/lasso_design.cpp +5 -0
  24. scm2cpp_learn/_generated/derived_blas/lasso_design.hpp +156 -0
  25. scm2cpp_learn/_generated/derived_blas/lasso_design_capi.cpp +19 -0
  26. scm2cpp_learn/_generated/derived_cublas/lasso_design.cpp +5 -0
  27. scm2cpp_learn/_generated/derived_cublas/lasso_design.hpp +158 -0
  28. scm2cpp_learn/_generated/derived_cublas/lasso_design_capi.cpp +19 -0
  29. scm2cpp_learn/_generated/derived_loops/lasso_design.cpp +5 -0
  30. scm2cpp_learn/_generated/derived_loops/lasso_design.hpp +179 -0
  31. scm2cpp_learn/_generated/derived_loops/lasso_design_capi.cpp +19 -0
  32. scm2cpp_learn/_generated/gpu_cuda/gpu.cpp +37 -0
  33. scm2cpp_learn/_generated/gpu_cuda/gpu.hpp +3846 -0
  34. scm2cpp_learn/_generated/gpu_cuda/gpu_capi.cpp +308 -0
  35. scm2cpp_learn/_generated/gramops_blas/gramops.cpp +82 -0
  36. scm2cpp_learn/_generated/gramops_blas/gramops.hpp +5411 -0
  37. scm2cpp_learn/_generated/gramops_blas/gramops_capi.cpp +244 -0
  38. scm2cpp_learn/_generated/gramops_loops/gramops.cpp +82 -0
  39. scm2cpp_learn/_generated/gramops_loops/gramops.hpp +6395 -0
  40. scm2cpp_learn/_generated/gramops_loops/gramops_capi.cpp +250 -0
  41. scm2cpp_learn/_generated/lasso_cov.cpp +7 -0
  42. scm2cpp_learn/_generated/lasso_cov.hpp +152 -0
  43. scm2cpp_learn/_generated/lasso_cov_capi.cpp +25 -0
  44. scm2cpp_learn/_generated/nmf_blas/nmf_cd.cpp +5 -0
  45. scm2cpp_learn/_generated/nmf_blas/nmf_cd.hpp +132 -0
  46. scm2cpp_learn/_generated/nmf_blas/nmf_cd_capi.cpp +19 -0
  47. scm2cpp_learn/_generated/nmf_cublas/nmf_cd.cpp +5 -0
  48. scm2cpp_learn/_generated/nmf_cublas/nmf_cd.hpp +136 -0
  49. scm2cpp_learn/_generated/nmf_cublas/nmf_cd_capi.cpp +19 -0
  50. scm2cpp_learn/_generated/nmf_loops/nmf_cd.cpp +5 -0
  51. scm2cpp_learn/_generated/nmf_loops/nmf_cd.hpp +167 -0
  52. scm2cpp_learn/_generated/nmf_loops/nmf_cd_capi.cpp +19 -0
  53. scm2cpp_learn/_generated/scm2cpp-blas.hpp +158 -0
  54. scm2cpp_learn/_generated/scm2cpp-cublas.hpp +155 -0
  55. scm2cpp_learn/_generated/scm2cpp-cuda.hpp +285 -0
  56. scm2cpp_learn/_generated/scm2cpp.hpp +1627 -0
  57. scm2cpp_learn/_gramops_blas.cp312-win_amd64.pyd +0 -0
  58. scm2cpp_learn/_lasso_cov.cp312-win_amd64.pyd +0 -0
  59. scm2cpp_learn/_lasso_derived_blas.cp312-win_amd64.pyd +0 -0
  60. scm2cpp_learn/_libfind.py +153 -0
  61. scm2cpp_learn/_nmf_blas.cp312-win_amd64.pyd +0 -0
  62. scm2cpp_learn/batch_capi.cu +428 -0
  63. scm2cpp_learn-0.18.0.dist-info/DELVEWHEEL +2 -0
  64. scm2cpp_learn-0.18.0.dist-info/METADATA +1171 -0
  65. scm2cpp_learn-0.18.0.dist-info/RECORD +72 -0
  66. scm2cpp_learn-0.18.0.dist-info/WHEEL +5 -0
  67. scm2cpp_learn-0.18.0.dist-info/top_level.txt +1 -0
  68. scm2cpp_learn.libs/.load-order-scm2cpp_learn-0.18.0 +4 -0
  69. scm2cpp_learn.libs/libopenblas-0486735c359a67419d8832e3c40e2837.dll +4 -0
  70. scm2cpp_learn.libs/msvcp140-73438573e87f9a2fd944b01def13566d.dll +0 -0
  71. scm2cpp_learn.libs/vcruntime140-052ad6a20d375957e82aa6a3c441ea54.dll +0 -0
  72. scm2cpp_learn.libs/vcruntime140_1-a198370e11c6e7469d1cf0ef5b3f433d.dll +0 -0
@@ -0,0 +1,153 @@
1
+ """Finding the compiled pieces at run time.
2
+
3
+ The CPU kernel is built as an extension module, so its file lands in
4
+ this package directory under whatever name the interpreter's ABI tag
5
+ gives it; ctypes loads it by path. The CUDA library is built only when
6
+ nvcc was present at install time, so its absence is normal and means
7
+ the GPU path is simply unavailable.
8
+ """
9
+ import ctypes
10
+ import functools
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ _HERE = Path(__file__).resolve().parent
15
+
16
+
17
+ # which libraries were built is fixed at install time, and the glob cost
18
+ # 20 us a call: _gram looked twice per fit, a third of a small GramRidge
19
+ @functools.lru_cache(maxsize=None)
20
+ def _find(stem):
21
+ for pattern in (stem + ".*.so", stem + ".so", stem + ".*.pyd",
22
+ stem + ".dll", stem + ".*.dylib", stem + ".dylib"):
23
+ hits = sorted(_HERE.glob(pattern))
24
+ if hits:
25
+ return hits[0]
26
+ return None
27
+
28
+
29
+ def load_kernel_lib():
30
+ """The translated covariance kernel. Required."""
31
+ path = _find("_lasso_cov")
32
+ if path is None:
33
+ raise ImportError(
34
+ "scm2cpp_learn: the compiled kernel is missing from "
35
+ f"{_HERE}. Reinstall the package (pip install --force-reinstall "
36
+ "scm2cpp-learn); building it needs a C++17 compiler.")
37
+ return ctypes.CDLL(str(path))
38
+
39
+
40
+ def load_classifier_lib():
41
+ """The translated classifiers (svm-liblinear.scm, svm-smo.scm,
42
+ logistic-l1.scm in one library). Built with the package."""
43
+ path = _find("_classifiers")
44
+ if path is None:
45
+ raise ImportError(
46
+ "scm2cpp_learn: the compiled classifiers are missing from "
47
+ f"{_HERE}. Reinstall the package (pip install --force-reinstall "
48
+ "scm2cpp-learn); building it needs a C++17 compiler.")
49
+ return ctypes.CDLL(str(path))
50
+
51
+
52
+ def gramops_backend():
53
+ """The Gram-solver library variant that was built: blas or loops."""
54
+ for v in ("blas", "loops"):
55
+ if _find("_gramops_" + v) is not None:
56
+ return v
57
+ raise ImportError("scm2cpp_learn: the BLAS solvers (graphical lasso, OMP, L-BFGS) were not built")
58
+
59
+
60
+ def load_gramops_lib(variant):
61
+ path = _find("_gramops_" + variant)
62
+ if path is None:
63
+ raise ImportError(f"scm2cpp_learn: the Gram solvers' {variant} variant was not built")
64
+ return ctypes.CDLL(str(path))
65
+
66
+
67
+ NMF_VARIANTS = ("cublas", "blas", "loops") # in order of preference
68
+
69
+
70
+ def nmf_backends():
71
+ """The variants of the derived NMF that were built, best first."""
72
+ return [v for v in NMF_VARIANTS if _find("_nmf_" + v) is not None]
73
+
74
+
75
+ def load_nmf_lib(variant):
76
+ """One variant of the derived NMF (nmf-cd.scm --derive with --cublas,
77
+ --blas or neither)."""
78
+ path = _find("_nmf_" + variant)
79
+ if path is None:
80
+ raise ImportError(f"scm2cpp_learn: the derived NMF's {variant} variant was not built")
81
+ return ctypes.CDLL(str(path))
82
+
83
+
84
+ DERIVED_VARIANTS = ("cublas", "blas", "loops") # in order of preference
85
+
86
+
87
+ def derived_backends():
88
+ """The variants of the derived kernel that were built, best first."""
89
+ return [v for v in DERIVED_VARIANTS if _find("_lasso_derived_" + v) is not None]
90
+
91
+
92
+ def load_derived_lib(variant):
93
+ """One variant of the derived kernel (lasso-kernel.scm --derive
94
+ with --cublas, --blas or neither). Raises ImportError when that
95
+ variant was not built; the package catches that per variant."""
96
+ path = _find("_lasso_derived_" + variant)
97
+ if path is None:
98
+ raise ImportError(f"scm2cpp_learn: the derived kernel's {variant} variant was not built")
99
+ return ctypes.CDLL(str(path))
100
+
101
+
102
+ def load_batch_lib():
103
+ """The CUDA batch path, or None when it was not built."""
104
+ path = _find("libscm2cpp_batch")
105
+ if path is None:
106
+ return None
107
+ try:
108
+ lib = ctypes.CDLL(str(path))
109
+ except OSError:
110
+ return None
111
+ lib.scm2cpp_batch_descend.restype = ctypes.c_int
112
+ lib.scm2cpp_batch_descend.argtypes = [
113
+ ctypes.POINTER(ctypes.c_double), ctypes.c_int,
114
+ ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double),
115
+ ctypes.POINTER(ctypes.c_double), ctypes.c_double, ctypes.c_int,
116
+ ctypes.c_int, ctypes.c_double, ctypes.c_int, ctypes.c_int,
117
+ ctypes.c_double]
118
+ # the grid entry, one block per problem: (g, n_grams, c, w, lam,
119
+ # l1_ratio, batch, p, ntask, nobs, cap, chunk, tol, span, mode).
120
+ # A library built from an older source lacks it; the callers ask.
121
+ if hasattr(lib, "scm2cpp_cv_descend"):
122
+ lib.scm2cpp_cv_descend.restype = ctypes.c_int
123
+ lib.scm2cpp_cv_descend.argtypes = [
124
+ ctypes.POINTER(ctypes.c_double), ctypes.c_int,
125
+ ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double),
126
+ ctypes.POINTER(ctypes.c_double), ctypes.c_double, ctypes.c_int,
127
+ ctypes.c_int, ctypes.c_int, ctypes.c_double, ctypes.c_int,
128
+ ctypes.c_int, ctypes.c_double, ctypes.c_int, ctypes.c_int]
129
+ return lib
130
+
131
+
132
+ def accel_built():
133
+ """True when the --cublas build of the selection and Gram products was made."""
134
+ return _find("_accel_cublas") is not None
135
+
136
+
137
+ def load_accel_lib():
138
+ path = _find("_accel_cublas")
139
+ if path is None:
140
+ raise ImportError("scm2cpp_learn: the cuBLAS products (accel_cublas) were not built")
141
+ return ctypes.CDLL(str(path))
142
+
143
+
144
+ def gpu_built():
145
+ """True when libscm2cpp_gpu.so (the --cuda translation) was built."""
146
+ return (_HERE / "libscm2cpp_gpu.so").exists()
147
+
148
+
149
+ def load_gpu_lib():
150
+ path = _HERE / "libscm2cpp_gpu.so"
151
+ if not path.exists():
152
+ raise ImportError("scm2cpp_learn: the --cuda estimators (libscm2cpp_gpu.so) were not built")
153
+ return ctypes.CDLL(str(path))
@@ -0,0 +1,428 @@
1
+ // The GPU entry point the packages expose: a whole regularization path
2
+ // in one launch, one thread per lambda. Coordinate descent is
3
+ // sequential inside a problem, so the parallelism is across the batch --
4
+ // the shape a cross-validation grid already has. The translated
5
+ // enet_descend is called unchanged; it carries __host__ __device__
6
+ // because its body stays inside the device-safe subset, and with
7
+ // l1_ratio = 1 every operation is bit-identical to the pure lasso.
8
+ #include "_generated/lasso_cov.hpp"
9
+ #include <cuda_runtime.h>
10
+
11
+ __global__ void scm2cpp_batch_kernel(const double* g, size_t g_stride,
12
+ double* c, double* beta,
13
+ double* prev, const double* lam,
14
+ double l1r, int cap, int chunk,
15
+ double tol, double nobs, int p,
16
+ int batch) {
17
+ int t = blockIdx.x * blockDim.x + threadIdx.x;
18
+ if (t >= batch) return;
19
+ const double* gt = g + (size_t)t * g_stride; // 0: one shared Gram
20
+ double* ct = c + (size_t)t * p;
21
+ double* bt = beta + (size_t)t * p;
22
+ double* pt = prev + (size_t)t * p;
23
+ double lam1 = lam[t] * l1r;
24
+ double lam2 = lam[t] * (1.0 - l1r);
25
+ int swept = 0;
26
+ while (swept < cap) {
27
+ for (int j = 0; j < p; ++j) pt[j] = bt[j];
28
+ enet_descend(scm2cpp::cspan<double>(gt), scm2cpp::span<double>(ct),
29
+ scm2cpp::span<double>(bt), lam1, lam2, chunk, nobs, p);
30
+ swept += chunk;
31
+ double d = 0.0;
32
+ for (int j = 0; j < p; ++j) {
33
+ double e = bt[j] - pt[j];
34
+ if (e < 0) e = -e;
35
+ if (e > d) d = e;
36
+ }
37
+ if (d < tol) break;
38
+ }
39
+ }
40
+
41
+ // Returns 0 on success, or the CUDA error code. beta and c are
42
+ // batch x p, row-major, updated in place; the caller keeps ownership.
43
+ // n_grams is 1 for one shared Gram matrix, or batch for one per
44
+ // thread -- a bootstrap, where every resample has its own.
45
+ extern "C" int scm2cpp_batch_descend(const double* g, int n_grams,
46
+ double* c, double* beta,
47
+ const double* lam, double l1_ratio,
48
+ int batch, int p, double nobs, int cap,
49
+ int chunk, double tol) {
50
+ size_t g_stride = (n_grams > 1) ? (size_t)p * p : 0;
51
+ size_t gsz = (size_t)p * p * sizeof(double) * (size_t)(n_grams > 1 ? batch : 1);
52
+ size_t bsz = (size_t)batch * p * sizeof(double);
53
+ double *dg = 0, *dc = 0, *db = 0, *dp = 0, *dl = 0;
54
+ cudaError_t e = cudaSuccess;
55
+ #define TRY(call) do { e = (call); if (e != cudaSuccess) goto done; } while (0)
56
+ TRY(cudaMalloc(&dg, gsz));
57
+ TRY(cudaMalloc(&dc, bsz));
58
+ TRY(cudaMalloc(&db, bsz));
59
+ TRY(cudaMalloc(&dp, bsz));
60
+ TRY(cudaMalloc(&dl, (size_t)batch * sizeof(double)));
61
+ TRY(cudaMemcpy(dg, g, gsz, cudaMemcpyHostToDevice));
62
+ TRY(cudaMemcpy(dc, c, bsz, cudaMemcpyHostToDevice));
63
+ TRY(cudaMemcpy(db, beta, bsz, cudaMemcpyHostToDevice));
64
+ TRY(cudaMemcpy(dl, lam, (size_t)batch * sizeof(double),
65
+ cudaMemcpyHostToDevice));
66
+ scm2cpp_batch_kernel<<<(batch + 127) / 128, 128>>>(
67
+ dg, g_stride, dc, db, dp, dl, l1_ratio, cap, chunk, tol, nobs, p,
68
+ batch);
69
+ TRY(cudaDeviceSynchronize());
70
+ TRY(cudaMemcpy(beta, db, bsz, cudaMemcpyDeviceToHost));
71
+ TRY(cudaMemcpy(c, dc, bsz, cudaMemcpyDeviceToHost));
72
+ done:
73
+ #undef TRY
74
+ if (dg) cudaFree(dg);
75
+ if (dc) cudaFree(dc);
76
+ if (db) cudaFree(db);
77
+ if (dp) cudaFree(dp);
78
+ if (dl) cudaFree(dl);
79
+ return (int)e;
80
+ }
81
+
82
+ // ---- the cross-validation grid: one block per problem ----
83
+ //
84
+ // A CV grid is cv folds by num penalties, 500 problems at the usual
85
+ // sizes. One thread each (the kernel above) leaves the device nearly
86
+ // idle and pays each problem the whole descent from zero at one
87
+ // thread's speed, which at p = 1000 loses to the CPU's warm path.
88
+ // Here a whole block serves one problem: the coordinate step itself
89
+ // stays sequential -- thread 0 does the soft threshold, the same
90
+ // arithmetic as the translated enet_descend -- and the O(p) update of
91
+ // the correlations after a coordinate moves is spread over the block.
92
+ // c and beta live in shared memory, the Gram rows stream from global
93
+ // memory one row per moved coordinate, and a coordinate that did not
94
+ // move costs one barrier. Grams are indexed by fold: problem i reads
95
+ // Gram i / per_gram, so a grid carries cv Gram matrices and no copies.
96
+ // Consecutive problems of a fold can be walked warm in runs of `span`
97
+ // by one block, as the CPU path does; measured, span 1 is fastest at
98
+ // every size tried (the device has blocks to spare, and a warm descent
99
+ // still sweeps all p coordinates), so the callers pass 1. The
100
+ // stopping test is the CPU path's relative one, max |db| < tol *
101
+ // max(1, max |b|), and the translated kernels' sweep loop, chunked and
102
+ // stopped when a sweep moves nothing, is kept.
103
+
104
+ __global__ void scm2cpp_cv_block_kernel(const double* g, size_t g_stride,
105
+ int per_gram, double* c,
106
+ double* beta, double* prev,
107
+ const double* lam, double l1r,
108
+ int cap, int chunk, double tol,
109
+ double nobs, int p, int span) {
110
+ extern __shared__ double sh[];
111
+ double* sc = sh; // p: correlations
112
+ double* sb = sc + p; // p: coefficients
113
+ double* sdiag = sb + p; // p: the Gram diagonal
114
+ double* red = sdiag + p; // blockDim: reduction scratch
115
+ __shared__ double s_delta[2]; // by coordinate parity: a write for
116
+ __shared__ int s_moved[2]; // j+1 never races a read for j
117
+ const int tid = threadIdx.x, nt = blockDim.x;
118
+ const int first = blockIdx.x * span;
119
+ const double* gt = g + (size_t)(first / per_gram) * g_stride;
120
+ double* sp = prev + (size_t)blockIdx.x * p;
121
+ for (int k = tid; k < p; k += nt) {
122
+ sc[k] = c[(size_t)first * p + k];
123
+ sb[k] = beta[(size_t)first * p + k];
124
+ sdiag[k] = gt[(size_t)k * p + k];
125
+ }
126
+ __syncthreads();
127
+ for (int i = first; i < first + span; ++i) {
128
+ const double lam1 = lam[i] * l1r * nobs;
129
+ const double lam2 = lam[i] * (1.0 - l1r) * nobs;
130
+ int swept = 0;
131
+ while (swept < cap) {
132
+ for (int k = tid; k < p; k += nt) sp[k] = sb[k];
133
+ __syncthreads();
134
+ for (int s = 0; s < chunk; ++s) {
135
+ if (tid == 0) s_moved[s & 1] = 0;
136
+ for (int j = 0; j < p; ++j) {
137
+ if (tid == 0) {
138
+ double gjj = sdiag[j];
139
+ double old = sb[j];
140
+ double bnew = soft_threshold(sc[j] + old * gjj, lam1)
141
+ / (gjj + lam2);
142
+ sb[j] = bnew;
143
+ double d = bnew - old;
144
+ if (d != 0.0) s_moved[s & 1] = 1;
145
+ s_delta[j & 1] = d;
146
+ }
147
+ __syncthreads();
148
+ double d = s_delta[j & 1];
149
+ if (d != 0.0) {
150
+ const double* row = gt + (size_t)j * p;
151
+ for (int k = tid; k < p; k += nt) sc[k] -= d * row[k];
152
+ __syncthreads();
153
+ }
154
+ }
155
+ __syncthreads();
156
+ if (s_moved[s & 1] == 0) break;
157
+ }
158
+ swept += chunk;
159
+ double dmax = 0.0, bmax = 1.0;
160
+ for (int k = tid; k < p; k += nt) {
161
+ double e = sb[k] - sp[k];
162
+ if (e < 0) e = -e;
163
+ if (e > dmax) dmax = e;
164
+ double a = sb[k] < 0 ? -sb[k] : sb[k];
165
+ if (a > bmax) bmax = a;
166
+ }
167
+ red[tid] = dmax;
168
+ __syncthreads();
169
+ for (int h = nt / 2; h > 0; h >>= 1) {
170
+ if (tid < h && red[tid + h] > red[tid]) red[tid] = red[tid + h];
171
+ __syncthreads();
172
+ }
173
+ dmax = red[0];
174
+ __syncthreads();
175
+ red[tid] = bmax;
176
+ __syncthreads();
177
+ for (int h = nt / 2; h > 0; h >>= 1) {
178
+ if (tid < h && red[tid + h] > red[tid]) red[tid] = red[tid + h];
179
+ __syncthreads();
180
+ }
181
+ bmax = red[0];
182
+ __syncthreads();
183
+ if (dmax < tol * bmax) break;
184
+ }
185
+ for (int k = tid; k < p; k += nt) {
186
+ beta[(size_t)i * p + k] = sb[k];
187
+ c[(size_t)i * p + k] = sc[k];
188
+ }
189
+ }
190
+ }
191
+
192
+ // The multi-task step, the translated mt_descend's arithmetic: thread 0
193
+ // forms the group norm over the tasks of coordinate j and the ntask
194
+ // moves; the block updates the p x ntask correlations. c and w are
195
+ // row-major p x ntask, as the CPU kernel takes them.
196
+ __global__ void scm2cpp_cv_block_kernel_mt(const double* g, size_t g_stride,
197
+ int per_gram, double* c,
198
+ double* w, double* prev,
199
+ const double* lam, double l1r,
200
+ int cap, int chunk, double tol,
201
+ double nobs, int p, int ntask,
202
+ int span) {
203
+ extern __shared__ double sh[];
204
+ const int pt_ = p * ntask;
205
+ double* sc = sh;
206
+ double* sw = sc + pt_;
207
+ double* sdiag = sw + pt_;
208
+ double* red = sdiag + p;
209
+ double* s_delta = red + blockDim.x; // 2 * (ntask + 1), by parity:
210
+ // ntask moves and an any-moved flag
211
+ __shared__ int s_moved[2];
212
+ const int tid = threadIdx.x, nt = blockDim.x;
213
+ const int first = blockIdx.x * span;
214
+ const double* gt = g + (size_t)(first / per_gram) * g_stride;
215
+ double* sp = prev + (size_t)blockIdx.x * pt_;
216
+ for (int k = tid; k < pt_; k += nt) {
217
+ sc[k] = c[(size_t)first * pt_ + k];
218
+ sw[k] = w[(size_t)first * pt_ + k];
219
+ }
220
+ for (int k = tid; k < p; k += nt) sdiag[k] = gt[(size_t)k * p + k];
221
+ __syncthreads();
222
+ for (int i = first; i < first + span; ++i) {
223
+ const double thr = lam[i] * l1r * nobs;
224
+ const double lam2 = lam[i] * (1.0 - l1r) * nobs;
225
+ int swept = 0;
226
+ while (swept < cap) {
227
+ for (int k = tid; k < pt_; k += nt) sp[k] = sw[k];
228
+ __syncthreads();
229
+ for (int s = 0; s < chunk; ++s) {
230
+ if (tid == 0) s_moved[s & 1] = 0;
231
+ for (int j = 0; j < p; ++j) {
232
+ double* dj = s_delta + (j & 1) * (ntask + 1);
233
+ if (tid == 0) {
234
+ double gjj = sdiag[j];
235
+ double nrm2 = 0.0;
236
+ for (int t = 0; t < ntask; ++t) {
237
+ double z = sc[j * ntask + t] + gjj * sw[j * ntask + t];
238
+ nrm2 = nrm2 + z * z;
239
+ }
240
+ double nrm = sqrt(nrm2);
241
+ double scale = (nrm > thr) ? ((nrm - thr) / (nrm * (gjj + lam2)))
242
+ : 0.0;
243
+ int any = 0;
244
+ for (int t = 0; t < ntask; ++t) {
245
+ double old = sw[j * ntask + t];
246
+ double wnew = scale * (sc[j * ntask + t] + gjj * old);
247
+ sw[j * ntask + t] = wnew;
248
+ double d = wnew - old;
249
+ if (d != 0.0) any = 1;
250
+ dj[t] = d;
251
+ }
252
+ if (any) s_moved[s & 1] = 1;
253
+ dj[ntask] = any;
254
+ }
255
+ __syncthreads();
256
+ if (dj[ntask] != 0.0) {
257
+ const double* row = gt + (size_t)j * p;
258
+ for (int idx = tid; idx < pt_; idx += nt) {
259
+ int k = idx / ntask, t = idx - k * ntask;
260
+ double d = dj[t];
261
+ if (d != 0.0) sc[idx] -= d * row[k];
262
+ }
263
+ __syncthreads();
264
+ }
265
+ }
266
+ __syncthreads();
267
+ if (s_moved[s & 1] == 0) break;
268
+ }
269
+ swept += chunk;
270
+ double dmax = 0.0, bmax = 1.0;
271
+ for (int k = tid; k < pt_; k += nt) {
272
+ double e = sw[k] - sp[k];
273
+ if (e < 0) e = -e;
274
+ if (e > dmax) dmax = e;
275
+ double a = sw[k] < 0 ? -sw[k] : sw[k];
276
+ if (a > bmax) bmax = a;
277
+ }
278
+ red[tid] = dmax;
279
+ __syncthreads();
280
+ for (int h = nt / 2; h > 0; h >>= 1) {
281
+ if (tid < h && red[tid + h] > red[tid]) red[tid] = red[tid + h];
282
+ __syncthreads();
283
+ }
284
+ dmax = red[0];
285
+ __syncthreads();
286
+ red[tid] = bmax;
287
+ __syncthreads();
288
+ for (int h = nt / 2; h > 0; h >>= 1) {
289
+ if (tid < h && red[tid + h] > red[tid]) red[tid] = red[tid + h];
290
+ __syncthreads();
291
+ }
292
+ bmax = red[0];
293
+ __syncthreads();
294
+ if (dmax < tol * bmax) break;
295
+ }
296
+ for (int k = tid; k < pt_; k += nt) {
297
+ w[(size_t)i * pt_ + k] = sw[k];
298
+ c[(size_t)i * pt_ + k] = sc[k];
299
+ }
300
+ }
301
+ }
302
+
303
+ // The fallback when the block's state does not fit in shared memory:
304
+ // one thread per run, the translated kernels unchanged, the same
305
+ // relative stopping test. ntask = 0 selects enet_descend on p
306
+ // coefficients, otherwise mt_descend on p x ntask.
307
+ __global__ void scm2cpp_cv_thread_kernel(const double* g, size_t g_stride,
308
+ int per_gram, double* c,
309
+ double* w, double* prev,
310
+ const double* lam, double l1r,
311
+ int cap, int chunk, double tol,
312
+ double nobs, int p, int ntask,
313
+ int span, int nruns) {
314
+ int u = blockIdx.x * blockDim.x + threadIdx.x;
315
+ if (u >= nruns) return;
316
+ const int pt_ = p * (ntask > 0 ? ntask : 1);
317
+ int first = u * span;
318
+ const double* gt = g + (size_t)(first / per_gram) * g_stride;
319
+ double* pv = prev + (size_t)u * pt_;
320
+ for (int i = first; i < first + span; ++i) {
321
+ double* ct = c + (size_t)i * pt_;
322
+ double* wt = w + (size_t)i * pt_;
323
+ if (i > first) { // warm: continue from the previous penalty
324
+ for (int j = 0; j < pt_; ++j) { wt[j] = wt[j - pt_]; ct[j] = ct[j - pt_]; }
325
+ }
326
+ double lam1 = lam[i] * l1r;
327
+ double lam2 = lam[i] * (1.0 - l1r);
328
+ int swept = 0;
329
+ while (swept < cap) {
330
+ for (int j = 0; j < pt_; ++j) pv[j] = wt[j];
331
+ if (ntask > 0)
332
+ mt_descend(scm2cpp::cspan<double>(gt), scm2cpp::span<double>(ct),
333
+ scm2cpp::span<double>(wt), lam1, lam2, chunk, nobs, p,
334
+ ntask);
335
+ else
336
+ enet_descend(scm2cpp::cspan<double>(gt), scm2cpp::span<double>(ct),
337
+ scm2cpp::span<double>(wt), lam1, lam2, chunk, nobs, p);
338
+ swept += chunk;
339
+ double d = 0.0, m = 1.0;
340
+ for (int j = 0; j < pt_; ++j) {
341
+ double e = wt[j] - pv[j];
342
+ if (e < 0) e = -e;
343
+ if (e > d) d = e;
344
+ double a = wt[j] < 0 ? -wt[j] : wt[j];
345
+ if (a > m) m = a;
346
+ }
347
+ if (d < tol * m) break;
348
+ }
349
+ }
350
+ }
351
+
352
+ // The grid entry, single-task (ntask = 0) and multi-task. g holds
353
+ // n_grams Gram matrices and problem i uses Gram i / per_gram with
354
+ // per_gram = batch / n_grams; c and w are batch x p (x ntask),
355
+ // c each problem's fold correlations and w its start, usually zero.
356
+ // Consecutive problems of a fold are walked warm by one block in runs
357
+ // of `span`, which must divide per_gram. mode 0 forces the one-
358
+ // thread-per-run fallback; otherwise the block kernel runs whenever
359
+ // its state fits the device's shared memory. Returns 0, a CUDA error
360
+ // code, or -1 for a shape it cannot take.
361
+ extern "C" int scm2cpp_cv_descend(const double* g, int n_grams, double* c,
362
+ double* w, const double* lam,
363
+ double l1_ratio, int batch, int p,
364
+ int ntask, double nobs, int cap,
365
+ int chunk, double tol, int span,
366
+ int mode) {
367
+ if (n_grams < 1 || batch % n_grams != 0) return -1;
368
+ int per_gram = batch / n_grams;
369
+ if (span < 1 || per_gram % span != 0) return -1;
370
+ int nruns = batch / span;
371
+ int width = p * (ntask > 0 ? ntask : 1);
372
+ int bs = (width <= 128) ? 128 : 256;
373
+ size_t shm = (ntask > 0)
374
+ ? (size_t)(2 * width + p + bs + 2 * (ntask + 1)) * sizeof(double)
375
+ : (size_t)(3 * p + bs) * sizeof(double);
376
+ int dev = 0, optin = 0;
377
+ cudaError_t e = cudaSuccess;
378
+ #define TRY(call) do { e = (call); if (e != cudaSuccess) goto done; } while (0)
379
+ double *dg = 0, *dc = 0, *dw = 0, *dp = 0, *dl = 0;
380
+ size_t g_stride = (size_t)p * p;
381
+ size_t gsz = g_stride * sizeof(double) * (size_t)n_grams;
382
+ size_t bsz = (size_t)batch * width * sizeof(double);
383
+ int block = 0;
384
+ TRY(cudaGetDevice(&dev));
385
+ TRY(cudaDeviceGetAttribute(&optin, cudaDevAttrMaxSharedMemoryPerBlockOptin,
386
+ dev));
387
+ block = (mode != 0 && shm <= (size_t)optin);
388
+ if (block) {
389
+ TRY(cudaFuncSetAttribute(
390
+ ntask > 0 ? (const void*)scm2cpp_cv_block_kernel_mt
391
+ : (const void*)scm2cpp_cv_block_kernel,
392
+ cudaFuncAttributeMaxDynamicSharedMemorySize, (int)shm));
393
+ }
394
+ TRY(cudaMalloc(&dg, gsz));
395
+ TRY(cudaMalloc(&dc, bsz));
396
+ TRY(cudaMalloc(&dw, bsz));
397
+ TRY(cudaMalloc(&dp, (size_t)nruns * width * sizeof(double)));
398
+ TRY(cudaMalloc(&dl, (size_t)batch * sizeof(double)));
399
+ TRY(cudaMemcpy(dg, g, gsz, cudaMemcpyHostToDevice));
400
+ TRY(cudaMemcpy(dc, c, bsz, cudaMemcpyHostToDevice));
401
+ TRY(cudaMemcpy(dw, w, bsz, cudaMemcpyHostToDevice));
402
+ TRY(cudaMemcpy(dl, lam, (size_t)batch * sizeof(double),
403
+ cudaMemcpyHostToDevice));
404
+ if (block && ntask > 0)
405
+ scm2cpp_cv_block_kernel_mt<<<nruns, bs, shm>>>(
406
+ dg, g_stride, per_gram, dc, dw, dp, dl, l1_ratio, cap, chunk, tol,
407
+ nobs, p, ntask, span);
408
+ else if (block)
409
+ scm2cpp_cv_block_kernel<<<nruns, bs, shm>>>(
410
+ dg, g_stride, per_gram, dc, dw, dp, dl, l1_ratio, cap, chunk, tol,
411
+ nobs, p, span);
412
+ else
413
+ scm2cpp_cv_thread_kernel<<<(nruns + 127) / 128, 128>>>(
414
+ dg, g_stride, per_gram, dc, dw, dp, dl, l1_ratio, cap, chunk, tol,
415
+ nobs, p, ntask, span, nruns);
416
+ TRY(cudaGetLastError());
417
+ TRY(cudaDeviceSynchronize());
418
+ TRY(cudaMemcpy(w, dw, bsz, cudaMemcpyDeviceToHost));
419
+ TRY(cudaMemcpy(c, dc, bsz, cudaMemcpyDeviceToHost));
420
+ done:
421
+ #undef TRY
422
+ if (dg) cudaFree(dg);
423
+ if (dc) cudaFree(dc);
424
+ if (dw) cudaFree(dw);
425
+ if (dp) cudaFree(dp);
426
+ if (dl) cudaFree(dl);
427
+ return (int)e;
428
+ }
@@ -0,0 +1,2 @@
1
+ Version: 1.13.2
2
+ Arguments: ['C:\\Users\\runneradmin\\AppData\\Local\\Temp\\cibw-run-g76sssb3\\cp312-win_amd64\\build\\venv\\Scripts\\delvewheel', 'repair', '--add-path', 'C:/openblas/bin', '-w', 'C:\\Users\\runneradmin\\AppData\\Local\\Temp\\cibw-run-g76sssb3\\cp312-win_amd64\\repaired_wheel', '-v', 'C:\\Users\\runneradmin\\AppData\\Local\\Temp\\cibw-run-g76sssb3\\cp312-win_amd64\\built_wheel\\scm2cpp_learn-0.18.0-py3-none-win_amd64.whl']