-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathsmw.h
More file actions
409 lines (397 loc) · 17.6 KB
/
Copy pathsmw.h
File metadata and controls
409 lines (397 loc) · 17.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
#ifndef SMW_H
#define SMW_H
#include "pyfgc.h"
#include "blaze/util/Serialization.h"
#include "minicore/util/csc.h"
dist::DissimilarityMeasure assure_dm(py::object obj);
struct SparseMatrixWrapper {
void tofile(std::string path) const {
blaze::Archive<std::ofstream> arch(path);
perform([&arch](auto &x) {arch << x;});
}
void fromfile(std::string path) {
blaze::Archive<std::ifstream> arch(path);
try {
arch >> this->getfloat();
} catch(...) {
arch >> this->getdouble();
}
}
private:
template<typename IndPtrT, typename IndicesT, typename Data>
SparseMatrixWrapper(IndPtrT *indptr, IndicesT *indices, Data *data,
size_t nnz, uint32_t nfeat, uint32_t nitems, bool skip_empty=false, bool use_float=true) {
if(use_float) {
matrix_ = csc2sparse<float>(CSCMatrixView<IndPtrT, IndicesT, Data>(indptr, indices, data, nnz, nfeat, nitems), skip_empty);
auto &m(getfloat());
std::cerr << m;
} else {
matrix_ = csc2sparse<double>(CSCMatrixView<IndPtrT, IndicesT, Data>(indptr, indices, data, nnz, nfeat, nitems), skip_empty);
auto &m(getdouble());
std::cerr << m;
}
}
public:
SparseMatrixWrapper() {}
SparseMatrixWrapper(std::string path) {
fromfile(path);
}
template<typename FT>
SparseMatrixWrapper(blz::SM<FT> &&mat): matrix_(std::move(mat)) {}
blz::SM<float> &getfloat() { return std::get<SMF>(matrix_);}
const blz::SM<float> &getfloat() const { return std::get<SMF>(matrix_);}
blz::SM<double> &getdouble() { return std::get<SMD>(matrix_);}
const blz::SM<double> &getdouble() const { return std::get<SMD>(matrix_);}
template<typename FT>
SparseMatrixWrapper& operator=(blz::SM<FT> &&mat) {
if(is_float()) {
matrix_ = std::move(mat);
} else {
{
SMD tmpmat(std::move(std::get<SMD>(matrix_)));
}
}
return *this;
}
size_t nnz() const {
size_t ret;
perform([&](auto &x) {ret = blz::nonZeros(x);});
return ret;
}
size_t columns() const {
size_t ret;
perform([&](auto &x) {ret = x.columns();});
return ret;
}
size_t rows() const {
size_t ret;
perform([&](auto &x) {ret = x.rows();});
return ret;
}
template<typename IpT, typename IdxT, typename DataT>
SparseMatrixWrapper(IpT *indptr, IdxT *idx, DataT *data, size_t xdim, size_t ydim, size_t nnz, bool use_float=true, bool skip_empty=true) {
if(use_float)
matrix_ = csc2sparse<float>(CSCMatrixView<IpT, IdxT, DataT>(indptr, idx, data, nnz, ydim, xdim));
else
matrix_ = csc2sparse<double>(CSCMatrixView<IpT, IdxT, DataT>(indptr, idx, data, nnz, ydim, xdim));
}
SparseMatrixWrapper(py::object spmat, py::object skip_empty_py, py::object use_float_py) {
if(py::isinstance<py::str>(spmat) || !hasattr(spmat, "indices")) {
*this = SparseMatrixWrapper(spmat.cast<std::string>());
return;
}
py::array indices = spmat.attr("indices");
py::array indptr = spmat.attr("indptr"), data = spmat.attr("data");
py::tuple shape = py::cast<py::tuple>(spmat.attr("shape"));
const bool use_float = py::cast<bool>(use_float_py), skip_empty = py::cast<bool>(skip_empty_py);
size_t xdim = py::cast<size_t>(shape[0]), ydim = py::cast<size_t>(shape[1]);
size_t nnz = py::cast<size_t>(spmat.attr("nnz"));
auto indbuf = indices.request(), indpbuf = indptr.request(), databuf = data.request();
void *datptr = databuf.ptr, *indptrptr = indpbuf.ptr, *indicesptr = indbuf.ptr;
#define __DISPATCH(T1, T2, T3) do { \
if(use_float) {\
if(databuf.readonly || indbuf.readonly) {\
matrix_ = csc2sparse<float>(CSCMatrixView<T1, const T2, const T3>(reinterpret_cast<T1 *>(indptrptr), reinterpret_cast<const T2 *>(const_cast<const void *>(indicesptr)), reinterpret_cast<const T3 *>(const_cast<const void *>(datptr)), nnz, ydim, xdim), skip_empty); \
} else { \
matrix_ = csc2sparse<float>(CSCMatrixView<T1, T2, T3>(reinterpret_cast<T1 *>(indptrptr), reinterpret_cast<T2 *>(indicesptr), reinterpret_cast<T3 *>(datptr), nnz, ydim, xdim), skip_empty); \
}\
} else { \
if(databuf.readonly || indbuf.readonly) {\
matrix_ = csc2sparse<double>(CSCMatrixView<T1, const T2, const T3>(reinterpret_cast<T1 *>(indptrptr), reinterpret_cast<const T2 *>(const_cast<const void *>(indicesptr)), reinterpret_cast<const T3 *>(const_cast<const void *>(datptr)), nnz, ydim, xdim), skip_empty); \
} else { \
matrix_ = csc2sparse<double>(CSCMatrixView<T1, T2, T3>(reinterpret_cast<T1 *>(indptrptr), reinterpret_cast<T2 *>(indicesptr), reinterpret_cast<T3 *>(datptr), nnz, ydim, xdim), skip_empty); \
}\
}\
return; \
} while(0)
#define __DISPATCH_IF(T1, T2, T3) do { \
if(py::format_descriptor<T3>::format() == databuf.format) { \
__DISPATCH(T1, T2, T3); \
} } while(0)
#define __DISPATCH_ALL_IF(T1, T2) do {\
__DISPATCH_IF(T1, T2, uint16_t);\
__DISPATCH_IF(T1, T2, uint32_t);\
__DISPATCH_IF(T1, T2, uint64_t);\
__DISPATCH_IF(T1, T2, int16_t);\
__DISPATCH_IF(T1, T2, int32_t);\
__DISPATCH_IF(T1, T2, int64_t);\
__DISPATCH_IF(T1, T2, float);\
__DISPATCH_IF(T1, T2, double);\
} while(0)
if(indbuf.itemsize == 4) {
if(indpbuf.itemsize == 4) {
__DISPATCH_ALL_IF(uint32_t, uint32_t);
} else {
__DISPATCH_ALL_IF(uint64_t, uint32_t);
}
} else {
assert(indbuf.itemsize == 8);
if(indpbuf.itemsize == 4) {
__DISPATCH_ALL_IF(uint32_t, uint64_t);
} else {
__DISPATCH_ALL_IF(uint64_t, uint64_t);
}
}
throw std::runtime_error("Unexpected type");
#undef __DISPATCH_ALL_IF
#undef __DISPATCH_IF
#undef __DISPATCH
}
std::variant<SMF, SMD> matrix_;
bool is_float() const {
assert(is_float() != is_double());
return std::holds_alternative<SMF>(matrix_);
}
bool is_double() const {
return std::holds_alternative<SMD>(matrix_);
}
template<typename Func>
void perform(const Func &func) {
if(is_float()) func(std::get<SMF>(matrix_));
else func(std::get<SMD>(matrix_));
}
template<typename Func>
void perform(const Func &func) const {
if(is_float()) func(std::get<SMF>(matrix_));
else func(std::get<SMD>(matrix_));
}
std::vector<std::pair<uint32_t, double>> row2tups(size_t r) const {
if(r > rows()) throw std::invalid_argument("Cannot get tuples from a row that dne");
std::vector<std::pair<uint32_t, double>> ret;
perform([&ret,r](const auto &x) {
for(const auto &pair: row(x, r))
ret.emplace_back(pair.index(), pair.value());
});
return ret;
}
std::pair<void *, bool> get_opaque() {
return {is_float() ? static_cast<void *>(&std::get<SMF>(matrix_)): static_cast<void *>(&std::get<SMD>(matrix_)),
is_float()};
}
};
template<typename Mat>
inline py::object py_kmeanspp_noso(Mat &smw, py::object msr, py::int_ k, double gamma_beta, uint64_t seed, unsigned nkmc, unsigned ntimes,
py::ssize_t lspp, bool use_exponential_skips, py::ssize_t n_local_trials,
py::object weights)
{
if(gamma_beta < 0.) {
gamma_beta = 1. / smw.columns();
std::fprintf(stderr, "Warning: unset beta prior defaults to 1 / # columns (%g)\n", gamma_beta);
}
if(nkmc > 1) std::fprintf(stderr, "Warning: nkmc been removed.\n");
if(seed == 0) seed = std::mt19937_64(std::rand())();
const void *wptr = nullptr;
int kind = -1;
const auto mmsr = assure_dm(msr);
const size_t nr = smw.rows();
py::object newarr = py::none();
if(py::isinstance<py::array>(weights)) {
auto arr = py::cast<py::array>(weights);
auto info = arr.request();
if(info.format.size() > 1) throw std::invalid_argument(std::string("Invalid array format: ") + info.format);
switch(info.format.front()) {
case 'f': case 'd': kind = info.format.front(); wptr = info.ptr; break;
default: {
py::array_t<double, py::array::forcecast | py::array::c_style> npa(weights);
wptr = npa.request().ptr; kind = 'd';
newarr = npa;
}
}
}
auto ki = k.cast<Py_ssize_t>();
wy::WyRand<uint64_t> rng(seed);
const auto psum = gamma_beta * smw.columns();
const blz::StaticVector<double, 1> prior({gamma_beta});
py::array_t<uint32_t> ret(ki);
py::object retasn = py::none();
int retasnbits;
if(ki <= 256) {
retasn = py::array_t<uint8_t>(nr);
retasnbits = 8;
} else if(ki <= 63356) {
retasn = py::array_t<uint16_t>(nr);
retasnbits = 16;
} else {
retasn = py::array_t<uint32_t>(nr);
retasnbits = 32;
}
auto retai = py::cast<py::array>(retasn).request();
auto rptr = (uint32_t *)ret.request().ptr;
py::array_t<double> costs(smw.rows());
auto costp = (double *)costs.request().ptr;
blaze::DynamicVector<double> rsums(smw.rows());
smw.perform([&](auto &x) {
using TmpT = typename std::decay_t<decltype(x)>::ElementType;
using FT = std::conditional_t<(sizeof(TmpT) <= 4), float, double>;
using minicore::util::sum;
using blz::sum;
rsums = sum<blaze::rowwise>(x);
auto cmp = [&x,measure=mmsr,rsums=rsums.data(),psum,&prior](size_t xi, size_t yi) {
// Note that this has been transposed
auto rx = row(x, xi), ry = row(x, yi);
return cmp::msr_with_prior<FT>(measure, ry, rx, prior, psum, rsums[yi], rsums[xi]);
};
std::unique_ptr<double[]> tmpw;
switch(kind) {
case 'f': tmpw.reset(new double[nr]); std::copy((float *)wptr, (float *)wptr + nr, tmpw.get()); wptr = (void *)tmpw.get(); break;
case 'd': case -1: break;
default: throw std::runtime_error("Unsupported dtype for weights");
}
auto sol = kmeanspp(cmp, rng, x.rows(), ki, (double *)wptr, lspp, use_exponential_skips, true, n_local_trials);
auto solc = sum(std::get<2>(sol));
for(auto nt = 0u;nt < ntimes; ++nt) {
auto sol2 = kmeanspp(cmp, rng, x.rows(), ki, (double *)wptr, lspp, use_exponential_skips, true, n_local_trials);
auto sol2c = sum(std::get<2>(sol));
if(sol2c < solc) {
std::swap(sol2, sol);
std::swap(sol2c, solc);
std::fprintf(stderr, "Replaced old cost of %0.20g with %0.20g\n", sol2c, solc);
}
}
auto &lidx = std::get<0>(sol);
auto &lasn = std::get<1>(sol);
auto &lcosts = std::get<2>(sol);
switch(retasnbits) {
case 8: {
auto raptr = (uint8_t *)retai.ptr;
OMP_PFOR
for(size_t i = 0; i < lasn.size(); ++i)
raptr[i] = lasn[i];
} break;
case 16: {
auto raptr = (uint16_t *)retai.ptr;
OMP_PFOR
for(size_t i = 0; i < lasn.size(); ++i)
raptr[i] = lasn[i];
} break;
case 32: {
auto raptr = (uint32_t *)retai.ptr;
OMP_PFOR
for(size_t i = 0; i < lasn.size(); ++i)
raptr[i] = lasn[i];
} break;
default: __builtin_unreachable();
}
OMP_PFOR
for(size_t i = 0; i < lcosts.size(); ++i)
costp[i] = lcosts[i];
OMP_PFOR
for(size_t i = 0; i < lidx.size(); ++i)
rptr[i] = lidx[i];
});
return py::make_tuple(ret, retasn, costs);
}
template<typename Mat>
inline py::tuple py_kmeanspp_so(const Mat &smw, const SumOpts &sm, py::object weights) {
return py_kmeanspp_noso(smw, py::int_((int)sm.dis), sm.k, sm.gamma, sm.seed, sm.kmc2_rounds, std::max(sm.extra_sample_tries - 1, 0u),
sm.lspp, sm.use_exponential_skips, sm.n_local_trials, weights);
}
template<typename Mat>
inline py::object py_kmeanspp_noso_dense(Mat &smw, py::object msr, py::int_ k, double gamma_beta, uint64_t seed, unsigned nkmc, unsigned ntimes,
Py_ssize_t lspp, bool use_exponential_skips, py::ssize_t n_local_trials,
py::object weights)
{
if(gamma_beta < 0.) {
gamma_beta = 1. / smw.columns();
std::fprintf(stderr, "Warning: unset beta prior defaults to 1 / # columns (%g)\n", gamma_beta);
}
if(seed == 0) seed = std::mt19937_64(std::rand())();
if(nkmc > 1) std::fprintf(stderr, "Warning: nkmc been removed.\n");
const void *wptr = nullptr;
int kind = -1;
const auto mmsr = assure_dm(msr);
const size_t nr = smw.rows();
py::object carr = py::none();
if(py::isinstance<py::array>(weights)) {
auto arr = py::cast<py::array>(weights);
py::buffer_info winfo = arr.request();
switch(standardize_dtype(winfo.format).front()) {
case 'f': case 'd': kind = winfo.format.front(); wptr = winfo.ptr; break;
default: {
py::array_t<double, py::array::forcecast | py::array::c_style> arr2(arr);
wptr = arr2.request().ptr; carr = arr2;
}
break;
}
}
auto ki = k.cast<Py_ssize_t>();
wy::WyRand<uint64_t> rng(seed);
const auto psum = gamma_beta * smw.columns();
const blz::StaticVector<double, 1> prior({gamma_beta});
py::array_t<uint32_t> ret(ki);
py::object retasn = py::none();
int retasnbits;
if(ki <= 256) {
retasn = py::array_t<uint8_t>(nr);
retasnbits = 8;
} else if(ki <= 63356) {
retasn = py::array_t<uint16_t>(nr);
retasnbits = 16;
} else {
retasn = py::array_t<uint32_t>(nr);
retasnbits = 32;
}
auto retai = py::cast<py::array>(retasn).request();
auto rptr = (uint32_t *)ret.request().ptr;
py::array_t<float> costs(smw.rows());
auto costp = (float *)costs.request().ptr;
using TmpT = typename Mat::ElementType;
using FT = std::conditional_t<sizeof(TmpT) <= 4, float, double>;
using minicore::util::sum;
using blz::sum;
blaze::DynamicVector<double> rsums(smw.rows());
rsums = sum<blaze::rowwise>(smw);
auto cmp = [&smw,measure=mmsr,rsums=rsums.data(),psum,&prior](size_t xi, size_t yi) {
// Note that this has been transposed
auto rx = row(smw, xi, blz::unchecked), ry = row(smw, yi, blz::unchecked);
return cmp::msr_with_prior<FT>(measure, ry, rx, prior, psum, rsums[yi], rsums[xi]);
};
std::unique_ptr<double[]> tmpw;
switch(kind) {
case 'f': tmpw.reset(new double[nr]); std::copy((float *)wptr, (float *)wptr + nr, tmpw.get()); wptr = (void *)tmpw.get(); break;
case 'd': case -1: break;
default: throw std::runtime_error("Unsupported dtype for weights");
}
auto sol = kmeanspp(cmp, rng, smw.rows(), ki, (double *)wptr, lspp, use_exponential_skips, true, n_local_trials);
auto solc = sum(std::get<2>(sol));
for(auto nt = 0u;nt < ntimes; ++nt) {
auto sol2 = kmeanspp(cmp, rng, smw.rows(), ki, (double *)wptr, lspp, use_exponential_skips, true, n_local_trials);
auto sol2c = sum(std::get<2>(sol2));
if(sol2c < solc) {
std::swap(sol2, sol); std::swap(sol2c, solc);
std::fprintf(stderr, "Replaced old cost of %0.20g with %0.20g\n", sol2c, solc);
}
}
auto &lidx = std::get<0>(sol);
auto &lasn = std::get<1>(sol);
auto &lcosts = std::get<2>(sol);
switch(retasnbits) {
case 8: {
auto raptr = (uint8_t *)retai.ptr;
OMP_PFOR
for(size_t i = 0; i < lasn.size(); ++i)
raptr[i] = lasn[i];
} break;
case 16: {
auto raptr = (uint16_t *)retai.ptr;
OMP_PFOR
for(size_t i = 0; i < lasn.size(); ++i)
raptr[i] = lasn[i];
} break;
case 32: {
auto raptr = (uint32_t *)retai.ptr;
OMP_PFOR
for(size_t i = 0; i < lasn.size(); ++i)
raptr[i] = lasn[i];
} break;
default: __builtin_unreachable();
}
//std::fprintf(stderr, "Computed initial centers\n");
for(size_t i = 0; i < lcosts.size(); ++i)
costp[i] = lcosts[i];
for(size_t i = 0; i < lidx.size(); ++i)
rptr[i] = lidx[i];
return py::make_tuple(ret, retasn, costs);
}
dist::DissimilarityMeasure assure_dm(py::object obj);
#endif