19 #include <sys/types.h>
24 #include <immintrin.h>
29 #include "AuxIndexStructures.h"
30 #include "FaissAssert.h"
43 int sgemm_ (
const char *transa,
const char *transb, FINTEGER *m, FINTEGER *
44 n, FINTEGER *k,
const float *alpha,
const float *a,
45 FINTEGER *lda,
const float *b, FINTEGER *
46 ldb,
float *beta,
float *c, FINTEGER *ldc);
50 int sgeqrf_ (FINTEGER *m, FINTEGER *n,
float *a, FINTEGER *lda,
51 float *tau,
float *work, FINTEGER *lwork, FINTEGER *info);
53 int sorgqr_(FINTEGER *m, FINTEGER *n, FINTEGER *k,
float *a,
54 FINTEGER *lda,
float *tau,
float *work,
55 FINTEGER *lwork, FINTEGER *info);
57 int sgemv_(
const char *trans, FINTEGER *m, FINTEGER *n,
float *alpha,
58 const float *a, FINTEGER *lda,
const float *x, FINTEGER *incx,
59 float *beta,
float *y, FINTEGER *incy);
72 gettimeofday (&tv,
nullptr);
73 return tv.tv_sec * 1e3 + tv.tv_usec * 1e-3;
83 snprintf (fname, 256,
"/proc/%d/status", pid);
84 FILE * f = fopen (fname,
"r");
85 FAISS_THROW_IF_NOT_MSG (f,
"cannot open proc status file");
89 if (!fgets (buf, 256, f))
break;
90 if (sscanf (buf,
"VmRSS: %ld kB", &sz) == 1)
break;
100 fprintf(stderr,
"WARN: get_mem_usage_kb not implemented on the mac\n");
112 RandomGenerator::RandomGenerator (
long seed)
113 : mt((unsigned int)seed) {}
115 int RandomGenerator::rand_int ()
117 return mt() & 0x7fffffff;
120 long RandomGenerator::rand_long ()
122 return long(rand_int()) | long(rand_int()) << 31;
125 int RandomGenerator::rand_int (
int max)
130 float RandomGenerator::rand_float ()
132 return mt() / float(mt.max());
135 double RandomGenerator::rand_double ()
137 return mt() / double(mt.max());
149 void float_rand (
float * x,
size_t n,
long seed)
152 const size_t nblock = n < 1024 ? 1 : 1024;
154 RandomGenerator rng0 (seed);
155 int a0 = rng0.rand_int (), b0 = rng0.rand_int ();
157 #pragma omp parallel for
158 for (
size_t j = 0; j < nblock; j++) {
160 RandomGenerator rng (a0 + j * b0);
162 const size_t istart = j * n / nblock;
163 const size_t iend = (j + 1) * n / nblock;
165 for (
size_t i = istart; i < iend; i++)
166 x[i] = rng.rand_float ();
171 void float_randn (
float * x,
size_t n,
long seed)
174 const size_t nblock = n < 1024 ? 1 : 1024;
176 RandomGenerator rng0 (seed);
177 int a0 = rng0.rand_int (), b0 = rng0.rand_int ();
179 #pragma omp parallel for
180 for (
size_t j = 0; j < nblock; j++) {
181 RandomGenerator rng (a0 + j * b0);
183 double a = 0, b = 0, s = 0;
186 const size_t istart = j * n / nblock;
187 const size_t iend = (j + 1) * n / nblock;
189 for (
size_t i = istart; i < iend; i++) {
193 a = 2.0 * rng.rand_double () - 1;
194 b = 2.0 * rng.rand_double () - 1;
197 x[i] = a * sqrt(-2.0 * log(s) / s);
200 x[i] = b * sqrt(-2.0 * log(s) / s);
208 void long_rand (
long * x,
size_t n,
long seed)
211 const size_t nblock = n < 1024 ? 1 : 1024;
213 RandomGenerator rng0 (seed);
214 int a0 = rng0.rand_int (), b0 = rng0.rand_int ();
216 #pragma omp parallel for
217 for (
size_t j = 0; j < nblock; j++) {
219 RandomGenerator rng (a0 + j * b0);
221 const size_t istart = j * n / nblock;
222 const size_t iend = (j + 1) * n / nblock;
223 for (
size_t i = istart; i < iend; i++)
224 x[i] = rng.rand_long ();
230 void rand_perm (
int *perm,
size_t n,
long seed)
232 for (
size_t i = 0; i < n; i++) perm[i] = i;
234 RandomGenerator rng (seed);
236 for (
size_t i = 0; i + 1 < n; i++) {
237 int i2 = i + rng.rand_int (n - i);
238 std::swap(perm[i], perm[i2]);
245 void byte_rand (uint8_t * x,
size_t n,
long seed)
248 const size_t nblock = n < 1024 ? 1 : 1024;
250 RandomGenerator rng0 (seed);
251 int a0 = rng0.rand_int (), b0 = rng0.rand_int ();
253 #pragma omp parallel for
254 for (
size_t j = 0; j < nblock; j++) {
256 RandomGenerator rng (a0 + j * b0);
258 const size_t istart = j * n / nblock;
259 const size_t iend = (j + 1) * n / nblock;
262 for (i = istart; i < iend; i++)
263 x[i] = rng.rand_long ();
269 void reflection (
const float * __restrict u,
270 float * __restrict x,
271 size_t n,
size_t d,
size_t nu)
274 for (i = 0; i < n; i++) {
275 const float * up = u;
276 for (l = 0; l < nu; l++) {
277 float ip1 = 0, ip2 = 0;
279 for (j = 0; j < d; j+=2) {
281 ip2 += up[j+1] * x[j+1];
283 float ip = 2 * (ip1 + ip2);
285 for (j = 0; j < d; j++)
295 void reflection_ref (
const float * u,
float * x,
size_t n,
size_t d,
size_t nu)
298 for (i = 0; i < n; i++) {
299 const float * up = u;
300 for (l = 0; l < nu; l++) {
303 for (j = 0; j < d; j++)
307 for (j = 0; j < d; j++)
330 void fvec_inner_products_ny (
float * ip,
340 float one = 1.0, zero = 0.0;
342 sgemv_ (
"T", &di, &nyi, &one, y, &di, x, &onei, &zero, ip, &onei);
345 for (
size_t i = 0; i < ny; i++) {
346 ip[i] = fvec_inner_product (x, y, d);
356 void fvec_norms_L2 (
float * __restrict nr,
357 const float * __restrict x,
361 #pragma omp parallel for
362 for (
size_t i = 0; i < nx; i++) {
363 nr[i] = sqrtf (fvec_norm_L2sqr (x + i * d, d));
367 void fvec_norms_L2sqr (
float * __restrict nr,
368 const float * __restrict x,
371 #pragma omp parallel for
372 for (
size_t i = 0; i < nx; i++)
373 nr[i] = fvec_norm_L2sqr (x + i * d, d);
378 void fvec_renorm_L2 (
size_t d,
size_t nx,
float * __restrict x)
380 #pragma omp parallel for
381 for (
size_t i = 0; i < nx; i++) {
382 float * __restrict xi = x + i * d;
388 const float inv_nr = 1.0 / sqrtf (nr);
389 for (j = 0; j < d; j++)
418 static void knn_inner_product_sse (
const float * x,
420 size_t d,
size_t nx,
size_t ny,
421 float_minheap_array_t * res)
425 #pragma omp parallel for
426 for (
size_t i = 0; i < nx; i++) {
427 const float * x_i = x + i * d;
428 const float * y_j = y;
430 float * __restrict simi = res->get_val(i);
431 long * __restrict idxi = res->get_ids (i);
433 minheap_heapify (k, simi, idxi);
435 for (
size_t j = 0; j < ny; j++) {
436 float ip = fvec_inner_product (x_i, y_j, d);
439 minheap_pop (k, simi, idxi);
440 minheap_push (k, simi, idxi, ip, j);
444 minheap_reorder (k, simi, idxi);
449 static void knn_L2sqr_sse (
452 size_t d,
size_t nx,
size_t ny,
453 float_maxheap_array_t * res)
457 #pragma omp parallel for
458 for (
size_t i = 0; i < nx; i++) {
459 const float * x_i = x + i * d;
460 const float * y_j = y;
462 float * __restrict simi = res->get_val(i);
463 long * __restrict idxi = res->get_ids (i);
465 maxheap_heapify (k, simi, idxi);
466 for (j = 0; j < ny; j++) {
469 if (disij < simi[0]) {
470 maxheap_pop (k, simi, idxi);
471 maxheap_push (k, simi, idxi, disij, j);
475 maxheap_reorder (k, simi, idxi);
482 static void knn_inner_product_blas (
485 size_t d,
size_t nx,
size_t ny,
486 float_minheap_array_t * res)
491 if (nx == 0 || ny == 0)
return;
494 const size_t bs_x = 4096, bs_y = 1024;
496 float *ip_block =
new float[bs_x * bs_y];
498 for (
size_t i0 = 0; i0 < nx; i0 += bs_x) {
499 size_t i1 = i0 + bs_x;
502 for (
size_t j0 = 0; j0 < ny; j0 += bs_y) {
503 size_t j1 = j0 + bs_y;
504 if (j1 > ny) j1 = ny;
507 float one = 1, zero = 0;
508 FINTEGER nyi = j1 - j0, nxi = i1 - i0, di = d;
509 sgemm_ (
"Transpose",
"Not transpose", &nyi, &nxi, &di, &one,
511 x + i0 * d, &di, &zero,
516 res->addn (j1 - j0, ip_block, j0, i0, i1 - i0);
525 template<
class DistanceCorrection>
526 static void knn_L2sqr_blas (
const float * x,
528 size_t d,
size_t nx,
size_t ny,
529 float_maxheap_array_t * res,
530 const DistanceCorrection &corr)
535 if (nx == 0 || ny == 0)
return;
540 const size_t bs_x = 4096, bs_y = 1024;
542 float *ip_block =
new float[bs_x * bs_y];
544 float *x_norms =
new float[nx];
545 fvec_norms_L2sqr (x_norms, x, d, nx);
547 float *y_norms =
new float[ny];
548 fvec_norms_L2sqr (y_norms, y, d, ny);
550 for (
size_t i0 = 0; i0 < nx; i0 += bs_x) {
551 size_t i1 = i0 + bs_x;
554 for (
size_t j0 = 0; j0 < ny; j0 += bs_y) {
555 size_t j1 = j0 + bs_y;
556 if (j1 > ny) j1 = ny;
559 float one = 1, zero = 0;
560 FINTEGER nyi = j1 - j0, nxi = i1 - i0, di = d;
561 sgemm_ (
"Transpose",
"Not transpose", &nyi, &nxi, &di, &one,
563 x + i0 * d, &di, &zero,
568 #pragma omp parallel for
569 for (
size_t i = i0; i < i1; i++) {
570 float * __restrict simi = res->get_val(i);
571 long * __restrict idxi = res->get_ids (i);
572 const float *ip_line = ip_block + (i - i0) * (j1 - j0);
574 for (
size_t j = j0; j < j1; j++) {
575 float ip = *ip_line++;
576 float dis = x_norms[i] + y_norms[j] - 2 * ip;
580 if (dis < 0) dis = 0;
582 dis = corr (dis, i, j);
585 maxheap_pop (k, simi, idxi);
586 maxheap_push (k, simi, idxi, dis, j);
611 int distance_compute_blas_threshold = 20;
615 size_t d,
size_t nx,
size_t ny,
618 if (d % 4 == 0 && nx < distance_compute_blas_threshold) {
619 knn_inner_product_sse (x, y, d, nx, ny, res);
621 knn_inner_product_blas (x, y, d, nx, ny, res);
628 float operator()(
float dis,
size_t ,
size_t )
const {
635 size_t d,
size_t nx,
size_t ny,
638 if (d % 4 == 0 && nx < distance_compute_blas_threshold) {
639 knn_L2sqr_sse (x, y, d, nx, ny, res);
642 knn_L2sqr_blas (x, y, d, nx, ny, res, nop);
647 const float *base_shift;
648 float operator()(
float dis,
size_t ,
size_t bno)
const {
649 return dis - base_shift[bno];
656 size_t d,
size_t nx,
size_t ny,
658 const float *base_shift)
661 knn_L2sqr_blas (x, y, d, nx, ny, res, corr);
672 void fvec_inner_products_by_idx (
float * __restrict ip,
675 const long * __restrict ids,
676 size_t d,
size_t nx,
size_t ny)
678 #pragma omp parallel for
679 for (
size_t j = 0; j < nx; j++) {
680 const long * __restrict idsj = ids + j * ny;
681 const float * xj = x + j * d;
682 float * __restrict ipj = ip + j * ny;
683 for (
size_t i = 0; i < ny; i++) {
686 ipj[i] = fvec_inner_product (xj, y + d * idsj[i], d);
693 void fvec_L2sqr_by_idx (
float * __restrict dis,
696 const long * __restrict ids,
697 size_t d,
size_t nx,
size_t ny)
699 #pragma omp parallel for
700 for (
size_t j = 0; j < nx; j++) {
701 const long * __restrict idsj = ids + j * ny;
702 const float * xj = x + j * d;
703 float * __restrict disj = dis + j * ny;
704 for (
size_t i = 0; i < ny; i++) {
707 disj[i] =
fvec_L2sqr (xj, y + d * idsj[i], d);
718 void knn_inner_products_by_idx (
const float * x,
721 size_t d,
size_t nx,
size_t ny,
722 float_minheap_array_t * res)
726 #pragma omp parallel for
727 for (
size_t i = 0; i < nx; i++) {
728 const float * x_ = x + i * d;
729 const long * idsi = ids + i * ny;
731 float * __restrict simi = res->get_val(i);
732 long * __restrict idxi = res->get_ids (i);
733 minheap_heapify (k, simi, idxi);
735 for (j = 0; j < ny; j++) {
736 if (idsi[j] < 0)
break;
737 float ip = fvec_inner_product (x_, y + d * idsi[j], d);
740 minheap_pop (k, simi, idxi);
741 minheap_push (k, simi, idxi, ip, idsi[j]);
744 minheap_reorder (k, simi, idxi);
749 void knn_L2sqr_by_idx (
const float * x,
751 const long * __restrict ids,
752 size_t d,
size_t nx,
size_t ny,
753 float_maxheap_array_t * res)
757 #pragma omp parallel for
758 for (
size_t i = 0; i < nx; i++) {
759 const float * x_ = x + i * d;
760 const long * __restrict idsi = ids + i * ny;
761 float * __restrict simi = res->get_val(i);
762 long * __restrict idxi = res->get_ids (i);
763 maxheap_heapify (res->k, simi, idxi);
764 for (
size_t j = 0; j < ny; j++) {
765 float disij =
fvec_L2sqr (x_, y + d * idsi[j], d);
767 if (disij < simi[0]) {
768 maxheap_pop (k, simi, idxi);
769 maxheap_push (k, simi, idxi, disij, idsi[j]);
772 maxheap_reorder (res->k, simi, idxi);
788 template <
bool compute_l2>
789 static void range_search_blas (
792 size_t d,
size_t nx,
size_t ny,
794 RangeSearchResult *result)
798 if (nx == 0 || ny == 0)
return;
801 const size_t bs_x = 4096, bs_y = 1024;
803 float *ip_block =
new float[bs_x * bs_y];
805 float *x_norms =
nullptr, *y_norms =
nullptr;
808 x_norms =
new float[nx];
809 fvec_norms_L2sqr (x_norms, x, d, nx);
810 y_norms =
new float[ny];
811 fvec_norms_L2sqr (y_norms, y, d, ny);
814 std::vector <RangeSearchPartialResult *> partial_results;
816 for (
size_t j0 = 0; j0 < ny; j0 += bs_y) {
817 size_t j1 = j0 + bs_y;
818 if (j1 > ny) j1 = ny;
819 RangeSearchPartialResult * pres =
new RangeSearchPartialResult (result);
820 partial_results.push_back (pres);
822 for (
size_t i0 = 0; i0 < nx; i0 += bs_x) {
823 size_t i1 = i0 + bs_x;
828 float one = 1, zero = 0;
829 FINTEGER nyi = j1 - j0, nxi = i1 - i0, di = d;
830 sgemm_ (
"Transpose",
"Not transpose", &nyi, &nxi, &di, &one,
832 x + i0 * d, &di, &zero,
837 for (
size_t i = i0; i < i1; i++) {
838 const float *ip_line = ip_block + (i - i0) * (j1 - j0);
840 RangeSearchPartialResult::QueryResult & qres =
841 pres->new_result (i);
843 for (
size_t j = j0; j < j1; j++) {
844 float ip = *ip_line++;
846 float dis = x_norms[i] + y_norms[j] - 2 * ip;
865 int npres = partial_results.size();
867 for (
size_t i = 0; i < nx; i++) {
868 for (
int j = 0; j < npres; j++)
869 result->lims[i] += partial_results[j]->queries[i].nres;
871 result->do_allocation ();
872 for (
int j = 0; j < npres; j++) {
873 partial_results[j]->set_result (
true);
874 delete partial_results[j];
878 for (
size_t i = nx; i > 0; i--) {
879 result->lims [i] = result->lims [i - 1];
881 result->lims [0] = 0;
886 template <
bool compute_l2>
887 static void range_search_sse (
const float * x,
889 size_t d,
size_t nx,
size_t ny,
891 RangeSearchResult *res)
893 FAISS_THROW_IF_NOT (d % 4 == 0);
897 RangeSearchPartialResult pres (res);
900 for (
size_t i = 0; i < nx; i++) {
901 const float * x_ = x + i * d;
902 const float * y_ = y;
905 RangeSearchPartialResult::QueryResult & qres =
908 for (j = 0; j < ny; j++) {
911 if (disij < radius) {
915 float ip = fvec_inner_product (x_, y_, d);
935 size_t d,
size_t nx,
size_t ny,
940 if (d % 4 == 0 && nx < distance_compute_blas_threshold) {
941 range_search_sse<true> (x, y, d, nx, ny, radius, res);
943 range_search_blas<true> (x, y, d, nx, ny, radius, res);
950 size_t d,
size_t nx,
size_t ny,
955 if (d % 4 == 0 && nx < distance_compute_blas_threshold) {
956 range_search_sse<false> (x, y, d, nx, ny, radius, res);
958 range_search_blas<false> (x, y, d, nx, ny, radius, res);
972 void inner_product_to_L2sqr (
float * __restrict dis,
975 size_t n1,
size_t n2)
978 #pragma omp parallel for
979 for (
size_t j = 0 ; j < n1 ; j++) {
980 float * disj = dis + j * n2;
981 for (
size_t i = 0 ; i < n2 ; i++)
982 disj[i] = nr1[j] + nr2[i] - 2 * disj[i];
989 FAISS_THROW_IF_NOT (m >= n);
990 FINTEGER mi = m, ni = n, ki = mi < ni ? mi : ni;
991 std::vector<float> tau (ki);
992 FINTEGER lwork = -1, info;
995 sgeqrf_ (&mi, &ni, a, &mi, tau.data(),
996 &work_size, &lwork, &info);
997 lwork = size_t(work_size);
998 std::vector<float> work (lwork);
1000 sgeqrf_ (&mi, &ni, a, &mi,
1001 tau.data(), work.data(), &lwork, &info);
1003 sorgqr_ (&mi, &ni, &ki, a, &mi, tau.data(),
1004 work.data(), &lwork, &info);
1010 long nq,
const float *xq,
1011 long nb,
const float *xb,
1013 long ldq,
long ldb,
long ldd)
1015 if (nq == 0 || nb == 0)
return;
1016 if (ldq == -1) ldq = d;
1017 if (ldb == -1) ldb = d;
1018 if (ldd == -1) ldd = nb;
1021 float *b_norms = dis;
1023 #pragma omp parallel for
1024 for (
long i = 0; i < nb; i++)
1027 #pragma omp parallel for
1028 for (
long i = 1; i < nq; i++) {
1030 for (
long j = 0; j < nb; j++)
1031 dis[i * ldd + j] = q_norm + b_norms [j];
1036 for (
long j = 0; j < nb; j++)
1041 FINTEGER nbi = nb, nqi = nq, di = d, ldqi = ldq, ldbi = ldb, lddi = ldd;
1042 float one = 1.0, minus_2 = -2.0;
1044 sgemm_ (
"Transposed",
"Not transposed",
1063 #define EPS (1 / 1024.)
1069 size_t d,
size_t k,
size_t n,
1073 centroids += k_frozen * d;
1075 std::vector<size_t> hassign(k);
1076 memset (centroids, 0,
sizeof(*centroids) * d * k);
1078 #pragma omp parallel
1080 int nt = omp_get_num_threads();
1081 int rank = omp_get_thread_num();
1083 size_t c0 = (k * rank) / nt;
1084 size_t c1 = (k * (rank + 1)) / nt;
1085 const float *xi = x;
1088 for (
size_t i = 0; i < n; i++) {
1089 long ci = assign[i];
1090 assert (ci >= 0 && ci < k + k_frozen);
1092 if (ci >= c0 && ci < c1) {
1093 float * c = centroids + ci * d;
1095 for (
size_t j = 0; j < d; j++)
1104 #pragma omp parallel for
1105 for (
size_t ci = 0; ci < k; ci++) {
1106 float * c = centroids + ci * d;
1107 float ni = (float) hassign[ci];
1109 for (
size_t j = 0; j < d; j++)
1117 for (
size_t ci = 0; ci < k; ci++) {
1118 if (hassign[ci] == 0) {
1120 for (cj = 0; 1; cj = (cj + 1) % k) {
1122 float p = (hassign[cj] - 1.0) / (float) (n - k);
1128 memcpy (centroids+ci*d, centroids+cj*d,
sizeof(*centroids) * d);
1131 for (
size_t j = 0; j < d; j++) {
1133 centroids[ci * d + j] *= 1 + EPS;
1134 centroids[cj * d + j] *= 1 - EPS;
1136 centroids[ci * d + j] *= 1 - EPS;
1137 centroids[cj * d + j] *= 1 + EPS;
1142 hassign[ci] = hassign[cj] / 2;
1143 hassign[cj] -= hassign[ci];
1162 float prev_dis = -1e38;
1164 for (
int i = 0; i < k; i++) {
1165 if (dis[i] != prev_dis) {
1166 if (i > prev_i + 1) {
1168 std::sort (idx + prev_i, idx + i);
1177 long *I0,
float *D0,
1178 const long *I1,
const float *D1,
1184 #pragma omp parallel reduction(+:n1)
1186 std::vector<long> tmpI (k);
1187 std::vector<float> tmpD (k);
1190 for (
size_t i = 0; i < n; i++) {
1191 long *lI0 = I0 + i * k;
1192 float *lD0 = D0 + i * k;
1193 const long *lI1 = I1 + i * k;
1194 const float *lD1 = D1 + i * k;
1199 for (
size_t j = 0; j < k; j++) {
1201 if (lI0[r0] >= 0 && lD0[r0] < lD1[r1]) {
1205 }
else if (lD1[r1] >= 0) {
1207 tmpI[j] = lI1[r1] + translation;
1215 for (
size_t j = 0; j < k; j++) {
1216 if (lI0[r0] >= 0 && lD0[r0] > lD1[r1]) {
1220 }
else if (lD1[r1] >= 0) {
1222 tmpI[j] = lI1[r1] + translation;
1231 memcpy (lD0, tmpD.data(),
sizeof (lD0[0]) * k);
1232 memcpy (lI0, tmpI.data(),
sizeof (lI0[0]) * k);
1242 size_t k2,
const long *v2_in)
1245 long *v2 =
new long [k2];
1246 memcpy (v2, v2_in,
sizeof (
long) * k2);
1247 std::sort (v2, v2 + k2);
1251 for (
size_t i = 0; i < k2; i++) {
1252 if (v2 [i] != prev) {
1253 v2[wp++] = prev = v2 [i];
1258 const long seen_flag = 1L << 60;
1260 for (
size_t i = 0; i < k1; i++) {
1262 size_t i0 = 0, i1 = k2;
1263 while (i0 + 1 < i1) {
1264 size_t imed = (i1 + i0) / 2;
1265 long piv = v2 [imed] & ~seen_flag;
1266 if (piv <= q) i0 = imed;
1271 v2 [i0] |= seen_flag;
1280 double tot = 0, uf = 0;
1282 for (
int i = 0 ; i < k ; i++) {
1284 uf += hist[i] * (double) hist[i];
1286 uf = uf * k / (tot * tot);
1293 std::vector<int> hist(k, 0);
1294 for (
int i = 0; i < n; i++) {
1303 int ivec_hist (
size_t n,
const int * v,
int vmax,
int *hist) {
1304 memset (hist, 0,
sizeof(hist[0]) * vmax);
1307 if (v[n] < 0 || v[n] >= vmax) nout++;
1316 FAISS_THROW_IF_NOT (nbits % 8 == 0);
1317 size_t d = nbits / 8;
1318 std::vector<int> accu(d * 256);
1319 const uint8_t *c = codes;
1320 for (
size_t i = 0; i < n; i++)
1321 for(
int j = 0; j < d; j++)
1322 accu[j * 256 + *c++]++;
1323 memset (hist, 0,
sizeof(*hist) * nbits);
1324 for (
int i = 0; i < d; i++) {
1325 const int *ai = accu.data() + i * 256;
1326 int * hi = hist + i * 8;
1327 for (
int j = 0; j < 256; j++)
1328 for (
int k = 0; k < 8; k++)
1340 while (n--) cs = cs * 65713 + a[n] * 1686049;
1346 struct ArgsortComparator {
1348 bool operator() (
const size_t a,
const size_t b)
const {
1349 return vals[a] < vals[b];
1356 size_t len()
const {
1366 template<
typename T>
1367 void parallel_merge (
const T *src, T *dst,
1368 SegmentS &s1, SegmentS & s2,
int nt,
1369 const ArgsortComparator & comp) {
1370 if (s2.len() > s1.len()) {
1375 SegmentS s1s[nt], s2s[nt], sws[nt];
1377 s2s[nt - 1].i1 = s2.i1;
1380 #pragma omp parallel for num_threads(nt)
1381 for (
int t = 0; t < nt; t++) {
1382 s1s[t].i0 = s1.i0 + s1.len() * t / nt;
1383 s1s[t].i1 = s1.i0 + s1.len() * (t + 1) / nt;
1386 T pivot = src[s1s[t].i1];
1387 size_t i0 = s2.i0, i1 = s2.i1;
1388 while (i0 + 1 < i1) {
1389 size_t imed = (i1 + i0) / 2;
1390 if (comp (pivot, src[imed])) {i1 = imed; }
1393 s2s[t].i1 = s2s[t + 1].i0 = i1;
1396 s1.i0 = std::min(s1.i0, s2.i0);
1397 s1.i1 = std::max(s1.i1, s2.i1);
1400 for (
int t = 0; t < nt; t++) {
1401 sws[t].i1 = sws[t].i0 + s1s[t].len() + s2s[t].len();
1403 sws[t + 1].i0 = sws[t].i1;
1406 assert(sws[nt - 1].i1 == s1.i1);
1409 #pragma omp parallel for num_threads(nt)
1410 for (
int t = 0; t < nt; t++) {
1411 SegmentS sw = sws[t];
1412 SegmentS s1t = s1s[t];
1413 SegmentS s2t = s2s[t];
1414 if (s1t.i0 < s1t.i1 && s2t.i0 < s2t.i1) {
1417 if (comp(src[s1t.i0], src[s2t.i0])) {
1418 dst[sw.i0++] = src[s1t.i0++];
1419 if (s1t.i0 == s1t.i1)
break;
1421 dst[sw.i0++] = src[s2t.i0++];
1422 if (s2t.i0 == s2t.i1)
break;
1426 if (s1t.len() > 0) {
1427 assert(s1t.len() == sw.len());
1428 memcpy(dst + sw.i0, src + s1t.i0, s1t.len() *
sizeof(dst[0]));
1429 }
else if (s2t.len() > 0) {
1430 assert(s2t.len() == sw.len());
1431 memcpy(dst + sw.i0, src + s2t.i0, s2t.len() *
sizeof(dst[0]));
1438 void fvec_argsort (
size_t n,
const float *vals,
1441 for (
size_t i = 0; i < n; i++) perm[i] = i;
1442 ArgsortComparator comp = {vals};
1443 std::sort (perm, perm + n, comp);
1446 void fvec_argsort_parallel (
size_t n,
const float *vals,
1449 size_t * perm2 =
new size_t[n];
1451 size_t *permB = perm2, *permA = perm;
1453 int nt = omp_get_max_threads();
1458 nseg = (nseg + 1) / 2;
1459 std::swap (permA, permB);
1463 #pragma omp parallel
1464 for (
size_t i = 0; i < n; i++) permA[i] = i;
1466 ArgsortComparator comp = {vals};
1471 #pragma omp parallel for
1472 for (
int t = 0; t < nt; t++) {
1473 size_t i0 = t * n / nt;
1474 size_t i1 = (t + 1) * n / nt;
1475 SegmentS seg = {i0, i1};
1476 std::sort (permA + seg.i0, permA + seg.i1, comp);
1479 int prev_nested = omp_get_nested();
1484 int nseg1 = (nseg + 1) / 2;
1485 int sub_nt = nseg % 2 == 0 ? nt : nt - 1;
1486 int sub_nseg1 = nseg / 2;
1488 #pragma omp parallel for num_threads(nseg1)
1489 for (
int s = 0; s < nseg; s += 2) {
1490 if (s + 1 == nseg) {
1491 memcpy(permB + segs[s].i0, permA + segs[s].i0,
1492 segs[s].len() *
sizeof(
size_t));
1494 int t0 = s * sub_nt / sub_nseg1;
1495 int t1 = (s + 1) * sub_nt / sub_nseg1;
1496 printf(
"merge %d %d, %d threads\n", s, s + 1, t1 - t0);
1497 parallel_merge(permA, permB, segs[s], segs[s + 1],
1501 for (
int s = 0; s < nseg; s += 2)
1502 segs[s / 2] = segs[s];
1504 std::swap (permA, permB);
1506 assert (permA == perm);
1507 omp_set_nested(prev_nested);
1529 size_t d,
size_t *n,
size_t nmax,
const float *x,
1530 bool verbose,
long seed)
1533 if (*n <= nmax)
return x;
1537 printf (
" Input training set too big (max size is %ld), sampling "
1538 "%ld / %ld vectors\n", nmax, n2, *n);
1540 std::vector<int> subset (*n);
1541 rand_perm (subset.data (), *n, seed);
1542 float *x_subset =
new float[n2 * d];
1543 for (
long i = 0; i < n2; i++)
1544 memcpy (&x_subset[i * d],
1545 &x[subset[i] *
size_t(d)],
1553 for (
size_t i = 0; i < d; ++i) {
1554 x_out[i] = 2 * ((x_in[i >> 3] >> (i & 7)) & 1) - 1;
1559 for (
size_t i = 0; i < d / 8; ++i) {
1561 for (
int j = 0; j < 8; ++j) {
1562 if (x_in[8 * i + j] > 0) {
random generator that can be used in multithreaded contexts
int km_update_centroids(const float *x, float *centroids, long *assign, size_t d, size_t k, size_t n, size_t k_frozen)
void knn_L2sqr_base_shift(const float *x, const float *y, size_t d, size_t nx, size_t ny, float_maxheap_array_t *res, const float *base_shift)
float fvec_L2sqr(const float *x, const float *y, size_t d)
Squared L2 distance between two vectors.
void bincode_hist(size_t n, size_t nbits, const uint8_t *codes, int *hist)
const float * fvecs_maybe_subsample(size_t d, size_t *n, size_t nmax, const float *x, bool verbose, long seed)
void ranklist_handle_ties(int k, long *idx, const float *dis)
float rand_float()
between 0 and 1
size_t get_mem_usage_kb()
get current RSS usage in kB
int ivec_hist(size_t n, const int *v, int vmax, int *hist)
compute histogram on v
size_t merge_result_table_with(size_t n, size_t k, long *I0, float *D0, const long *I1, const float *D1, bool keep_min, long translation)
size_t ranklist_intersection_size(size_t k1, const long *v1, size_t k2, const long *v2_in)
void pairwise_L2sqr(long d, long nq, const float *xq, long nb, const float *xb, float *dis, long ldq, long ldb, long ldd)
void range_search_inner_product(const float *x, const float *y, size_t d, size_t nx, size_t ny, float radius, RangeSearchResult *res)
same as range_search_L2sqr for the inner product similarity
void knn_inner_product(const float *x, const float *y, size_t d, size_t nx, size_t ny, float_minheap_array_t *res)
double getmillisecs()
ms elapsed since some arbitrary epoch
void real_to_binary(size_t d, const float *x_in, uint8_t *x_out)
double imbalance_factor(int k, const int *hist)
same, takes a histogram as input
float fvec_norm_L2sqr(const float *x, size_t d)
void range_search_L2sqr(const float *x, const float *y, size_t d, size_t nx, size_t ny, float radius, RangeSearchResult *res)
void matrix_qr(int m, int n, float *a)
size_t ivec_checksum(size_t n, const int *a)
compute a checksum on a table.
void knn_L2sqr(const float *x, const float *y, size_t d, size_t nx, size_t ny, float_maxheap_array_t *res)
void binary_to_real(size_t d, const uint8_t *x_in, float *x_out)