Faiss

- Main Page
- Namespaces
- Classes
- Files

- File List

All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends
/tmp/faiss/VectorTransform.cpp
1 /**
2 * Copyright (c) 2015-present, Facebook, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD+Patents license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9 // -*- c++ -*-
10
11 #include "VectorTransform.h"
12
13 #include <cstdio>
14 #include <cmath>
15 #include <cstring>
16
17 #include "utils.h"
18 #include "FaissAssert.h"
19 #include "IndexPQ.h"
20
21 using namespace faiss;
22
23
24 extern "C" {
25
26 // this is to keep the clang syntax checker happy
27 #ifndef FINTEGER
28 #define FINTEGER int
29 #endif
30
31
32 /* declare BLAS functions, see http://www.netlib.org/clapack/cblas/ */
33
34 int sgemm_ (
35 const char *transa, const char *transb, FINTEGER *m, FINTEGER *
36 n, FINTEGER *k, const float *alpha, const float *a,
37 FINTEGER *lda, const float *b,
38 FINTEGER *ldb, float *beta,
39 float *c, FINTEGER *ldc);
40
41 int ssyrk_ (
42 const char *uplo, const char *trans, FINTEGER *n, FINTEGER *k,
43 float *alpha, float *a, FINTEGER *lda,
44 float *beta, float *c, FINTEGER *ldc);
45
46 /* Lapack functions from http://www.netlib.org/clapack/old/single/ */
47
48 int ssyev_ (
49 const char *jobz, const char *uplo, FINTEGER *n, float *a,
50 FINTEGER *lda, float *w, float *work, FINTEGER *lwork,
51 FINTEGER *info);
52
53 int dsyev_ (
54 const char *jobz, const char *uplo, FINTEGER *n, double *a,
55 FINTEGER *lda, double *w, double *work, FINTEGER *lwork,
56 FINTEGER *info);
57
58 int sgesvd_(
59 const char *jobu, const char *jobvt, FINTEGER *m, FINTEGER *n,
60 float *a, FINTEGER *lda, float *s, float *u, FINTEGER *ldu, float *vt,
61 FINTEGER *ldvt, float *work, FINTEGER *lwork, FINTEGER *info);
62
63 }
64
65 /*********************************************
66 * VectorTransform
67 *********************************************/
68
69
70
71 float * VectorTransform::apply (Index::idx_t n, const float * x) const
72 {
73 float * xt = new float[n * d_out];
74 apply_noalloc (n, x, xt);
75 return xt;
76 }
77
78
79 void VectorTransform::train (idx_t, const float *) {
80 // does nothing by default
81 }
82
83
84 void VectorTransform::reverse_transform (
85 idx_t , const float *,
86 float *) const
87 {
88 FAISS_THROW_MSG ("reverse transform not implemented");
89 }
90
91
92
93
94 /*********************************************
95 * LinearTransform
96 *********************************************/
97
98 /// both d_in > d_out and d_out < d_in are supported
99 LinearTransform::LinearTransform (int d_in, int d_out,
100 bool have_bias):
101 VectorTransform (d_in, d_out), have_bias (have_bias),
102 is_orthonormal (false), verbose (false)
103 {
104 is_trained = false; // will be trained when A and b are initialized
105 }
106
107 void LinearTransform::apply_noalloc (Index::idx_t n, const float * x,
108 float * xt) const
109 {
110 FAISS_THROW_IF_NOT_MSG(is_trained, "Transformation not trained yet");
111
112 float c_factor;
113 if (have_bias) {
114 FAISS_THROW_IF_NOT_MSG (b.size() == d_out, "Bias not initialized");
115 float * xi = xt;
116 for (int i = 0; i < n; i++)
117 for(int j = 0; j < d_out; j++)
118 *xi++ = b[j];
119 c_factor = 1.0;
120 } else {
121 c_factor = 0.0;
122 }
123
124 FAISS_THROW_IF_NOT_MSG (A.size() == d_out * d_in,
125 "Transformation matrix not initialized");
126
127 float one = 1;
128 FINTEGER nbiti = d_out, ni = n, di = d_in;
129 sgemm_ ("Transposed", "Not transposed",
130 &nbiti, &ni, &di,
131 &one, A.data(), &di, x, &di, &c_factor, xt, &nbiti);
132
133 }
134
135
136 void LinearTransform::transform_transpose (idx_t n, const float * y,
137 float *x) const
138 {
139 if (have_bias) { // allocate buffer to store bias-corrected data
140 float *y_new = new float [n * d_out];
141 const float *yr = y;
142 float *yw = y_new;
143 for (idx_t i = 0; i < n; i++) {
144 for (int j = 0; j < d_out; j++) {
145 *yw++ = *yr++ - b [j];
146 }
147 }
148 y = y_new;
149 }
150
151 {
152 FINTEGER dii = d_in, doi = d_out, ni = n;
153 float one = 1.0, zero = 0.0;
154 sgemm_ ("Not", "Not", &dii, &ni, &doi,
155 &one, A.data (), &dii, y, &doi, &zero, x, &dii);
156 }
157
158 if (have_bias) delete [] y;
159 }
160
161 void LinearTransform::set_is_orthonormal ()
162 {
163 if (d_out > d_in) {
164 // not clear what we should do in this case
165 is_orthonormal = false;
166 return;
167 }
168 if (d_out == 0) { // borderline case, unnormalized matrix
169 is_orthonormal = true;
170 return;
171 }
172
173 double eps = 4e-5;
174 FAISS_ASSERT(A.size() >= d_out * d_in);
175 {
176 std::vector<float> ATA(d_out * d_out);
177 FINTEGER dii = d_in, doi = d_out;
178 float one = 1.0, zero = 0.0;
179
180 sgemm_ ("Transposed", "Not", &doi, &doi, &dii,
181 &one, A.data (), &dii,
182 A.data(), &dii,
183 &zero, ATA.data(), &doi);
184
185 is_orthonormal = true;
186 for (long i = 0; i < d_out; i++) {
187 for (long j = 0; j < d_out; j++) {
188 float v = ATA[i + j * d_out];
189 if (i == j) v-= 1;
190 if (fabs(v) > eps) {
191 is_orthonormal = false;
192 }
193 }
194 }
195 }
196
197 }
198
199
200 void LinearTransform::reverse_transform (idx_t n, const float * xt,
201 float *x) const
202 {
203 if (is_orthonormal) {
204 transform_transpose (n, xt, x);
205 } else {
206 FAISS_THROW_MSG ("reverse transform not implemented for non-orthonormal matrices");
207 }
208 }
209
210
211
212 /*********************************************
213 * RandomRotationMatrix
214 *********************************************/
215
216 void RandomRotationMatrix::init (int seed)
217 {
218
219 if(d_out <= d_in) {
220 A.resize (d_out * d_in);
221 float *q = A.data();
222 float_randn(q, d_out * d_in, seed);
223 matrix_qr(d_in, d_out, q);
224 } else {
225 A.resize (d_out * d_out);
226 float *q = A.data();
227 float_randn(q, d_out * d_out, seed);
228 matrix_qr(d_out, d_out, q);
229 // remove columns
230 int i, j;
231 for (i = 0; i < d_out; i++) {
232 for(j = 0; j < d_in; j++) {
233 q[i * d_in + j] = q[i * d_out + j];
234 }
235 }
236 A.resize(d_in * d_out);
237 }
238 is_orthonormal = true;
239 is_trained = true;
240 }
241
242 void RandomRotationMatrix::train (Index::idx_t n, const float *x)
243 {
244 // initialize with some arbitrary seed
245 init (12345);
246 }
247
248
249 /*********************************************
250 * PCAMatrix
251 *********************************************/
252
253 PCAMatrix::PCAMatrix (int d_in, int d_out,
254 float eigen_power, bool random_rotation):
255 LinearTransform(d_in, d_out, true),
256 eigen_power(eigen_power), random_rotation(random_rotation)
257 {
258 is_trained = false;
259 max_points_per_d = 1000;
260 balanced_bins = 0;
261 }
262
263
264 namespace {
265
266 /// Compute the eigenvalue decomposition of symmetric matrix cov,
267 /// dimensions d_in-by-d_in. Output eigenvectors in cov.
268
269 void eig(size_t d_in, double *cov, double *eigenvalues, int verbose)
270 {
271 { // compute eigenvalues and vectors
272 FINTEGER info = 0, lwork = -1, di = d_in;
273 double workq;
274
275 dsyev_ ("Vectors as well", "Upper",
276 &di, cov, &di, eigenvalues, &workq, &lwork, &info);
277 lwork = FINTEGER(workq);
278 double *work = new double[lwork];
279
280 dsyev_ ("Vectors as well", "Upper",
281 &di, cov, &di, eigenvalues, work, &lwork, &info);
282
283 delete [] work;
284
285 if (info != 0) {
286 fprintf (stderr, "WARN ssyev info returns %d, "
287 "a very bad PCA matrix is learnt\n",
288 int(info));
289 // do not throw exception, as the matrix could still be useful
290 }
291
292
293 if(verbose && d_in <= 10) {
294 printf("info=%ld new eigvals=[", long(info));
295 for(int j = 0; j < d_in; j++) printf("%g ", eigenvalues[j]);
296 printf("]\n");
297
298 double *ci = cov;
299 printf("eigenvecs=\n");
300 for(int i = 0; i < d_in; i++) {
301 for(int j = 0; j < d_in; j++)
302 printf("%10.4g ", *ci++);
303 printf("\n");
304 }
305 }
306
307 }
308
309 // revert order of eigenvectors & values
310
311 for(int i = 0; i < d_in / 2; i++) {
312
313 std::swap(eigenvalues[i], eigenvalues[d_in - 1 - i]);
314 double *v1 = cov + i * d_in;
315 double *v2 = cov + (d_in - 1 - i) * d_in;
316 for(int j = 0; j < d_in; j++)
317 std::swap(v1[j], v2[j]);
318 }
319
320 }
321
322
323 }
324
325 void PCAMatrix::train (Index::idx_t n, const float *x)
326 {
327 const float * x_in = x;
328
329 x = fvecs_maybe_subsample (d_in, (size_t*)&n,
330 max_points_per_d * d_in, x, verbose);
331
332 ScopeDeleter<float> del_x (x != x_in ? x : nullptr);
333
334 // compute mean
335 mean.clear(); mean.resize(d_in, 0.0);
336 if (have_bias) { // we may want to skip the bias
337 const float *xi = x;
338 for (int i = 0; i < n; i++) {
339 for(int j = 0; j < d_in; j++)
340 mean[j] += *xi++;
341 }
342 for(int j = 0; j < d_in; j++)
343 mean[j] /= n;
344 }
345 if(verbose) {
346 printf("mean=[");
347 for(int j = 0; j < d_in; j++) printf("%g ", mean[j]);
348 printf("]\n");
349 }
350
351 if(n >= d_in) {
352 // compute covariance matrix, store it in PCA matrix
353 PCAMat.resize(d_in * d_in);
354 float * cov = PCAMat.data();
355 { // initialize with mean * mean^T term
356 float *ci = cov;
357 for(int i = 0; i < d_in; i++) {
358 for(int j = 0; j < d_in; j++)
359 *ci++ = - n * mean[i] * mean[j];
360 }
361 }
362 {
363 FINTEGER di = d_in, ni = n;
364 float one = 1.0;
365 ssyrk_ ("Up", "Non transposed",
366 &di, &ni, &one, (float*)x, &di, &one, cov, &di);
367
368 }
369 if(verbose && d_in <= 10) {
370 float *ci = cov;
371 printf("cov=\n");
372 for(int i = 0; i < d_in; i++) {
373 for(int j = 0; j < d_in; j++)
374 printf("%10g ", *ci++);
375 printf("\n");
376 }
377 }
378
379 std::vector<double> covd (d_in * d_in);
380 for (size_t i = 0; i < d_in * d_in; i++) covd [i] = cov [i];
381
382 std::vector<double> eigenvaluesd (d_in);
383
384 eig (d_in, covd.data (), eigenvaluesd.data (), verbose);
385
386 for (size_t i = 0; i < d_in * d_in; i++) PCAMat [i] = covd [i];
387 eigenvalues.resize (d_in);
388
389 for (size_t i = 0; i < d_in; i++)
390 eigenvalues [i] = eigenvaluesd [i];
391
392
393 } else {
394
395 std::vector<float> xc (n * d_in);
396
397 for (size_t i = 0; i < n; i++)
398 for(size_t j = 0; j < d_in; j++)
399 xc [i * d_in + j] = x [i * d_in + j] - mean[j];
400
401 // compute Gram matrix
402 std::vector<float> gram (n * n);
403 {
404 FINTEGER di = d_in, ni = n;
405 float one = 1.0, zero = 0.0;
406 ssyrk_ ("Up", "Transposed",
407 &ni, &di, &one, xc.data(), &di, &zero, gram.data(), &ni);
408 }
409
410 if(verbose && d_in <= 10) {
411 float *ci = gram.data();
412 printf("gram=\n");
413 for(int i = 0; i < n; i++) {
414 for(int j = 0; j < n; j++)
415 printf("%10g ", *ci++);
416 printf("\n");
417 }
418 }
419
420 std::vector<double> gramd (n * n);
421 for (size_t i = 0; i < n * n; i++)
422 gramd [i] = gram [i];
423
424 std::vector<double> eigenvaluesd (n);
425
426 // eig will fill in only the n first eigenvals
427
428 eig (n, gramd.data (), eigenvaluesd.data (), verbose);
429
430 PCAMat.resize(d_in * n);
431
432 for (size_t i = 0; i < n * n; i++)
433 gram [i] = gramd [i];
434
435 eigenvalues.resize (d_in);
436 // fill in only the n first ones
437 for (size_t i = 0; i < n; i++)
438 eigenvalues [i] = eigenvaluesd [i];
439
440 { // compute PCAMat = x' * v
441 FINTEGER di = d_in, ni = n;
442 float one = 1.0;
443
444 sgemm_ ("Non", "Non Trans",
445 &di, &ni, &ni,
446 &one, xc.data(), &di, gram.data(), &ni,
447 &one, PCAMat.data(), &di);
448 }
449
450 if(verbose && d_in <= 10) {
451 float *ci = PCAMat.data();
452 printf("PCAMat=\n");
453 for(int i = 0; i < n; i++) {
454 for(int j = 0; j < d_in; j++)
455 printf("%10g ", *ci++);
456 printf("\n");
457 }
458 }
459 fvec_renorm_L2 (d_in, n, PCAMat.data());
460
461 }
462
463 prepare_Ab();
464 is_trained = true;
465 }
466
467 void PCAMatrix::copy_from (const PCAMatrix & other)
468 {
469 FAISS_THROW_IF_NOT (other.is_trained);
470 mean = other.mean;
471 eigenvalues = other.eigenvalues;
472 PCAMat = other.PCAMat;
473 prepare_Ab ();
474 is_trained = true;
475 }
476
477 void PCAMatrix::prepare_Ab ()
478 {
479 FAISS_THROW_IF_NOT_FMT (
480 d_out * d_in <= PCAMat.size(),
481 "PCA matrix cannot output %d dimensions from %d ",
482 d_out, d_in);
483
484 if (!random_rotation) {
485 A = PCAMat;
486 A.resize(d_out * d_in); // strip off useless dimensions
487
488 // first scale the components
489 if (eigen_power != 0) {
490 float *ai = A.data();
491 for (int i = 0; i < d_out; i++) {
492 float factor = pow(eigenvalues[i], eigen_power);
493 for(int j = 0; j < d_in; j++)
494 *ai++ *= factor;
495 }
496 }
497
498 if (balanced_bins != 0) {
499 FAISS_THROW_IF_NOT (d_out % balanced_bins == 0);
500 int dsub = d_out / balanced_bins;
501 std::vector <float> Ain;
502 std::swap(A, Ain);
503 A.resize(d_out * d_in);
504
505 std::vector <float> accu(balanced_bins);
506 std::vector <int> counter(balanced_bins);
507
508 // greedy assignment
509 for (int i = 0; i < d_out; i++) {
510 // find best bin
511 int best_j = -1;
512 float min_w = 1e30;
513 for (int j = 0; j < balanced_bins; j++) {
514 if (counter[j] < dsub && accu[j] < min_w) {
515 min_w = accu[j];
516 best_j = j;
517 }
518 }
519 int row_dst = best_j * dsub + counter[best_j];
520 accu[best_j] += eigenvalues[i];
521 counter[best_j] ++;
522 memcpy (&A[row_dst * d_in], &Ain[i * d_in],
523 d_in * sizeof (A[0]));
524 }
525
526 if (verbose) {
527 printf(" bin accu=[");
528 for (int i = 0; i < balanced_bins; i++)
529 printf("%g ", accu[i]);
530 printf("]\n");
531 }
532 }
533
534
535 } else {
536 FAISS_THROW_IF_NOT_MSG (balanced_bins == 0,
537 "both balancing bins and applying a random rotation "
538 "does not make sense");
539 RandomRotationMatrix rr(d_out, d_out);
540
541 rr.init(5);
542
543 // apply scaling on the rotation matrix (right multiplication)
544 if (eigen_power != 0) {
545 for (int i = 0; i < d_out; i++) {
546 float factor = pow(eigenvalues[i], eigen_power);
547 for(int j = 0; j < d_out; j++)
548 rr.A[j * d_out + i] *= factor;
549 }
550 }
551
552 A.resize(d_in * d_out);
553 {
554 FINTEGER dii = d_in, doo = d_out;
555 float one = 1.0, zero = 0.0;
556
557 sgemm_ ("Not", "Not", &dii, &doo, &doo,
558 &one, PCAMat.data(), &dii, rr.A.data(), &doo, &zero,
559 A.data(), &dii);
560
561 }
562
563 }
564
565 b.clear(); b.resize(d_out);
566
567 for (int i = 0; i < d_out; i++) {
568 float accu = 0;
569 for (int j = 0; j < d_in; j++)
570 accu -= mean[j] * A[j + i * d_in];
571 b[i] = accu;
572 }
573
574 is_orthonormal = eigen_power == 0;
575
576 }
577
578 /*********************************************
579 * OPQMatrix
580 *********************************************/
581
582
583 OPQMatrix::OPQMatrix (int d, int M, int d2):
584 LinearTransform (d, d2 == -1 ? d : d2, false), M(M),
585 niter (50),
586 niter_pq (4), niter_pq_0 (40),
587 verbose(false),
588 pq(nullptr)
589 {
590 is_trained = false;
591 // OPQ is quite expensive to train, so set this right.
592 max_train_points = 256 * 256;
593 pq = nullptr;
594 }
595
596
597
598 void OPQMatrix::train (Index::idx_t n, const float *x)
599 {
600
601 const float * x_in = x;
602
603 x = fvecs_maybe_subsample (d_in, (size_t*)&n,
604 max_train_points, x, verbose);
605
606 ScopeDeleter<float> del_x (x != x_in ? x : nullptr);
607
608 // To support d_out > d_in, we pad input vectors with 0s to d_out
609 size_t d = d_out <= d_in ? d_in : d_out;
610 size_t d2 = d_out;
611
612 #if 0
613 // what this test shows: the only way of getting bit-exact
614 // reproducible results with sgeqrf and sgesvd seems to be forcing
615 // single-threading.
616 { // test repro
617 std::vector<float> r (d * d);
618 float * rotation = r.data();
619 float_randn (rotation, d * d, 1234);
620 printf("CS0: %016lx\n",
621 ivec_checksum (128*128, (int*)rotation));
622 matrix_qr (d, d, rotation);
623 printf("CS1: %016lx\n",
624 ivec_checksum (128*128, (int*)rotation));
625 return;
626 }
627 #endif
628
629 if (verbose) {
630 printf ("OPQMatrix::train: training an OPQ rotation matrix "
631 "for M=%d from %ld vectors in %dD -> %dD\n",
632 M, n, d_in, d_out);
633 }
634
635 std::vector<float> xtrain (n * d);
636 // center x
637 {
638 std::vector<float> sum (d);
639 const float *xi = x;
640 for (size_t i = 0; i < n; i++) {
641 for (int j = 0; j < d_in; j++)
642 sum [j] += *xi++;
643 }
644 for (int i = 0; i < d; i++) sum[i] /= n;
645 float *yi = xtrain.data();
646 xi = x;
647 for (size_t i = 0; i < n; i++) {
648 for (int j = 0; j < d_in; j++)
649 *yi++ = *xi++ - sum[j];
650 yi += d - d_in;
651 }
652 }
653 float *rotation;
654
655 if (A.size () == 0) {
656 A.resize (d * d);
657 rotation = A.data();
658 if (verbose)
659 printf(" OPQMatrix::train: making random %ld*%ld rotation\n",
660 d, d);
661 float_randn (rotation, d * d, 1234);
662 matrix_qr (d, d, rotation);
663 // we use only the d * d2 upper part of the matrix
664 A.resize (d * d2);
665 } else {
666 FAISS_THROW_IF_NOT (A.size() == d * d2);
667 rotation = A.data();
668 }
669
670 std::vector<float>
671 xproj (d2 * n), pq_recons (d2 * n), xxr (d * n),
672 tmp(d * d * 4);
673
674 std::vector<uint8_t> codes (M * n);
675
676 ProductQuantizer pq_default (d2, M, 8);
677 ProductQuantizer &pq_regular =
678 pq ? *pq : pq_default;
679 double t0 = getmillisecs();
680 for (int iter = 0; iter < niter; iter++) {
681
682 { // torch.mm(xtrain, rotation:t())
683 FINTEGER di = d, d2i = d2, ni = n;
684 float zero = 0, one = 1;
685 sgemm_ ("Transposed", "Not transposed",
686 &d2i, &ni, &di,
687 &one, rotation, &di,
688 xtrain.data(), &di,
689 &zero, xproj.data(), &d2i);
690 }
691
692 pq_regular.cp.max_points_per_centroid = 1000;
693 pq_regular.cp.niter = iter == 0 ? niter_pq_0 : niter_pq;
694 pq_regular.cp.verbose = verbose;
695 pq_regular.train (n, xproj.data());
696
697 pq_regular.compute_codes (xproj.data(), codes.data(), n);
698 pq_regular.decode (codes.data(), pq_recons.data(), n);
699
700 float pq_err = fvec_L2sqr (pq_recons.data(), xproj.data(), n * d2) / n;
701
702 if (verbose)
703 printf (" Iteration %d (%d PQ iterations):"
704 "%.3f s, obj=%g\n", iter, pq_regular.cp.niter,
705 (getmillisecs () - t0) / 1000.0, pq_err);
706
707 {
708 float *u = tmp.data(), *vt = &tmp [d * d];
709 float *sing_val = &tmp [2 * d * d];
710 FINTEGER di = d, d2i = d2, ni = n;
711 float one = 1, zero = 0;
712
713 // torch.mm(xtrain:t(), pq_recons)
714 sgemm_ ("Not", "Transposed",
715 &d2i, &di, &ni,
716 &one, pq_recons.data(), &d2i,
717 xtrain.data(), &di,
718 &zero, xxr.data(), &d2i);
719
720
721 FINTEGER lwork = -1, info = -1;
722 float worksz;
723 // workspace query
724 sgesvd_ ("All", "All",
725 &d2i, &di, xxr.data(), &d2i,
726 sing_val,
727 vt, &d2i, u, &di,
728 &worksz, &lwork, &info);
729
730 lwork = int(worksz);
731 std::vector<float> work (lwork);
732 // u and vt swapped
733 sgesvd_ ("All", "All",
734 &d2i, &di, xxr.data(), &d2i,
735 sing_val,
736 vt, &d2i, u, &di,
737 work.data(), &lwork, &info);
738
739 sgemm_ ("Transposed", "Transposed",
740 &di, &d2i, &d2i,
741 &one, u, &di, vt, &d2i,
742 &zero, rotation, &di);
743
744 }
745 pq_regular.train_type = ProductQuantizer::Train_hot_start;
746 }
747
748 // revert A matrix
749 if (d > d_in) {
750 for (long i = 0; i < d_out; i++)
751 memmove (&A[i * d_in], &A[i * d], sizeof(A[0]) * d_in);
752 A.resize (d_in * d_out);
753 }
754
755 is_trained = true;
756 is_orthonormal = true;
757 }
758
759
760 /*********************************************
761 * NormalizationTransform
762 *********************************************/
763
764 NormalizationTransform::NormalizationTransform (int d, float norm):
765 VectorTransform (d, d), norm (norm)
766 {
767 }
768
769 NormalizationTransform::NormalizationTransform ():
770 VectorTransform (-1, -1), norm (-1)
771 {
772 }
773
774 void NormalizationTransform::apply_noalloc
775 (idx_t n, const float* x, float* xt) const
776 {
777 if (norm == 2.0) {
778 memcpy (xt, x, sizeof (x[0]) * n * d_in);
779 fvec_renorm_L2 (d_in, n, xt);
780 } else {
781 FAISS_THROW_MSG ("not implemented");
782 }
783 }
784
785 void NormalizationTransform::reverse_transform (idx_t n, const float* xt,
786 float* x) const
787 {
788 memcpy (x, xt, sizeof (xt[0]) * n * d_in);
789 }
790
791 /*********************************************
792 * IndexPreTransform
793 *********************************************/
794
795 IndexPreTransform::IndexPreTransform ():
796 index(nullptr), own_fields (false)
797 {
798 }
799
800
801 IndexPreTransform::IndexPreTransform (
802 Index * index):
803 Index (index->d, index->metric_type),
804 index (index), own_fields (false)
805 {
806 is_trained = index->is_trained;
807 }
808
809
810 IndexPreTransform::IndexPreTransform (
811 VectorTransform * ltrans,
812 Index * index):
813 Index (index->d, index->metric_type),
814 index (index), own_fields (false)
815 {
816 is_trained = index->is_trained;
817 prepend_transform (ltrans);
818 }
819
820 void IndexPreTransform::prepend_transform (VectorTransform *ltrans)
821 {
822 FAISS_THROW_IF_NOT (ltrans->d_out == d);
823 is_trained = is_trained && ltrans->is_trained;
824 chain.insert (chain.begin(), ltrans);
825 d = ltrans->d_in;
826 }
827
828
829 IndexPreTransform::~IndexPreTransform ()
830 {
831 if (own_fields) {
832 for (int i = 0; i < chain.size(); i++)
833 delete chain[i];
834 delete index;
835 }
836 }
837
838
839
840
841 void IndexPreTransform::train (idx_t n, const float *x)
842 {
843 int last_untrained = 0;
844 if (!index->is_trained) {
845 last_untrained = chain.size();
846 } else {
847 for (int i = chain.size() - 1; i >= 0; i--) {
848 if (!chain[i]->is_trained) {
849 last_untrained = i;
850 break;
851 }
852 }
853 }
854 const float *prev_x = x;
855 ScopeDeleter<float> del;
856
857 if (verbose) {
858 printf("IndexPreTransform::train: training chain 0 to %d\n",
859 last_untrained);
860 }
861
862 for (int i = 0; i <= last_untrained; i++) {
863
864 if (i < chain.size()) {
865 VectorTransform *ltrans = chain [i];
866 if (!ltrans->is_trained) {
867 if (verbose) {
868 printf(" Training chain component %d/%zd\n",
869 i, chain.size());
870 if (OPQMatrix *opqm = dynamic_cast<OPQMatrix*>(ltrans)) {
871 opqm->verbose = true;
872 }
873 }
874 ltrans->train (n, prev_x);
875 }
876 } else {
877 if (verbose) {
878 printf(" Training sub-index\n");
879 }
880 index->train (n, prev_x);
881 }
882 if (i == last_untrained) break;
883 if (verbose) {
884 printf(" Applying transform %d/%zd\n",
885 i, chain.size());
886 }
887
888 float * xt = chain[i]->apply (n, prev_x);
889
890 if (prev_x != x) delete [] prev_x;
891 prev_x = xt;
892 del.set(xt);
893 }
894
895 is_trained = true;
896 }
897
898
899 const float *IndexPreTransform::apply_chain (idx_t n, const float *x) const
900 {
901 const float *prev_x = x;
902 ScopeDeleter<float> del;
903
904 for (int i = 0; i < chain.size(); i++) {
905 float * xt = chain[i]->apply (n, prev_x);
906 ScopeDeleter<float> del2 (xt);
907 del2.swap (del);
908 prev_x = xt;
909 }
910 del.release ();
911 return prev_x;
912 }
913
914 void IndexPreTransform::reverse_chain (idx_t n, const float* xt, float* x) const
915 {
916 const float* next_x = xt;
917 ScopeDeleter<float> del;
918
919 for (int i = chain.size() - 1; i >= 0; i--) {
920 float* prev_x = (i == 0) ? x : new float [n * chain[i]->d_in];
921 ScopeDeleter<float> del2 ((prev_x == x) ? nullptr : prev_x);
922 chain [i]->reverse_transform (n, next_x, prev_x);
923 del2.swap (del);
924 next_x = prev_x;
925 }
926 }
927
928 void IndexPreTransform::add (idx_t n, const float *x)
929 {
930 FAISS_THROW_IF_NOT (is_trained);
931 const float *xt = apply_chain (n, x);
932 ScopeDeleter<float> del(xt == x ? nullptr : xt);
933 index->add (n, xt);
934 ntotal = index->ntotal;
935 }
936
937 void IndexPreTransform::add_with_ids (idx_t n, const float * x,
938 const long *xids)
939 {
940 FAISS_THROW_IF_NOT (is_trained);
941 const float *xt = apply_chain (n, x);
942 ScopeDeleter<float> del(xt == x ? nullptr : xt);
943 index->add_with_ids (n, xt, xids);
944 ntotal = index->ntotal;
945 }
946
947
948
949
950 void IndexPreTransform::search (idx_t n, const float *x, idx_t k,
951 float *distances, idx_t *labels) const
952 {
953 FAISS_THROW_IF_NOT (is_trained);
954 const float *xt = apply_chain (n, x);
955 ScopeDeleter<float> del(xt == x ? nullptr : xt);
956 index->search (n, xt, k, distances, labels);
957 }
958
959
960 void IndexPreTransform::reset () {
961 index->reset();
962 ntotal = 0;
963 }
964
965 long IndexPreTransform::remove_ids (const IDSelector & sel) {
966 long nremove = index->remove_ids (sel);
967 ntotal = index->ntotal;
968 return nremove;
969 }
970
971
972 void IndexPreTransform::reconstruct (idx_t key, float * recons) const
973 {
974 float *x = chain.empty() ? recons : new float [index->d];
975 ScopeDeleter<float> del (recons == x ? nullptr : x);
976 // Initial reconstruction
977 index->reconstruct (key, x);
978
979 // Revert transformations from last to first
980 reverse_chain (1, x, recons);
981 }
982
983
984 void IndexPreTransform::reconstruct_n (idx_t i0, idx_t ni, float *recons) const
985 {
986 float *x = chain.empty() ? recons : new float [ni * index->d];
987 ScopeDeleter<float> del (recons == x ? nullptr : x);
988 // Initial reconstruction
989 index->reconstruct_n (i0, ni, x);
990
991 // Revert transformations from last to first
992 reverse_chain (ni, x, recons);
993 }
994
995
996 void IndexPreTransform::search_and_reconstruct (
997 idx_t n, const float *x, idx_t k,
998 float *distances, idx_t *labels, float* recons) const
999 {
1000 FAISS_THROW_IF_NOT (is_trained);
1001
1002 const float* xt = apply_chain (n, x);
1003 ScopeDeleter<float> del ((xt == x) ? nullptr : xt);
1004
1005 float* recons_temp = chain.empty() ? recons : new float [n * k * index->d];
1006 ScopeDeleter<float> del2 ((recons_temp == recons) ? nullptr : recons_temp);
1007 index->search_and_reconstruct (n, xt, k, distances, labels, recons_temp);
1008
1009 // Revert transformations from last to first
1010 reverse_chain (n * k, recons_temp, recons);
1011 }
1012
1013
1014 /*********************************************
1015 * RemapDimensionsTransform
1016 *********************************************/
1017
1018
1019 RemapDimensionsTransform::RemapDimensionsTransform (
1020 int d_in, int d_out, const int *map_in):
1021 VectorTransform (d_in, d_out)
1022 {
1023 map.resize (d_out);
1024 for (int i = 0; i < d_out; i++) {
1025 map[i] = map_in[i];
1026 FAISS_THROW_IF_NOT (map[i] == -1 || (map[i] >= 0 && map[i] < d_in));
1027 }
1028 }
1029
1030 RemapDimensionsTransform::RemapDimensionsTransform (
1031 int d_in, int d_out, bool uniform): VectorTransform (d_in, d_out)
1032 {
1033 map.resize (d_out, -1);
1034
1035 if (uniform) {
1036 if (d_in < d_out) {
1037 for (int i = 0; i < d_in; i++) {
1038 map [i * d_out / d_in] = i;
1039 }
1040 } else {
1041 for (int i = 0; i < d_out; i++) {
1042 map [i] = i * d_in / d_out;
1043 }
1044 }
1045 } else {
1046 for (int i = 0; i < d_in && i < d_out; i++)
1047 map [i] = i;
1048 }
1049 }
1050
1051
1052 void RemapDimensionsTransform::apply_noalloc (idx_t n, const float * x,
1053 float *xt) const
1054 {
1055 for (idx_t i = 0; i < n; i++) {
1056 for (int j = 0; j < d_out; j++) {
1057 xt[j] = map[j] < 0 ? 0 : x[map[j]];
1058 }
1059 x += d_in;
1060 xt += d_out;
1061 }
1062 }
1063
1064 void RemapDimensionsTransform::reverse_transform (idx_t n, const float * xt,
1065 float *x) const
1066 {
1067 memset (x, 0, sizeof (*x) * n * d_in);
1068 for (idx_t i = 0; i < n; i++) {
1069 for (int j = 0; j < d_out; j++) {
1070 if (map[j] >= 0) x[map[j]] = xt[j];
1071 }
1072 x += d_in;
1073 xt += d_out;
1074 }
1075 }
faiss::LinearTransform::transform_transpose
void transform_transpose(idx_t n, const float *y, float *x) const
Definition: VectorTransform.cpp:136
faiss::IndexPreTransform::index
Index * index
! chain of tranforms
Definition: VectorTransform.h:256
faiss::RandomRotationMatrix
Randomly rotate a set of vectors.
Definition: VectorTransform.h:116
faiss::ClusteringParameters::niter
int niter
clustering iterations
Definition: Clustering.h:24
faiss::OPQMatrix::niter
int niter
Number of outer training iterations.
Definition: VectorTransform.h:192
faiss::ProductQuantizer::decode
void decode(const uint8_t *code, float *x) const
decode a vector from a given code (or n vectors if third argument)
Definition: ProductQuantizer.cpp:335
faiss::fvec_L2sqr
float fvec_L2sqr(const float *x, const float *y, size_t d)
Squared L2 distance between two vectors.
Definition: utils_simd.cpp:502
faiss::RandomRotationMatrix::init
void init(int seed)
must be called before the transform is used
Definition: VectorTransform.cpp:216
faiss::IndexPreTransform::reset
void reset() override
removes all elements from the database.
Definition: VectorTransform.cpp:960
faiss::Index::reset
virtual void reset()=0
removes all elements from the database.
faiss::OPQMatrix::niter_pq
int niter_pq
Number of training iterations for the PQ.
Definition: VectorTransform.h:193
faiss::LinearTransform::A
std::vector< float > A
Transformation matrix, size d_out * d_in.
Definition: VectorTransform.h:84
faiss::Index::search_and_reconstruct
virtual void search_and_reconstruct(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels, float *recons) const
Definition: Index.cpp:67
faiss::fvecs_maybe_subsample
const float * fvecs_maybe_subsample(size_t d, size_t *n, size_t nmax, const float *x, bool verbose, long seed)
Definition: utils.cpp:1528
faiss::PCAMatrix::eigen_power
float eigen_power
Definition: VectorTransform.h:142
faiss::IDSelector
Definition: AuxIndexStructures.h:54
faiss::LinearTransform::LinearTransform
LinearTransform(int d_in=0, int d_out=0, bool have_bias=false)
both d_in &gt; d_out and d_out &lt; d_in are supported
Definition: VectorTransform.cpp:99
faiss::PCAMatrix
Definition: VectorTransform.h:134
faiss::LinearTransform::set_is_orthonormal
void set_is_orthonormal()
compute A^T * A to set the is_orthonormal flag
Definition: VectorTransform.cpp:161
faiss::OPQMatrix::pq
ProductQuantizer * pq
Definition: VectorTransform.h:202
faiss::Index::train
virtual void train(idx_t n, const float *x)
Definition: Index.cpp:24
faiss::Index::add_with_ids
virtual void add_with_ids(idx_t n, const float *x, const long *xids)
Definition: Index.cpp:42
faiss::OPQMatrix
Definition: VectorTransform.h:189
faiss::ScopeDeleter
Definition: FaissException.h:39
faiss::PCAMatrix::train
void train(Index::idx_t n, const float *x) override
Definition: VectorTransform.cpp:325
faiss::PCAMatrix::mean
std::vector< float > mean
Mean, size d_in.
Definition: VectorTransform.h:154
faiss::RemapDimensionsTransform::map
std::vector< int > map
Definition: VectorTransform.h:218
faiss::IndexPreTransform::apply_chain
const float * apply_chain(idx_t n, const float *x) const
Definition: VectorTransform.cpp:899
faiss::PCAMatrix::PCAMat
std::vector< float > PCAMat
PCA matrix, size d_in * d_in.
Definition: VectorTransform.h:160
faiss::IndexPreTransform::train
void train(idx_t n, const float *x) override
Definition: VectorTransform.cpp:841
faiss::ProductQuantizer::compute_codes
void compute_codes(const float *x, uint8_t *codes, size_t n) const
same as compute_code for several vectors
Definition: ProductQuantizer.cpp:382
faiss::Index::d
int d
vector dimension
Definition: Index.h:66
faiss::LinearTransform::b
std::vector< float > b
bias vector, size d_out
Definition: VectorTransform.h:87
faiss::LinearTransform::reverse_transform
void reverse_transform(idx_t n, const float *xt, float *x) const override
works only if is_orthonormal
Definition: VectorTransform.cpp:200
faiss::Index::reconstruct_n
virtual void reconstruct_n(idx_t i0, idx_t ni, float *recons) const
Definition: Index.cpp:60
faiss::Index::add
virtual void add(idx_t n, const float *x)=0
faiss::NormalizationTransform::reverse_transform
void reverse_transform(idx_t n, const float *xt, float *x) const override
Identity transform since norm is not revertible.
Definition: VectorTransform.cpp:785
faiss::LinearTransform
Definition: VectorTransform.h:76
faiss::OPQMatrix::train
void train(Index::idx_t n, const float *x) override
Definition: VectorTransform.cpp:598
faiss::PCAMatrix::balanced_bins
int balanced_bins
try to distribute output eigenvectors in this many bins
Definition: VectorTransform.h:151
faiss::VectorTransform::is_trained
bool is_trained
Definition: VectorTransform.h:41
faiss::Index::idx_t
long idx_t
all indices are this type
Definition: Index.h:64
faiss::RandomRotationMatrix::train
void train(Index::idx_t n, const float *x) override
Definition: VectorTransform.cpp:242
faiss::IndexPreTransform::reconstruct_n
void reconstruct_n(idx_t i0, idx_t ni, float *recons) const override
Definition: VectorTransform.cpp:984
faiss::Index::ntotal
idx_t ntotal
total nb of indexed vectors
Definition: Index.h:67
faiss::NormalizationTransform::apply_noalloc
void apply_noalloc(idx_t n, const float *x, float *xt) const override
same as apply, but result is pre-allocated
Definition: VectorTransform.cpp:775
faiss::ProductQuantizer::Train_hot_start
the centroids are already initialized
Definition: ProductQuantizer.h:41
faiss::getmillisecs
double getmillisecs()
ms elapsed since some arbitrary epoch
Definition: utils.cpp:70
faiss::Index::remove_ids
virtual long remove_ids(const IDSelector &sel)
Definition: Index.cpp:49
faiss::Index::search
virtual void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const =0
faiss::VectorTransform
Definition: VectorTransform.h:27
faiss::matrix_qr
void matrix_qr(int m, int n, float *a)
Definition: utils.cpp:987
faiss::IndexPreTransform::own_fields
bool own_fields
! the sub-index
Definition: VectorTransform.h:258
faiss::OPQMatrix::niter_pq_0
int niter_pq_0
same, for the first outer iteration
Definition: VectorTransform.h:194
faiss::ProductQuantizer::cp
ClusteringParameters cp
parameters used during clustering
Definition: ProductQuantizer.h:48
faiss::ivec_checksum
size_t ivec_checksum(size_t n, const int *a)
compute a checksum on a table.
Definition: utils.cpp:1337
faiss::VectorTransform::train
virtual void train(idx_t n, const float *x)
Definition: VectorTransform.cpp:79
faiss::VectorTransform::reverse_transform
virtual void reverse_transform(idx_t n, const float *xt, float *x) const
Definition: VectorTransform.cpp:84
faiss::IndexPreTransform::search_and_reconstruct
void search_and_reconstruct(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels, float *recons) const override
Definition: VectorTransform.cpp:996
faiss::RemapDimensionsTransform::reverse_transform
void reverse_transform(idx_t n, const float *xt, float *x) const override
reverse transform correct only when the mapping is a permuation
Definition: VectorTransform.cpp:1064
faiss::OPQMatrix::max_train_points
size_t max_train_points
if there are too many training points, resample
Definition: VectorTransform.h:197
faiss::PCAMatrix::copy_from
void copy_from(const PCAMatrix &other)
copy pre-trained PCA matrix
Definition: VectorTransform.cpp:467
faiss::VectorTransform::d_out
int d_out
! input dimension
Definition: VectorTransform.h:32
faiss::OPQMatrix::OPQMatrix
OPQMatrix(int d=0, int M=1, int d2=-1)
if d2 != -1, output vectors of this dimension
Definition: VectorTransform.cpp:583
faiss::Index
Definition: Index.h:62
faiss::PCAMatrix::prepare_Ab
void prepare_Ab()
called after mean, PCAMat and eigenvalues are computed
Definition: VectorTransform.cpp:477
faiss::IndexPreTransform::add
void add(idx_t n, const float *x) override
Definition: VectorTransform.cpp:928
faiss::RemapDimensionsTransform::apply_noalloc
void apply_noalloc(idx_t n, const float *x, float *xt) const override
same as apply, but result is pre-allocated
Definition: VectorTransform.cpp:1052
faiss::ProductQuantizer
Definition: ProductQuantizer.h:24
faiss::Index::is_trained
bool is_trained
set if the Index does not require training, or if training is done already
Definition: Index.h:71
faiss::IndexPreTransform::reverse_chain
void reverse_chain(idx_t n, const float *xt, float *x) const
Definition: VectorTransform.cpp:914
faiss::LinearTransform::is_orthonormal
bool is_orthonormal
! whether to use the bias term
Definition: VectorTransform.h:81
faiss::PCAMatrix::eigenvalues
std::vector< float > eigenvalues
eigenvalues of covariance matrix (= squared singular values)
Definition: VectorTransform.h:157
faiss::IndexPreTransform::search
void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const override
Definition: VectorTransform.cpp:950
faiss::Index::reconstruct
virtual void reconstruct(idx_t key, float *recons) const
Definition: Index.cpp:55
faiss::IndexPreTransform::add_with_ids
void add_with_ids(idx_t n, const float *x, const long *xids) override
Definition: VectorTransform.cpp:937
faiss::PCAMatrix::random_rotation
bool random_rotation
random rotation after PCA
Definition: VectorTransform.h:145
faiss::PCAMatrix::max_points_per_d
size_t max_points_per_d
ratio between # training vectors and dimension
Definition: VectorTransform.h:148
faiss::ClusteringParameters::max_points_per_centroid
int max_points_per_centroid
to limit size of dataset
Definition: Clustering.h:33
faiss::VectorTransform::apply
float * apply(idx_t n, const float *x) const
Definition: VectorTransform.cpp:71
faiss::IndexPreTransform::remove_ids
long remove_ids(const IDSelector &sel) override
Definition: VectorTransform.cpp:965
faiss::VectorTransform::apply_noalloc
virtual void apply_noalloc(idx_t n, const float *x, float *xt) const =0
same as apply, but result is pre-allocated
faiss::IndexPreTransform::reconstruct
void reconstruct(idx_t key, float *recons) const override
Definition: VectorTransform.cpp:972
faiss::OPQMatrix::M
int M
nb of subquantizers
Definition: VectorTransform.h:191
faiss::LinearTransform::apply_noalloc
void apply_noalloc(idx_t n, const float *x, float *xt) const override
same as apply, but result is pre-allocated
Definition: VectorTransform.cpp:107