13 #include "VectorTransform.h"
20 #include "FaissAssert.h"
23 using namespace faiss;
37 const char *transa,
const char *transb, FINTEGER *m, FINTEGER *
38 n, FINTEGER *k,
const float *alpha,
const float *a,
39 FINTEGER *lda,
const float *b,
40 FINTEGER *ldb,
float *beta,
41 float *c, FINTEGER *ldc);
44 const char *uplo,
const char *trans, FINTEGER *n, FINTEGER *k,
45 float *alpha,
float *a, FINTEGER *lda,
46 float *beta,
float *c, FINTEGER *ldc);
51 const char *jobz,
const char *uplo, FINTEGER *n,
float *a,
52 FINTEGER *lda,
float *w,
float *work, FINTEGER *lwork,
56 const char *jobu,
const char *jobvt, FINTEGER *m, FINTEGER *n,
57 float *a, FINTEGER *lda,
float *s,
float *u, FINTEGER *ldu,
float *vt,
58 FINTEGER *ldvt,
float *work, FINTEGER *lwork, FINTEGER *info);
70 float * xt =
new float[n *
d_out];
82 idx_t ,
const float *,
85 FAISS_ASSERT (!
"reverse transform not implemented");
98 max_points_per_d (1 << 20), verbose (false)
104 FAISS_ASSERT(
is_trained || !
"Transformation not trained yet");
108 FAISS_ASSERT (
b.size() ==
d_out || !
"Bias not initialized");
110 for (
int i = 0; i < n; i++)
111 for(
int j = 0; j <
d_out; j++)
118 FAISS_ASSERT (
A.size() ==
d_out * d_in ||
119 !
"Transformation matrix not initialized");
122 FINTEGER nbiti =
d_out, ni = n, di = d_in;
123 sgemm_ (
"Transposed",
"Not transposed",
125 &one,
A.data(), &di, x, &di, &c_factor, xt, &nbiti);
134 float *y_new =
new float [n *
d_out];
137 for (idx_t i = 0; i < n; i++) {
138 for (
int j = 0; j <
d_out; j++) {
139 *yw++ = *yr++ -
b [j];
146 FINTEGER dii = d_in, doi =
d_out, ni = n;
147 float one = 1.0, zero = 0.0;
148 sgemm_ (
"Not",
"Not", &dii, &ni, &doi,
149 &one,
A.data (), &dii, y, &doi, &zero, x, &dii);
152 if (have_bias)
delete [] y;
155 const float * LinearTransform::maybe_subsample_train_set (
158 if (*n <= max_points_per_d * d_in)
return x;
160 size_t n2 = max_points_per_d * d_in;
162 printf (
" Input training set too big, sampling "
163 "%ld / %ld vectors\n", n2, *n);
165 std::vector<int> subset (*n);
166 rand_perm (subset.data (), *n, 1234);
167 float *x_subset =
new float[n2 * d_in];
168 for (
long i = 0; i < n2; i++)
169 memcpy (&x_subset[i * d_in],
170 &x[subset[i] *
size_t(d_in)],
171 sizeof (x[0]) * d_in);
187 float_randn(q,
d_out * d_in, seed);
192 float_randn(q, d_out * d_out, seed);
196 for (i = 0; i <
d_out; i++) {
197 for(j = 0; j < d_in; j++) {
198 q[i * d_in + j] = q[i * d_out + j];
201 A.resize(d_in * d_out);
216 PCAMatrix::PCAMatrix (
int d_in,
int d_out,
217 float eigen_power,
bool random_rotation):
219 eigen_power(eigen_power), random_rotation(random_rotation)
222 max_points_per_d = 1000;
229 const float * x_in = x;
231 x = maybe_subsample_train_set(&n, x);
234 mean.clear();
mean.resize(d_in, 0.0);
237 for (
int i = 0; i < n; i++) {
238 for(
int j = 0; j < d_in; j++)
241 for(
int j = 0; j < d_in; j++)
246 for(
int j = 0; j < d_in; j++) printf(
"%g ",
mean[j]);
252 PCAMat.resize(d_in * d_in);
253 float * cov =
PCAMat.data();
256 for(
int i = 0; i < d_in; i++) {
257 for(
int j = 0; j < d_in; j++)
262 FINTEGER di = d_in, ni = n;
264 ssyrk_ (
"Up",
"Non transposed",
265 &di, &ni, &one, (
float*)x, &di, &one, cov, &di);
268 if(verbose && d_in <= 10) {
271 for(
int i = 0; i < d_in; i++) {
272 for(
int j = 0; j < d_in; j++)
273 printf(
"%10g ", *ci++);
280 FINTEGER info = 0, lwork = -1, di = d_in;
283 ssyev_ (
"Vectors as well",
"Upper",
284 &di, cov, &di,
eigenvalues.data(), &workq, &lwork, &info);
285 lwork = FINTEGER(workq);
286 float *work =
new float[lwork];
288 ssyev_ (
"Vectors as well",
"Upper",
289 &di, cov, &di,
eigenvalues.data(), work, &lwork, &info);
292 fprintf (stderr,
"WARN ssyev info returns %d, "
293 "a very bad PCA matrix is learnt\n",
300 if(verbose && d_in <= 10) {
301 printf(
"info=%ld new eigvals=[",
long(info));
302 for(
int j = 0; j < d_in; j++) printf(
"%g ",
eigenvalues[j]);
306 printf(
"eigenvecs=\n");
307 for(
int i = 0; i < d_in; i++) {
308 for(
int j = 0; j < d_in; j++)
309 printf(
"%10.4g ", *ci++);
318 for(
int i = 0; i < d_in / 2; i++) {
321 float *v1 = cov + i * d_in;
322 float *v2 = cov + (d_in - 1 - i) * d_in;
323 for(
int j = 0; j < d_in; j++)
324 std::swap(v1[j], v2[j]);
328 FAISS_ASSERT(!
"Gramm matrix version not implemented "
329 "-- provide more training examples than dimensions");
333 if (x != x_in)
delete [] x;
358 float *ai =
A.data();
359 for (
int i = 0; i <
d_out; i++) {
361 for(
int j = 0; j < d_in; j++)
369 std::vector <float> Ain;
377 for (
int i = 0; i <
d_out; i++) {
382 if (counter[j] < dsub && accu[j] < min_w) {
387 int row_dst = best_j * dsub + counter[best_j];
390 memcpy (&
A[row_dst * d_in], &Ain[i * d_in],
391 d_in *
sizeof (
A[0]));
395 printf(
" bin accu=[");
397 printf(
"%g ", accu[i]);
405 !
"both balancing bins and applying a random rotation "
406 "does not make sense");
413 for (
int i = 0; i <
d_out; i++) {
415 for(
int j = 0; j <
d_out; j++)
416 rr.
A[j * d_out + i] *= factor;
422 FINTEGER dii = d_in, doo =
d_out;
423 float one = 1.0, zero = 0.0;
425 sgemm_ (
"Not",
"Not", &dii, &doo, &doo,
426 &one,
PCAMat.data(), &dii, rr.
A.data(), &doo, &zero,
435 for (
int i = 0; i <
d_out; i++) {
437 for (
int j = 0; j < d_in; j++)
438 accu -=
mean[j] *
A[j + i * d_in];
448 !
"reverse only implemented for orthogonal transforms");
460 niter_pq (4), niter_pq_0 (40),
472 const float * x_in = x;
474 x = maybe_subsample_train_set (&n, x);
485 std::vector<float> r (d * d);
486 float * rotation = r.data();
487 float_randn (rotation, d * d, 1234);
488 printf(
"CS0: %016lx\n",
491 printf(
"CS1: %016lx\n",
498 printf (
"OPQMatrix::train: training an OPQ rotation matrix "
499 "for M=%d from %ld vectors in %dD -> %dD\n",
503 std::vector<float> xtrain (n * d);
506 std::vector<float> sum (d);
508 for (
size_t i = 0; i < n; i++) {
509 for (
int j = 0; j < d_in; j++)
512 for (
int i = 0; i < d; i++) sum[i] /= n;
513 float *yi = xtrain.data();
515 for (
size_t i = 0; i < n; i++) {
516 for (
int j = 0; j < d_in; j++)
517 *yi++ = *xi++ - sum[j];
523 if (
A.size () == 0) {
527 printf(
" OPQMatrix::train: making random %ld*%ld rotation\n",
529 float_randn (rotation, d * d, 1234);
534 FAISS_ASSERT (
A.size() == d * d2);
540 xproj (d2 * n), pq_recons (d2 * n), xxr (d * n),
543 std::vector<uint8_t> codes (
M * n);
546 for (
int iter = 0; iter <
niter; iter++) {
549 FINTEGER di = d, d2i = d2, ni = n;
550 float zero = 0, one = 1;
551 sgemm_ (
"Transposed",
"Not transposed",
555 &zero, xproj.data(), &d2i);
560 pq_regular.
cp.verbose = verbose;
561 pq_regular.train (n, xproj.data());
564 pq_regular.
decode (codes.data(), pq_recons.data(), n);
566 float pq_err =
fvec_L2sqr (pq_recons.data(), xproj.data(), n * d2) / n;
569 printf (
" Iteration %d (%d PQ iterations):"
570 "%.3f s, obj=%g\n", iter, pq_regular.
cp.
niter,
574 float *u = tmp.data(), *vt = &tmp [d * d];
575 float *sing_val = &tmp [2 * d * d];
576 FINTEGER di = d, d2i = d2, ni = n;
577 float one = 1, zero = 0;
580 sgemm_ (
"Not",
"Transposed",
582 &one, pq_recons.data(), &d2i,
584 &zero, xxr.data(), &d2i);
587 FINTEGER lwork = -1, info = -1;
590 sgesvd_ (
"All",
"All",
591 &d2i, &di, xxr.data(), &d2i,
594 &worksz, &lwork, &info);
597 std::vector<float> work (lwork);
599 sgesvd_ (
"All",
"All",
600 &d2i, &di, xxr.data(), &d2i,
603 work.data(), &lwork, &info);
605 sgemm_ (
"Transposed",
"Transposed",
607 &one, u, &di, vt, &d2i,
608 &zero, rotation, &di);
616 for (
long i = 0; i <
d_out; i++)
617 memmove (&
A[i * d_in], &
A[i * d],
sizeof(
A[0]) * d_in);
618 A.resize (d_in * d_out);
640 IndexPreTransform::IndexPreTransform ():
641 index(nullptr), own_fields (false)
646 IndexPreTransform::IndexPreTransform (
648 Index (index->d, index->metric_type),
649 index (index), own_fields (false)
658 IndexPreTransform::IndexPreTransform (
661 Index (index->d, index->metric_type),
662 index (index), own_fields (false)
665 prepend_transform (ltrans);
671 FAISS_ASSERT (ltrans->
d_out ==
d);
673 chain.insert (chain.begin(), ltrans);
679 void IndexPreTransform::set_typename ()
682 index_typename =
"PreLT[" +
index->index_typename +
"]";
686 IndexPreTransform::~IndexPreTransform ()
689 for (
int i = 0; i < chain.size(); i++)
700 int last_untrained = 0;
701 for (
int i = 0; i < chain.size(); i++)
702 if (!chain[i]->
is_trained) last_untrained = i;
704 const float *prev_x = x;
706 for (
int i = 0; i <= last_untrained; i++) {
707 if (i < chain.size()) {
710 ltrans->
train(n, prev_x);
714 if (i == last_untrained)
break;
716 float * xt = chain[i]->apply (n, prev_x);
717 if (prev_x != x)
delete [] prev_x;
721 if (prev_x != x)
delete [] prev_x;
728 const float *prev_x = x;
729 for (
int i = 0; i < chain.size(); i++) {
730 float * xt = chain[i]->apply (n, prev_x);
731 if (prev_x != x)
delete [] prev_x;
742 if (xt != x)
delete [] xt;
752 if (xt != x)
delete [] xt;
760 float *distances,
idx_t *labels)
const
765 if (xt != x)
delete [] xt;
783 float *x = chain.empty() ? recons :
new float [ni *
index->
d];
788 for (
int i = chain.size() - 1; i >= 0; i--) {
789 float *x_pre = i == 0 ? recons :
new float [chain[i]->d_in * ni];
790 chain [i]->reverse_transform (ni, x, x_pre);
803 RemapDimensionsTransform::RemapDimensionsTransform (
804 int d_in,
int d_out,
const int *map_in):
808 for (
int i = 0; i < d_out; i++) {
810 FAISS_ASSERT (map[i] == -1 || (map[i] >= 0 && map[i] < d_in));
814 RemapDimensionsTransform::RemapDimensionsTransform (
817 map.resize (d_out, -1);
821 for (
int i = 0; i < d_in; i++) {
822 map [i * d_out / d_in] = i;
825 for (
int i = 0; i <
d_out; i++) {
830 for (
int i = 0; i < d_in && i <
d_out; i++)
839 for (idx_t i = 0; i < n; i++) {
840 for (
int j = 0; j <
d_out; j++) {
841 xt[j] =
map[j] < 0 ? 0 : x[
map[j]];
851 memset (x, 0,
sizeof (*x) * n * d_in);
852 for (idx_t i = 0; i < n; i++) {
853 for (
int j = 0; j <
d_out; j++) {
854 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 add_with_ids(idx_t n, const float *x, const long *xids)
virtual 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
int max_points_per_d
if there are too many training points, resample
virtual void reconstruct_n(idx_t i0, idx_t ni, float *recons) const
virtual void add(idx_t n, const float *x)=0
virtual 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
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)
virtual void reverse_transform(idx_t n, const float *xt, float *x) const override
ClusteringParameters cp
parameters used during clustering
size_t ivec_checksum(size_t n, const int *a)
compute a checksum on a table.
virtual void reverse_transform(idx_t n, const float *xt, float *x) const override
virtual void reverse_transform(idx_t n, const float *xt, float *x) const override
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 train(idx_t n, const float *x)
bool random_rotation
random rotation after PCA
int max_points_per_centroid
to limit size of dataset