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.
- scm2cpp_learn/__init__.py +3836 -0
- scm2cpp_learn/_classifiers.cp312-win_amd64.pyd +0 -0
- scm2cpp_learn/_compat.py +284 -0
- scm2cpp_learn/_generated/__init__.py +0 -0
- scm2cpp_learn/_generated/_loader.py +41 -0
- scm2cpp_learn/_generated/_loader_accel_cublas.py +173 -0
- scm2cpp_learn/_generated/_loader_classifiers.py +308 -0
- scm2cpp_learn/_generated/_loader_derived_blas.py +29 -0
- scm2cpp_learn/_generated/_loader_derived_cublas.py +29 -0
- scm2cpp_learn/_generated/_loader_derived_loops.py +29 -0
- scm2cpp_learn/_generated/_loader_gpu_cuda.py +313 -0
- scm2cpp_learn/_generated/_loader_gramops_blas.py +692 -0
- scm2cpp_learn/_generated/_loader_gramops_loops.py +711 -0
- scm2cpp_learn/_generated/_loader_nmf_blas.py +29 -0
- scm2cpp_learn/_generated/_loader_nmf_cublas.py +29 -0
- scm2cpp_learn/_generated/_loader_nmf_loops.py +29 -0
- scm2cpp_learn/_generated/accel_cublas/accel.cpp +24 -0
- scm2cpp_learn/_generated/accel_cublas/accel.hpp +1178 -0
- scm2cpp_learn/_generated/accel_cublas/accel_capi.cpp +76 -0
- scm2cpp_learn/_generated/classifiers.cpp +37 -0
- scm2cpp_learn/_generated/classifiers.hpp +3745 -0
- scm2cpp_learn/_generated/classifiers_capi.cpp +115 -0
- scm2cpp_learn/_generated/derived_blas/lasso_design.cpp +5 -0
- scm2cpp_learn/_generated/derived_blas/lasso_design.hpp +156 -0
- scm2cpp_learn/_generated/derived_blas/lasso_design_capi.cpp +19 -0
- scm2cpp_learn/_generated/derived_cublas/lasso_design.cpp +5 -0
- scm2cpp_learn/_generated/derived_cublas/lasso_design.hpp +158 -0
- scm2cpp_learn/_generated/derived_cublas/lasso_design_capi.cpp +19 -0
- scm2cpp_learn/_generated/derived_loops/lasso_design.cpp +5 -0
- scm2cpp_learn/_generated/derived_loops/lasso_design.hpp +179 -0
- scm2cpp_learn/_generated/derived_loops/lasso_design_capi.cpp +19 -0
- scm2cpp_learn/_generated/gpu_cuda/gpu.cpp +37 -0
- scm2cpp_learn/_generated/gpu_cuda/gpu.hpp +3846 -0
- scm2cpp_learn/_generated/gpu_cuda/gpu_capi.cpp +308 -0
- scm2cpp_learn/_generated/gramops_blas/gramops.cpp +82 -0
- scm2cpp_learn/_generated/gramops_blas/gramops.hpp +5411 -0
- scm2cpp_learn/_generated/gramops_blas/gramops_capi.cpp +244 -0
- scm2cpp_learn/_generated/gramops_loops/gramops.cpp +82 -0
- scm2cpp_learn/_generated/gramops_loops/gramops.hpp +6395 -0
- scm2cpp_learn/_generated/gramops_loops/gramops_capi.cpp +250 -0
- scm2cpp_learn/_generated/lasso_cov.cpp +7 -0
- scm2cpp_learn/_generated/lasso_cov.hpp +152 -0
- scm2cpp_learn/_generated/lasso_cov_capi.cpp +25 -0
- scm2cpp_learn/_generated/nmf_blas/nmf_cd.cpp +5 -0
- scm2cpp_learn/_generated/nmf_blas/nmf_cd.hpp +132 -0
- scm2cpp_learn/_generated/nmf_blas/nmf_cd_capi.cpp +19 -0
- scm2cpp_learn/_generated/nmf_cublas/nmf_cd.cpp +5 -0
- scm2cpp_learn/_generated/nmf_cublas/nmf_cd.hpp +136 -0
- scm2cpp_learn/_generated/nmf_cublas/nmf_cd_capi.cpp +19 -0
- scm2cpp_learn/_generated/nmf_loops/nmf_cd.cpp +5 -0
- scm2cpp_learn/_generated/nmf_loops/nmf_cd.hpp +167 -0
- scm2cpp_learn/_generated/nmf_loops/nmf_cd_capi.cpp +19 -0
- scm2cpp_learn/_generated/scm2cpp-blas.hpp +158 -0
- scm2cpp_learn/_generated/scm2cpp-cublas.hpp +155 -0
- scm2cpp_learn/_generated/scm2cpp-cuda.hpp +285 -0
- scm2cpp_learn/_generated/scm2cpp.hpp +1627 -0
- scm2cpp_learn/_gramops_blas.cp312-win_amd64.pyd +0 -0
- scm2cpp_learn/_lasso_cov.cp312-win_amd64.pyd +0 -0
- scm2cpp_learn/_lasso_derived_blas.cp312-win_amd64.pyd +0 -0
- scm2cpp_learn/_libfind.py +153 -0
- scm2cpp_learn/_nmf_blas.cp312-win_amd64.pyd +0 -0
- scm2cpp_learn/batch_capi.cu +428 -0
- scm2cpp_learn-0.18.0.dist-info/DELVEWHEEL +2 -0
- scm2cpp_learn-0.18.0.dist-info/METADATA +1171 -0
- scm2cpp_learn-0.18.0.dist-info/RECORD +72 -0
- scm2cpp_learn-0.18.0.dist-info/WHEEL +5 -0
- scm2cpp_learn-0.18.0.dist-info/top_level.txt +1 -0
- scm2cpp_learn.libs/.load-order-scm2cpp_learn-0.18.0 +4 -0
- scm2cpp_learn.libs/libopenblas-0486735c359a67419d8832e3c40e2837.dll +4 -0
- scm2cpp_learn.libs/msvcp140-73438573e87f9a2fd944b01def13566d.dll +0 -0
- scm2cpp_learn.libs/vcruntime140-052ad6a20d375957e82aa6a3c441ea54.dll +0 -0
- scm2cpp_learn.libs/vcruntime140_1-a198370e11c6e7469d1cf0ef5b3f433d.dll +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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))
|
|
Binary file
|
|
@@ -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']
|