11 #include "VectorTransform.h"
18 #include "FaissAssert.h"
21 using namespace faiss;
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);
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);
49 const char *jobz,
const char *uplo, FINTEGER *n,
float *a,
50 FINTEGER *lda,
float *w,
float *work, FINTEGER *lwork,
54 const char *jobz,
const char *uplo, FINTEGER *n,
double *a,
55 FINTEGER *lda,
double *w,
double *work, FINTEGER *lwork,
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);
73 float * xt =
new float[n *
d_out];
85 idx_t ,
const float *,
88 FAISS_THROW_MSG (
"reverse transform not implemented");
102 is_orthonormal (false), verbose (false)
110 FAISS_THROW_IF_NOT_MSG(
is_trained,
"Transformation not trained yet");
114 FAISS_THROW_IF_NOT_MSG (
b.size() ==
d_out,
"Bias not initialized");
116 for (
int i = 0; i < n; i++)
117 for(
int j = 0; j <
d_out; j++)
124 FAISS_THROW_IF_NOT_MSG (
A.size() ==
d_out * d_in,
125 "Transformation matrix not initialized");
128 FINTEGER nbiti =
d_out, ni = n, di = d_in;
129 sgemm_ (
"Transposed",
"Not transposed",
131 &one,
A.data(), &di, x, &di, &c_factor, xt, &nbiti);
140 float *y_new =
new float [n *
d_out];
143 for (idx_t i = 0; i < n; i++) {
144 for (
int j = 0; j <
d_out; j++) {
145 *yw++ = *yr++ -
b [j];
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);
158 if (have_bias)
delete [] y;
174 FAISS_ASSERT(
A.size() >=
d_out * d_in);
177 FINTEGER dii = d_in, doi =
d_out;
178 float one = 1.0, zero = 0.0;
180 sgemm_ (
"Transposed",
"Not", &doi, &doi, &dii,
181 &one,
A.data (), &dii,
183 &zero, ATA.data(), &doi);
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];
191 is_orthonormal =
false;
206 FAISS_THROW_MSG (
"reverse transform not implemented for non-orthonormal matrices");
222 float_randn(q,
d_out * d_in, seed);
227 float_randn(q, d_out * d_out, seed);
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];
236 A.resize(d_in * d_out);
253 PCAMatrix::PCAMatrix (
int d_in,
int d_out,
254 float eigen_power,
bool random_rotation):
256 eigen_power(eigen_power), random_rotation(random_rotation)
259 max_points_per_d = 1000;
269 void eig(
size_t d_in,
double *cov,
double *eigenvalues,
int verbose)
272 FINTEGER info = 0, lwork = -1, di = d_in;
275 dsyev_ (
"Vectors as well",
"Upper",
276 &di, cov, &di, eigenvalues, &workq, &lwork, &info);
277 lwork = FINTEGER(workq);
278 double *work =
new double[lwork];
280 dsyev_ (
"Vectors as well",
"Upper",
281 &di, cov, &di, eigenvalues, work, &lwork, &info);
286 fprintf (stderr,
"WARN ssyev info returns %d, "
287 "a very bad PCA matrix is learnt\n",
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]);
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++);
311 for(
int i = 0; i < d_in / 2; i++) {
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]);
327 const float * x_in = x;
335 mean.clear();
mean.resize(d_in, 0.0);
338 for (
int i = 0; i < n; i++) {
339 for(
int j = 0; j < d_in; j++)
342 for(
int j = 0; j < d_in; j++)
347 for(
int j = 0; j < d_in; j++) printf(
"%g ",
mean[j]);
353 PCAMat.resize(d_in * d_in);
354 float * cov =
PCAMat.data();
357 for(
int i = 0; i < d_in; i++) {
358 for(
int j = 0; j < d_in; j++)
363 FINTEGER di = d_in, ni = n;
365 ssyrk_ (
"Up",
"Non transposed",
366 &di, &ni, &one, (
float*)x, &di, &one, cov, &di);
369 if(verbose && d_in <= 10) {
372 for(
int i = 0; i < d_in; i++) {
373 for(
int j = 0; j < d_in; j++)
374 printf(
"%10g ", *ci++);
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];
382 std::vector<double> eigenvaluesd (d_in);
384 eig (d_in, covd.data (), eigenvaluesd.data (), verbose);
386 for (
size_t i = 0; i < d_in * d_in; i++)
PCAMat [i] = covd [i];
387 eigenvalues.resize (d_in);
389 for (
size_t i = 0; i < d_in; i++)
390 eigenvalues [i] = eigenvaluesd [i];
395 std::vector<float> xc (n * d_in);
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];
402 std::vector<float> gram (n * n);
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);
410 if(verbose && d_in <= 10) {
411 float *ci = gram.data();
413 for(
int i = 0; i < n; i++) {
414 for(
int j = 0; j < n; j++)
415 printf(
"%10g ", *ci++);
420 std::vector<double> gramd (n * n);
421 for (
size_t i = 0; i < n * n; i++)
422 gramd [i] = gram [i];
424 std::vector<double> eigenvaluesd (n);
428 eig (n, gramd.data (), eigenvaluesd.data (), verbose);
432 for (
size_t i = 0; i < n * n; i++)
433 gram [i] = gramd [i];
435 eigenvalues.resize (d_in);
437 for (
size_t i = 0; i < n; i++)
438 eigenvalues [i] = eigenvaluesd [i];
441 FINTEGER di = d_in, ni = n;
444 sgemm_ (
"Non",
"Non Trans",
446 &one, xc.data(), &di, gram.data(), &ni,
447 &one,
PCAMat.data(), &di);
450 if(verbose && d_in <= 10) {
451 float *ci =
PCAMat.data();
453 for(
int i = 0; i < n; i++) {
454 for(
int j = 0; j < d_in; j++)
455 printf(
"%10g ", *ci++);
459 fvec_renorm_L2 (d_in, n,
PCAMat.data());
479 FAISS_THROW_IF_NOT_FMT (
481 "PCA matrix cannot output %d dimensions from %d ",
490 float *ai =
A.data();
491 for (
int i = 0; i <
d_out; i++) {
493 for(
int j = 0; j < d_in; j++)
501 std::vector <float> Ain;
509 for (
int i = 0; i <
d_out; i++) {
514 if (counter[j] < dsub && accu[j] < min_w) {
519 int row_dst = best_j * dsub + counter[best_j];
520 accu[best_j] += eigenvalues[i];
522 memcpy (&
A[row_dst * d_in], &Ain[i * d_in],
523 d_in *
sizeof (
A[0]));
527 printf(
" bin accu=[");
529 printf(
"%g ", accu[i]);
537 "both balancing bins and applying a random rotation "
538 "does not make sense");
545 for (
int i = 0; i <
d_out; i++) {
547 for(
int j = 0; j <
d_out; j++)
548 rr.
A[j * d_out + i] *= factor;
554 FINTEGER dii = d_in, doo =
d_out;
555 float one = 1.0, zero = 0.0;
557 sgemm_ (
"Not",
"Not", &dii, &doo, &doo,
558 &one,
PCAMat.data(), &dii, rr.
A.data(), &doo, &zero,
567 for (
int i = 0; i <
d_out; i++) {
569 for (
int j = 0; j < d_in; j++)
570 accu -=
mean[j] *
A[j + i * d_in];
586 niter_pq (4), niter_pq_0 (40),
601 const float * x_in = x;
617 std::vector<float> r (d * d);
618 float * rotation = r.data();
619 float_randn (rotation, d * d, 1234);
620 printf(
"CS0: %016lx\n",
623 printf(
"CS1: %016lx\n",
630 printf (
"OPQMatrix::train: training an OPQ rotation matrix "
631 "for M=%d from %ld vectors in %dD -> %dD\n",
635 std::vector<float> xtrain (n * d);
638 std::vector<float> sum (d);
640 for (
size_t i = 0; i < n; i++) {
641 for (
int j = 0; j < d_in; j++)
644 for (
int i = 0; i < d; i++) sum[i] /= n;
645 float *yi = xtrain.data();
647 for (
size_t i = 0; i < n; i++) {
648 for (
int j = 0; j < d_in; j++)
649 *yi++ = *xi++ - sum[j];
655 if (
A.size () == 0) {
659 printf(
" OPQMatrix::train: making random %ld*%ld rotation\n",
661 float_randn (rotation, d * d, 1234);
666 FAISS_THROW_IF_NOT (
A.size() == d * d2);
671 xproj (d2 * n), pq_recons (d2 * n), xxr (d * n),
674 std::vector<uint8_t> codes (
M * n);
678 pq ? *
pq : pq_default;
680 for (
int iter = 0; iter <
niter; iter++) {
683 FINTEGER di = d, d2i = d2, ni = n;
684 float zero = 0, one = 1;
685 sgemm_ (
"Transposed",
"Not transposed",
689 &zero, xproj.data(), &d2i);
694 pq_regular.
cp.verbose = verbose;
695 pq_regular.train (n, xproj.data());
698 pq_regular.
decode (codes.data(), pq_recons.data(), n);
700 float pq_err =
fvec_L2sqr (pq_recons.data(), xproj.data(), n * d2) / n;
703 printf (
" Iteration %d (%d PQ iterations):"
704 "%.3f s, obj=%g\n", iter, pq_regular.
cp.
niter,
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;
714 sgemm_ (
"Not",
"Transposed",
716 &one, pq_recons.data(), &d2i,
718 &zero, xxr.data(), &d2i);
721 FINTEGER lwork = -1, info = -1;
724 sgesvd_ (
"All",
"All",
725 &d2i, &di, xxr.data(), &d2i,
728 &worksz, &lwork, &info);
731 std::vector<float> work (lwork);
733 sgesvd_ (
"All",
"All",
734 &d2i, &di, xxr.data(), &d2i,
737 work.data(), &lwork, &info);
739 sgemm_ (
"Transposed",
"Transposed",
741 &one, u, &di, vt, &d2i,
742 &zero, rotation, &di);
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);
764 NormalizationTransform::NormalizationTransform (
int d,
float norm):
769 NormalizationTransform::NormalizationTransform ():
775 (idx_t n,
const float* x,
float* xt)
const
778 memcpy (xt, x,
sizeof (x[0]) * n * d_in);
779 fvec_renorm_L2 (d_in, n, xt);
781 FAISS_THROW_MSG (
"not implemented");
788 memcpy (x, xt,
sizeof (xt[0]) * n * d_in);
795 IndexPreTransform::IndexPreTransform ():
796 index(nullptr), own_fields (false)
801 IndexPreTransform::IndexPreTransform (
803 Index (index->d, index->metric_type),
804 index (index), own_fields (false)
810 IndexPreTransform::IndexPreTransform (
813 Index (index->d, index->metric_type),
814 index (index), own_fields (false)
817 prepend_transform (ltrans);
822 FAISS_THROW_IF_NOT (ltrans->
d_out ==
d);
824 chain.insert (chain.begin(), ltrans);
829 IndexPreTransform::~IndexPreTransform ()
832 for (
int i = 0; i < chain.size(); i++)
843 int last_untrained = 0;
845 last_untrained = chain.size();
847 for (
int i = chain.size() - 1; i >= 0; i--) {
854 const float *prev_x = x;
858 printf(
"IndexPreTransform::train: training chain 0 to %d\n",
862 for (
int i = 0; i <= last_untrained; i++) {
864 if (i < chain.size()) {
868 printf(
" Training chain component %d/%zd\n",
870 if (
OPQMatrix *opqm = dynamic_cast<OPQMatrix*>(ltrans)) {
871 opqm->verbose =
true;
874 ltrans->
train (n, prev_x);
878 printf(
" Training sub-index\n");
882 if (i == last_untrained)
break;
884 printf(
" Applying transform %d/%zd\n",
888 float * xt = chain[i]->apply (n, prev_x);
890 if (prev_x != x)
delete [] prev_x;
901 const float *prev_x = x;
904 for (
int i = 0; i < chain.size(); i++) {
905 float * xt = chain[i]->apply (n, prev_x);
916 const float* next_x = xt;
919 for (
int i = chain.size() - 1; i >= 0; i--) {
920 float* prev_x = (i == 0) ? x :
new float [n * chain[i]->d_in];
922 chain [i]->reverse_transform (n, next_x, prev_x);
951 float *distances,
idx_t *labels)
const
974 float *x = chain.empty() ? recons :
new float [
index->
d];
986 float *x = chain.empty() ? recons :
new float [ni *
index->
d];
998 float *distances,
idx_t *labels,
float* recons)
const
1005 float* recons_temp = chain.empty() ? recons :
new float [n * k *
index->
d];
1019 RemapDimensionsTransform::RemapDimensionsTransform (
1020 int d_in,
int d_out,
const int *map_in):
1024 for (
int i = 0; i < d_out; i++) {
1026 FAISS_THROW_IF_NOT (map[i] == -1 || (map[i] >= 0 && map[i] < d_in));
1030 RemapDimensionsTransform::RemapDimensionsTransform (
1033 map.resize (d_out, -1);
1037 for (
int i = 0; i < d_in; i++) {
1038 map [i * d_out / d_in] = i;
1041 for (
int i = 0; i <
d_out; i++) {
1046 for (
int i = 0; i < d_in && i <
d_out; i++)
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]];
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];
Randomly rotate a set of vectors.
int niter
clustering iterations
int niter
Number of outer training iterations.
void decode(const uint8_t *code, float *x) const
decode a vector from a given code (or n vectors if third argument)
float fvec_L2sqr(const float *x, const float *y, size_t d)
Squared L2 distance between two vectors.
void init(int seed)
must be called before the transform is used
virtual void reset()=0
removes all elements from the database.
int niter_pq
Number of training iterations for the PQ.
virtual void search_and_reconstruct(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels, float *recons) const
const float * fvecs_maybe_subsample(size_t d, size_t *n, size_t nmax, const float *x, bool verbose, long seed)
virtual void train(idx_t n, const float *x)
virtual void add_with_ids(idx_t n, const float *x, const long *xids)
void train(Index::idx_t n, const float *x) override
std::vector< float > mean
Mean, size d_in.
std::vector< float > PCAMat
PCA matrix, size d_in * d_in.
void compute_codes(const float *x, uint8_t *codes, size_t n) const
same as compute_code for several vectors
virtual void reconstruct_n(idx_t i0, idx_t ni, float *recons) const
virtual void add(idx_t n, const float *x)=0
void train(Index::idx_t n, const float *x) override
int balanced_bins
try to distribute output eigenvectors in this many bins
long idx_t
all indices are this type
void train(Index::idx_t n, const float *x) override
idx_t ntotal
total nb of indexed vectors
the centroids are already initialized
double getmillisecs()
ms elapsed since some arbitrary epoch
virtual long remove_ids(const IDSelector &sel)
virtual void search(idx_t n, const float *x, idx_t k, float *distances, idx_t *labels) const =0
void matrix_qr(int m, int n, float *a)
int niter_pq_0
same, for the first outer iteration
ClusteringParameters cp
parameters used during clustering
size_t ivec_checksum(size_t n, const int *a)
compute a checksum on a table.
size_t max_train_points
if there are too many training points, resample
void copy_from(const PCAMatrix &other)
copy pre-trained PCA matrix
OPQMatrix(int d=0, int M=1, int d2=-1)
if d2 != -1, output vectors of this dimension
void prepare_Ab()
called after mean, PCAMat and eigenvalues are computed
bool is_trained
set if the Index does not require training, or if training is done already
std::vector< float > eigenvalues
eigenvalues of covariance matrix (= squared singular values)
virtual void reconstruct(idx_t key, float *recons) const
bool random_rotation
random rotation after PCA
size_t max_points_per_d
ratio between # training vectors and dimension
int max_points_per_centroid
to limit size of dataset