faiss 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/lib/faiss/version.rb +1 -1
  4. data/vendor/faiss/faiss/AutoTune.cpp +3 -1
  5. data/vendor/faiss/faiss/Clustering.cpp +9 -1
  6. data/vendor/faiss/faiss/IVFlib.cpp +14 -3
  7. data/vendor/faiss/faiss/Index.h +2 -2
  8. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +9 -10
  9. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +2 -3
  10. data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +1 -2
  11. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +4 -5
  12. data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
  13. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +2 -4
  14. data/vendor/faiss/faiss/IndexEDEN.cpp +273 -0
  15. data/vendor/faiss/faiss/IndexEDEN.h +57 -0
  16. data/vendor/faiss/faiss/IndexFastScan.cpp +15 -4
  17. data/vendor/faiss/faiss/IndexFlat.cpp +13 -50
  18. data/vendor/faiss/faiss/IndexHNSW.cpp +10 -11
  19. data/vendor/faiss/faiss/IndexIDMap.cpp +16 -3
  20. data/vendor/faiss/faiss/IndexIDMap.h +2 -0
  21. data/vendor/faiss/faiss/IndexIVF.cpp +17 -6
  22. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
  23. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +3 -4
  24. data/vendor/faiss/faiss/IndexIVFEDEN.cpp +302 -0
  25. data/vendor/faiss/faiss/IndexIVFEDEN.h +70 -0
  26. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +5 -6
  27. data/vendor/faiss/faiss/IndexIVFFlat.cpp +3 -4
  28. data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
  29. data/vendor/faiss/faiss/IndexIVFPQ.cpp +40 -22
  30. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
  31. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +19 -49
  32. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
  33. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
  34. data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
  35. data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
  36. data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
  37. data/vendor/faiss/faiss/IndexNNDescent.cpp +9 -2
  38. data/vendor/faiss/faiss/IndexNSG.cpp +7 -2
  39. data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
  40. data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
  41. data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
  42. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
  43. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
  44. data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
  45. data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
  46. data/vendor/faiss/faiss/IndexShards.cpp +2 -2
  47. data/vendor/faiss/faiss/IndexShardsIVF.cpp +2 -2
  48. data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
  49. data/vendor/faiss/faiss/SuperKMeans.cpp +256 -240
  50. data/vendor/faiss/faiss/SuperKMeans.h +30 -0
  51. data/vendor/faiss/faiss/VectorTransform.cpp +33 -2
  52. data/vendor/faiss/faiss/clone_index.cpp +5 -0
  53. data/vendor/faiss/faiss/factory_tools.cpp +47 -4
  54. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
  55. data/vendor/faiss/faiss/gpu/GpuIndex.h +34 -11
  56. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +47 -0
  57. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
  58. data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
  59. data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
  60. data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
  61. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
  62. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +1 -5
  63. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
  64. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
  65. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +2 -2
  66. data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
  67. data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
  68. data/vendor/faiss/faiss/impl/HNSW.cpp +109 -152
  69. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
  70. data/vendor/faiss/faiss/impl/NSG.cpp +3 -1
  71. data/vendor/faiss/faiss/impl/Panorama.h +9 -7
  72. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
  73. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +34 -22
  74. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +44 -36
  75. data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
  76. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +168 -67
  77. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +19 -0
  78. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +2 -11
  79. data/vendor/faiss/faiss/impl/ResultHandler.h +25 -31
  80. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +258 -57
  81. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +20 -0
  82. data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
  83. data/vendor/faiss/faiss/impl/VisitedTable.cpp +22 -2
  84. data/vendor/faiss/faiss/impl/VisitedTable.h +20 -0
  85. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +1 -1
  86. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
  87. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +35 -2
  88. data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
  89. data/vendor/faiss/faiss/impl/index_read.cpp +376 -36
  90. data/vendor/faiss/faiss/impl/index_write.cpp +55 -4
  91. data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
  92. data/vendor/faiss/faiss/impl/platform_macros.h +3 -1
  93. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
  94. data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
  95. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
  96. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
  97. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +4 -4
  98. data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
  99. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
  100. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
  101. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
  102. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
  103. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +882 -0
  104. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +9 -8
  105. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +85 -23
  106. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +30 -30
  107. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +4 -5
  108. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +101 -34
  109. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +136 -0
  110. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +16 -16
  111. data/vendor/faiss/faiss/impl/simd_dispatch.h +30 -9
  112. data/vendor/faiss/faiss/index_factory.cpp +32 -6
  113. data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
  114. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +2 -2
  115. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +19 -4
  116. data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
  117. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
  118. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
  119. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
  120. data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
  121. data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
  122. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +31 -1
  123. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +15 -2
  124. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +1 -2
  125. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
  126. data/vendor/faiss/faiss/utils/distances.cpp +14 -2
  127. data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -3
  128. data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
  129. data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
  130. data/vendor/faiss/faiss/utils/hamming.cpp +1 -1
  131. data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
  132. data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
  133. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
  134. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
  135. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +160 -18
  136. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
  137. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +273 -0
  138. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_spr.cpp +92 -0
  139. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
  140. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
  141. data/vendor/faiss/faiss/utils/simd_levels.cpp +44 -0
  142. data/vendor/faiss/faiss/utils/simd_levels.h +14 -0
  143. data/vendor/faiss/faiss/utils/utils.cpp +9 -27
  144. metadata +16 -1
@@ -25,11 +25,13 @@
25
25
  #include <faiss/Index2Layer.h>
26
26
  #include <faiss/IndexAdditiveQuantizer.h>
27
27
  #include <faiss/IndexAdditiveQuantizerFastScan.h>
28
+ #include <faiss/IndexEDEN.h>
28
29
  #include <faiss/IndexFlat.h>
29
30
  #include <faiss/IndexHNSW.h>
30
31
  #include <faiss/IndexIVF.h>
31
32
  #include <faiss/IndexIVFAdditiveQuantizer.h>
32
33
  #include <faiss/IndexIVFAdditiveQuantizerFastScan.h>
34
+ #include <faiss/IndexIVFEDEN.h>
33
35
  #include <faiss/IndexIVFFlat.h>
34
36
  #include <faiss/IndexIVFFlatPanorama.h>
35
37
  #include <faiss/IndexIVFIndependentQuantizer.h>
@@ -63,6 +65,7 @@
63
65
  #include <faiss/IndexScalarQuantizer.h>
64
66
  #include <faiss/MetaIndexes.h>
65
67
  #include <faiss/VectorTransform.h>
68
+ #include <faiss/impl/EDENQuantizer.h>
66
69
 
67
70
  #include <faiss/IndexBinaryFlat.h>
68
71
  #include <faiss/IndexBinaryFromFloat.h>
@@ -439,6 +442,20 @@ static void write_RaBitQuantizer(
439
442
  }
440
443
  }
441
444
 
445
+ static void write_EDENScalarQuantizer(
446
+ const ScalarQuantizer* sq,
447
+ MetricType metric_type,
448
+ EDENScaleType scale_type,
449
+ size_t full_code_size,
450
+ IOWriter* f) {
451
+ WRITE1(sq->d);
452
+ WRITE1(full_code_size);
453
+ WRITE1(metric_type);
454
+ WRITE1(sq->bits);
455
+ int scale_type_int = static_cast<int>(scale_type);
456
+ WRITE1(scale_type_int);
457
+ }
458
+
442
459
  static void write_direct_map(const DirectMap* dm, IOWriter* f) {
443
460
  char maintain_direct_map =
444
461
  (char)dm->type; // for backwards compatibility with bool
@@ -871,8 +888,12 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
871
888
  : dynamic_cast<const IndexHNSWSQ*>(idx) ? fourcc("IHNs")
872
889
  : dynamic_cast<const IndexHNSW2Level*>(idx) ? fourcc("IHN2")
873
890
  : dynamic_cast<const IndexHNSWCagra*>(idx) ? fourcc("IHc2")
891
+ : typeid(*idx) == typeid(IndexHNSW) ? fourcc("IH00")
874
892
  : 0;
875
- FAISS_THROW_IF_NOT(h != 0);
893
+ FAISS_THROW_IF_NOT_FMT(
894
+ h != 0,
895
+ "don't know how to serialize this IndexHNSW subtype: %s",
896
+ typeid(*idx).name());
876
897
  WRITE1(h);
877
898
  write_index_header(idxhnsw, f);
878
899
  if (h == fourcc("IHfP")) {
@@ -900,7 +921,8 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
900
921
  : dynamic_cast<const IndexNSGPQ*>(idx) ? fourcc("INSp")
901
922
  : dynamic_cast<const IndexNSGSQ*>(idx) ? fourcc("INSs")
902
923
  : 0;
903
- FAISS_THROW_IF_NOT(h != 0);
924
+ FAISS_THROW_IF_MSG(
925
+ h == 0, "don't know how to serialize this IndexNSG subtype");
904
926
  WRITE1(h);
905
927
  write_index_header(idxnsg, f);
906
928
  WRITE1(idxnsg->GK);
@@ -915,9 +937,11 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
915
937
  const IndexNNDescent* idxnnd =
916
938
  dynamic_cast<const IndexNNDescent*>(idx)) {
917
939
  auto idxnndflat = dynamic_cast<const IndexNNDescentFlat*>(idx);
918
- FAISS_THROW_IF_NOT(idxnndflat != nullptr);
940
+ FAISS_THROW_IF_NOT(idxnndflat);
919
941
  uint32_t h = fourcc("INNf");
920
- FAISS_THROW_IF_NOT(h != 0);
942
+ FAISS_THROW_IF_MSG(
943
+ h == 0,
944
+ "don't know how to serialize this IndexNNDescent subtype");
921
945
  WRITE1(h);
922
946
  write_index_header(idxnnd, f);
923
947
  write_NNDescent(&idxnnd->nndescent, f);
@@ -965,6 +989,33 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
965
989
  WRITE1(h);
966
990
  write_index_header(imm_2, f);
967
991
  write_index(imm_2->index, f);
992
+ } else if (const IndexEDEN* idxe = dynamic_cast<const IndexEDEN*>(idx)) {
993
+ uint32_t h = fourcc("IxEe");
994
+ WRITE1(h);
995
+ write_index_header(idx, f);
996
+ write_EDENScalarQuantizer(
997
+ &idxe->sq,
998
+ idxe->metric_type,
999
+ idxe->scale_type,
1000
+ idxe->code_size,
1001
+ f);
1002
+ WRITEVECTOR(idxe->codes);
1003
+ WRITEVECTOR(idxe->center);
1004
+ } else if (
1005
+ const IndexIVFEDEN* iveden =
1006
+ dynamic_cast<const IndexIVFEDEN*>(idx)) {
1007
+ uint32_t h = fourcc("IwEe");
1008
+ WRITE1(h);
1009
+ write_ivf_header(iveden, f);
1010
+ write_EDENScalarQuantizer(
1011
+ &iveden->sq,
1012
+ iveden->metric_type,
1013
+ iveden->scale_type,
1014
+ iveden->code_size,
1015
+ f);
1016
+ WRITE1(iveden->code_size);
1017
+ WRITE1(iveden->by_residual);
1018
+ write_InvertedLists(iveden->invlists, f);
968
1019
  } else if (
969
1020
  const IndexRaBitQFastScan* idxqfs =
970
1021
  dynamic_cast<const IndexRaBitQFastScan*>(idx)) {
@@ -557,12 +557,11 @@ ZnSphereCodecRec::ZnSphereCodecRec(int dim_in, int r2_in)
557
557
 
558
558
  decode_cache.resize((r2 + 1));
559
559
 
560
- // The decode cache stores total_cache_entries * dimsub floats.
561
- // Cap at 2^27 total floats (~512 MB), aligned with the nv_cum
562
- // memory cap above which also uses 2^27 entries. The entry count
563
- // grows as the number of lattice points in dimension
564
- // 2^cache_level, which is O(r2^(dim/2)) -- much faster than the
565
- // O(r2^2) growth of nv_cum.
560
+ // The decode cache stores total_cache_entries * dimsub floats and is
561
+ // built with one decode() call per entry. The entry count grows as the
562
+ // number of lattice points in dimension 2^cache_level, which is
563
+ // O(r2^(dim/2)) -- much faster than the O(r2^2) growth of nv_cum, so it
564
+ // is bounded per-r2sub in the loop below.
566
565
  size_t total_cache_entries = 0;
567
566
  int dimsub = (1 << cache_level);
568
567
 
@@ -571,9 +570,9 @@ ZnSphereCodecRec::ZnSphereCodecRec(int dim_in, int r2_in)
571
570
  uint64_t nvi = get_nv(ld, r2sub);
572
571
  total_cache_entries += nvi;
573
572
  FAISS_THROW_IF_NOT_MSG(
574
- total_cache_entries <= (size_t(1) << 27) / dimsub,
575
- "ZnSphereCodecRec: r2 too large, decode cache "
576
- "would require excessive memory");
573
+ total_cache_entries <= (size_t(1) << 27) / (size_t)dim,
574
+ "ZnSphereCodecRec: r2/dim too large, decode cache "
575
+ "would require excessive memory and computation");
577
576
  std::vector<float>& cache = decode_cache[r2sub];
578
577
  cache.resize(nvi * dimsub);
579
578
  std::vector<float> c(dim);
@@ -25,7 +25,9 @@
25
25
 
26
26
  #define strtok_r strtok_s
27
27
 
28
- #ifdef _MSC_VER
28
+ // clang-cl defines _MSC_VER but provides __PRETTY_FUNCTION__ itself, in
29
+ // clang's format. Redefining it there breaks code that parses it.
30
+ #if defined(_MSC_VER) && !defined(__clang__)
29
31
  #define __PRETTY_FUNCTION__ __FUNCSIG__
30
32
  #endif // _MSC_VER
31
33
 
@@ -0,0 +1,284 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ // -*- c++ -*-
9
+
10
+ #ifdef COMPILE_SIMD_AVX512
11
+
12
+ #include <faiss/impl/PolysemousTraining.h>
13
+ #include <faiss/impl/polysemous_training/dispatch.h>
14
+ #include <faiss/utils/popcount.h>
15
+
16
+ #include <immintrin.h>
17
+ #include <cstdint>
18
+
19
+ namespace faiss {
20
+ namespace {
21
+
22
+ inline int hamming_dis(uint64_t a, uint64_t b) {
23
+ return popcount64(a ^ b);
24
+ }
25
+
26
+ inline double sqr(double x) {
27
+ return x * x;
28
+ }
29
+
30
+ inline __m512i popcnt_512(__m512i v) {
31
+ #ifdef __AVX512VPOPCNTDQ__
32
+ return _mm512_popcnt_epi64(v);
33
+ #else
34
+ const __m128i nibble_popcount =
35
+ _mm_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);
36
+ const __m512i lookup = _mm512_broadcast_i32x4(nibble_popcount);
37
+
38
+ const __m512i low_mask = _mm512_set1_epi8(0x0f);
39
+ const __m512i lo = _mm512_and_si512(v, low_mask);
40
+ const __m512i hi = _mm512_and_si512(_mm512_srli_epi16(v, 4), low_mask);
41
+
42
+ const __m512i popcnt_lo = _mm512_shuffle_epi8(lookup, lo);
43
+ const __m512i popcnt_hi = _mm512_shuffle_epi8(lookup, hi);
44
+ const __m512i popcnt_bytes = _mm512_add_epi8(popcnt_lo, popcnt_hi);
45
+
46
+ return _mm512_sad_epu8(popcnt_bytes, _mm512_setzero_si512());
47
+ #endif
48
+ }
49
+
50
+ } // namespace
51
+
52
+ namespace polysemous_training {
53
+
54
+ template <>
55
+ double hamming_compute_cost<SIMDLevel::AVX512>(
56
+ int n,
57
+ const int* perm,
58
+ const double* target_dis,
59
+ const double* weights) {
60
+ double total_cost = 0.0;
61
+ for (int i = 0; i < n; i++) {
62
+ __m512d cost_vec = _mm512_setzero_pd();
63
+ const __m512i perm_i_vec = _mm512_set1_epi64(perm[i]);
64
+ const int bro = i * n;
65
+ int j = 0;
66
+ for (; j <= n - 8; j += 8) {
67
+ const __m512d wanted_vec = _mm512_loadu_pd(&target_dis[bro + j]);
68
+ const __m512d w_vec = _mm512_loadu_pd(&weights[bro + j]);
69
+ const __m256i pj32 = _mm256_loadu_si256((__m256i const*)&perm[j]);
70
+ const __m512i pj64 = _mm512_cvtepi32_epi64(pj32);
71
+ const __m512i xor_res = _mm512_xor_si512(perm_i_vec, pj64);
72
+ const __m512d actual_vec = _mm512_cvtepi64_pd(popcnt_512(xor_res));
73
+ const __m512d diff = _mm512_sub_pd(wanted_vec, actual_vec);
74
+ cost_vec =
75
+ _mm512_fmadd_pd(w_vec, _mm512_mul_pd(diff, diff), cost_vec);
76
+ }
77
+ total_cost += _mm512_reduce_add_pd(cost_vec);
78
+ for (; j < n; j++) {
79
+ double wanted = target_dis[bro + j];
80
+ double w = weights[bro + j];
81
+ double actual = hamming_dis(perm[i], perm[j]);
82
+ total_cost += w * sqr(wanted - actual);
83
+ }
84
+ }
85
+ return total_cost;
86
+ }
87
+
88
+ template <>
89
+ double hamming_cost_update<SIMDLevel::AVX512>(
90
+ int n,
91
+ const int* perm,
92
+ int iw,
93
+ int jw,
94
+ const double* target_dis,
95
+ const double* weights) {
96
+ double delta_cost_scalar = 0;
97
+ const __m512i v_idx_base = _mm512_setr_epi64(0, 1, 2, 3, 4, 5, 6, 7);
98
+ // Loop-invariant across the j-loop and both process_row calls.
99
+ const __m512i v_iw = _mm512_set1_epi64(iw);
100
+ const __m512i v_jw = _mm512_set1_epi64(jw);
101
+ const __m512i v_perm_iw = _mm512_set1_epi64(perm[iw]);
102
+ const __m512i v_perm_jw = _mm512_set1_epi64(perm[jw]);
103
+ __m512d delta_cost_vec = _mm512_setzero_pd();
104
+
105
+ auto process_row = [&](int row, int old_pi, int new_pi) {
106
+ const int bro = row * n;
107
+ const __m512i v_old = _mm512_set1_epi64(old_pi);
108
+ const __m512i v_new = _mm512_set1_epi64(new_pi);
109
+ int j = 0;
110
+ for (; j <= n - 8; j += 8) {
111
+ __m512d wv = _mm512_loadu_pd(&target_dis[bro + j]);
112
+ __m512d ww = _mm512_loadu_pd(&weights[bro + j]);
113
+ __m256i pj32 = _mm256_loadu_si256((__m256i const*)&perm[j]);
114
+ __m512i pjv = _mm512_cvtepi32_epi64(pj32);
115
+ __m512d av = _mm512_cvtepi64_pd(
116
+ popcnt_512(_mm512_xor_si512(v_old, pjv)));
117
+ __m512d to = _mm512_sub_pd(wv, av);
118
+ to = _mm512_mul_pd(to, to);
119
+ delta_cost_vec = _mm512_fnmadd_pd(ww, to, delta_cost_vec);
120
+
121
+ __m512i ji = _mm512_add_epi64(_mm512_set1_epi64(j), v_idx_base);
122
+ __mmask8 miw = _mm512_cmpeq_epi64_mask(ji, v_iw);
123
+ __mmask8 mjw = _mm512_cmpeq_epi64_mask(ji, v_jw);
124
+ __m512i pnj = _mm512_mask_blend_epi64(mjw, pjv, v_perm_iw);
125
+ pnj = _mm512_mask_blend_epi64(miw, pnj, v_perm_jw);
126
+ __m512d nav = _mm512_cvtepi64_pd(
127
+ popcnt_512(_mm512_xor_si512(v_new, pnj)));
128
+ __m512d tn = _mm512_sub_pd(wv, nav);
129
+ tn = _mm512_mul_pd(tn, tn);
130
+ delta_cost_vec = _mm512_fmadd_pd(ww, tn, delta_cost_vec);
131
+ }
132
+ for (; j < n; j++) {
133
+ double wanted = target_dis[bro + j];
134
+ double w = weights[bro + j];
135
+ double actual = hamming_dis(old_pi, perm[j]);
136
+ delta_cost_scalar -= w * sqr(wanted - actual);
137
+ double new_actual = hamming_dis(
138
+ new_pi,
139
+ perm[j == iw ? jw
140
+ : j == jw ? iw
141
+ : j]);
142
+ delta_cost_scalar += w * sqr(wanted - new_actual);
143
+ }
144
+ };
145
+ process_row(iw, perm[iw], perm[jw]);
146
+ process_row(jw, perm[jw], perm[iw]);
147
+
148
+ for (int i = 0; i < n; ++i) {
149
+ if (i == iw || i == jw)
150
+ continue;
151
+ int j = iw;
152
+ {
153
+ double wanted = target_dis[i * n + j];
154
+ double w = weights[i * n + j];
155
+ delta_cost_scalar -=
156
+ w * sqr(wanted - hamming_dis(perm[i], perm[j]));
157
+ delta_cost_scalar +=
158
+ w * sqr(wanted - hamming_dis(perm[i], perm[jw]));
159
+ }
160
+ j = jw;
161
+ {
162
+ double wanted = target_dis[i * n + j];
163
+ double w = weights[i * n + j];
164
+ delta_cost_scalar -=
165
+ w * sqr(wanted - hamming_dis(perm[i], perm[j]));
166
+ delta_cost_scalar +=
167
+ w * sqr(wanted - hamming_dis(perm[i], perm[iw]));
168
+ }
169
+ }
170
+ return _mm512_reduce_add_pd(delta_cost_vec) + delta_cost_scalar;
171
+ }
172
+
173
+ template <>
174
+ double distances_compute_cost<SIMDLevel::AVX512>(
175
+ const ReproduceDistancesObjective& obj,
176
+ const int* perm) {
177
+ const int n = obj.n;
178
+ double total_cost = 0.0;
179
+ for (int i = 0; i < n; ++i) {
180
+ const int pi = perm[i];
181
+ const int bro_t = i * n;
182
+ const int bro_s = pi * n;
183
+ __m512d sum = _mm512_setzero_pd();
184
+ int j = 0;
185
+ for (; j <= n - 8; j += 8) {
186
+ __m512d wv = _mm512_loadu_pd(&obj.target_dis[bro_t + j]);
187
+ __m512d ww = _mm512_loadu_pd(&obj.weights[bro_t + j]);
188
+ __m256i pj = _mm256_loadu_si256(
189
+ reinterpret_cast<const __m256i*>(&perm[j]));
190
+ __m256i idx = _mm256_add_epi32(_mm256_set1_epi32(bro_s), pj);
191
+ __m512d av = _mm512_i32gather_pd(idx, obj.source_dis.data(), 8);
192
+ __m512d d = _mm512_sub_pd(wv, av);
193
+ sum = _mm512_fmadd_pd(_mm512_mul_pd(d, d), ww, sum);
194
+ }
195
+ total_cost += _mm512_reduce_add_pd(sum);
196
+ for (; j < n; ++j) {
197
+ double wanted = obj.target_dis[bro_t + j];
198
+ double w = obj.weights[bro_t + j];
199
+ double actual = obj.get_source_dis(pi, perm[j]);
200
+ total_cost += w * sqr(wanted - actual);
201
+ }
202
+ }
203
+ return total_cost;
204
+ }
205
+
206
+ template <>
207
+ double distances_cost_update<SIMDLevel::AVX512>(
208
+ const ReproduceDistancesObjective& obj,
209
+ const int* perm,
210
+ int iw,
211
+ int jw) {
212
+ const int n = obj.n;
213
+ double delta_cost = 0.0;
214
+ const int p_iw = perm[iw], p_jw = perm[jw];
215
+ const __m256i v_joff = _mm256_set_epi32(7, 6, 5, 4, 3, 2, 1, 0);
216
+ const __m256i vi = _mm256_set1_epi32(iw);
217
+ const __m256i vj = _mm256_set1_epi32(jw);
218
+ const __m256i vpi = _mm256_set1_epi32(p_iw);
219
+ const __m256i vpj = _mm256_set1_epi32(p_jw);
220
+ const __m256i vpin = _mm256_set1_epi32(p_iw * n);
221
+ const __m256i vpjn = _mm256_set1_epi32(p_jw * n);
222
+
223
+ auto process_row =
224
+ [&](int row, int old_p, int new_p, __m256i old_pn, __m256i new_pn) {
225
+ const int bro = row * n;
226
+ __m512d dv = _mm512_setzero_pd();
227
+ int j = 0;
228
+ for (; j <= n - 8; j += 8) {
229
+ __m512d wv = _mm512_loadu_pd(&obj.target_dis[bro + j]);
230
+ __m512d ww = _mm512_loadu_pd(&obj.weights[bro + j]);
231
+ __m256i pjv = _mm256_loadu_si256(
232
+ reinterpret_cast<const __m256i*>(&perm[j]));
233
+ __m256i ia = _mm256_add_epi32(old_pn, pjv);
234
+ __m512d av =
235
+ _mm512_i32gather_pd(ia, obj.source_dis.data(), 8);
236
+ __m512d da = _mm512_sub_pd(wv, av);
237
+ dv = _mm512_fnmadd_pd(ww, _mm512_mul_pd(da, da), dv);
238
+ __m256i vk = _mm256_add_epi32(_mm256_set1_epi32(j), v_joff);
239
+ __mmask8 mi = _mm256_cmpeq_epi32_mask(vk, vi);
240
+ __mmask8 mj = _mm256_cmpeq_epi32_mask(vk, vj);
241
+ __m256i pnj = _mm256_mask_blend_epi32(mi, pjv, vpj);
242
+ pnj = _mm256_mask_blend_epi32(mj, pnj, vpi);
243
+ __m256i in2 = _mm256_add_epi32(new_pn, pnj);
244
+ __m512d nav =
245
+ _mm512_i32gather_pd(in2, obj.source_dis.data(), 8);
246
+ __m512d dn = _mm512_sub_pd(wv, nav);
247
+ dv = _mm512_fmadd_pd(ww, _mm512_mul_pd(dn, dn), dv);
248
+ }
249
+ delta_cost += _mm512_reduce_add_pd(dv);
250
+ for (; j < n; ++j) {
251
+ double wanted = obj.target_dis[bro + j];
252
+ double w = obj.weights[bro + j];
253
+ double actual = obj.get_source_dis(old_p, perm[j]);
254
+ delta_cost -= w * sqr(wanted - actual);
255
+ int pnj = (j == iw) ? p_jw : ((j == jw) ? p_iw : perm[j]);
256
+ double na = obj.get_source_dis(new_p, pnj);
257
+ delta_cost += w * sqr(wanted - na);
258
+ }
259
+ };
260
+ process_row(iw, p_iw, p_jw, vpin, vpjn);
261
+ process_row(jw, p_jw, p_iw, vpjn, vpin);
262
+
263
+ for (int i = 0; i < n; ++i) {
264
+ if (i == iw || i == jw)
265
+ continue;
266
+ double wanted = obj.target_dis[i * n + iw], w = obj.weights[i * n + iw];
267
+ double actual = obj.get_source_dis(perm[i], p_iw);
268
+ delta_cost -= w * sqr(wanted - actual);
269
+ double na = obj.get_source_dis(perm[i], p_jw);
270
+ delta_cost += w * sqr(wanted - na);
271
+ wanted = obj.target_dis[i * n + jw];
272
+ w = obj.weights[i * n + jw];
273
+ actual = obj.get_source_dis(perm[i], p_jw);
274
+ delta_cost -= w * sqr(wanted - actual);
275
+ na = obj.get_source_dis(perm[i], p_iw);
276
+ delta_cost += w * sqr(wanted - na);
277
+ }
278
+ return delta_cost;
279
+ }
280
+
281
+ } // namespace polysemous_training
282
+ } // namespace faiss
283
+
284
+ #endif // COMPILE_SIMD_AVX512
@@ -0,0 +1,115 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ #pragma once
9
+
10
+ #include <faiss/utils/simd_levels.h>
11
+
12
+ namespace faiss {
13
+
14
+ struct ReproduceDistancesObjective;
15
+
16
+ namespace polysemous_training {
17
+
18
+ // Levels with a dedicated kernel: scalar (NONE) plus AVX-512. Any other runtime
19
+ // level (AVX2, NEON, ...) falls back to NONE via with_selected_simd_levels /
20
+ // get_simd_fallback, and AVX512_SPR reuses the AVX-512 kernel.
21
+ constexpr int SIMD_LEVELS =
22
+ (1 << int(SIMDLevel::NONE)) | (1 << int(SIMDLevel::AVX512));
23
+
24
+ /// compute_cost for ReproduceWithHammingObjective.
25
+ /// Parameters mirror the objective's fields to avoid exposing the
26
+ /// anonymous-namespace struct.
27
+ template <SIMDLevel SL>
28
+ double hamming_compute_cost(
29
+ int n,
30
+ const int* perm,
31
+ const double* target_dis,
32
+ const double* weights);
33
+
34
+ /// cost_update for ReproduceWithHammingObjective.
35
+ template <SIMDLevel SL>
36
+ double hamming_cost_update(
37
+ int n,
38
+ const int* perm,
39
+ int iw,
40
+ int jw,
41
+ const double* target_dis,
42
+ const double* weights);
43
+
44
+ /// compute_cost for ReproduceDistancesObjective.
45
+ template <SIMDLevel SL>
46
+ double distances_compute_cost(
47
+ const ReproduceDistancesObjective& obj,
48
+ const int* perm);
49
+
50
+ /// cost_update for ReproduceDistancesObjective.
51
+ template <SIMDLevel SL>
52
+ double distances_cost_update(
53
+ const ReproduceDistancesObjective& obj,
54
+ const int* perm,
55
+ int iw,
56
+ int jw);
57
+
58
+ // The scalar (NONE) specializations are defined in PolysemousTraining.cpp; the
59
+ // AVX-512 specializations are defined in polysemous_training/avx512.cpp. Both
60
+ // are declared here so callers instantiate the out-of-line definition rather
61
+ // than implicitly instantiating the (undefined) primary template.
62
+ template <>
63
+ double hamming_compute_cost<SIMDLevel::NONE>(
64
+ int n,
65
+ const int* perm,
66
+ const double* target_dis,
67
+ const double* weights);
68
+ template <>
69
+ double hamming_compute_cost<SIMDLevel::AVX512>(
70
+ int n,
71
+ const int* perm,
72
+ const double* target_dis,
73
+ const double* weights);
74
+
75
+ template <>
76
+ double hamming_cost_update<SIMDLevel::NONE>(
77
+ int n,
78
+ const int* perm,
79
+ int iw,
80
+ int jw,
81
+ const double* target_dis,
82
+ const double* weights);
83
+ template <>
84
+ double hamming_cost_update<SIMDLevel::AVX512>(
85
+ int n,
86
+ const int* perm,
87
+ int iw,
88
+ int jw,
89
+ const double* target_dis,
90
+ const double* weights);
91
+
92
+ template <>
93
+ double distances_compute_cost<SIMDLevel::NONE>(
94
+ const ReproduceDistancesObjective& obj,
95
+ const int* perm);
96
+ template <>
97
+ double distances_compute_cost<SIMDLevel::AVX512>(
98
+ const ReproduceDistancesObjective& obj,
99
+ const int* perm);
100
+
101
+ template <>
102
+ double distances_cost_update<SIMDLevel::NONE>(
103
+ const ReproduceDistancesObjective& obj,
104
+ const int* perm,
105
+ int iw,
106
+ int jw);
107
+ template <>
108
+ double distances_cost_update<SIMDLevel::AVX512>(
109
+ const ReproduceDistancesObjective& obj,
110
+ const int* perm,
111
+ int iw,
112
+ int jw);
113
+
114
+ } // namespace polysemous_training
115
+ } // namespace faiss
@@ -7,7 +7,6 @@
7
7
 
8
8
  #include <faiss/impl/pq_code_distance/IVFPQ_QueryTables.h>
9
9
 
10
- #include <faiss/Clustering.h>
11
10
  #include <faiss/utils/distances_dispatch.h>
12
11
 
13
12
  namespace faiss {
@@ -20,23 +20,39 @@ namespace faiss {
20
20
  namespace pq_code_distance {
21
21
 
22
22
  template <class PQCodeDist>
23
- struct PQDistanceComputer : FlatCodesDistanceComputer {
23
+ struct PQDistanceComputer final : FlatCodesDistanceComputer {
24
24
  using PQDecoder = typename PQCodeDist::PQDecoder;
25
- size_t d;
26
25
  MetricType metric;
27
- idx_t nb;
28
26
  const ProductQuantizer& pq;
29
27
  const float* sdc;
30
28
  std::vector<float> precomputed_table;
31
- size_t ndis;
32
- const float* q;
33
29
 
34
30
  float distance_to_code(const uint8_t* code) final {
35
- ndis++;
36
-
37
- float dis = PQCodeDist::distance_single_code(
31
+ return PQCodeDist::distance_single_code(
38
32
  pq.M, pq.nbits, precomputed_table.data(), code);
39
- return dis;
33
+ }
34
+
35
+ void distance_to_code_batch_4(
36
+ const uint8_t* c1,
37
+ const uint8_t* c2,
38
+ const uint8_t* c3,
39
+ const uint8_t* c4,
40
+ float& d1,
41
+ float& d2,
42
+ float& d3,
43
+ float& d4) override {
44
+ PQCodeDist::distance_four_codes(
45
+ pq.M,
46
+ pq.nbits,
47
+ precomputed_table.data(),
48
+ c1,
49
+ c2,
50
+ c3,
51
+ c4,
52
+ d1,
53
+ d2,
54
+ d3,
55
+ d4);
40
56
  }
41
57
 
42
58
  float symmetric_dis(idx_t i, idx_t j) override {
@@ -50,7 +66,6 @@ struct PQDistanceComputer : FlatCodesDistanceComputer {
50
66
  accu += sdci[codei.decode() + (codej.decode() << codei.nbits)];
51
67
  sdci += uint64_t(1) << (2 * codei.nbits);
52
68
  }
53
- ndis++;
54
69
  return accu;
55
70
  }
56
71
 
@@ -58,18 +73,14 @@ struct PQDistanceComputer : FlatCodesDistanceComputer {
58
73
  : FlatCodesDistanceComputer(
59
74
  storage.codes.data(),
60
75
  storage.code_size),
61
- pq(storage.pq),
62
- q(nullptr) {
76
+ pq(storage.pq) {
63
77
  precomputed_table.resize(pq.M * pq.ksub);
64
- nb = storage.ntotal;
65
- d = storage.d;
66
78
  metric = storage.metric_type;
67
79
  if (pq.sdc_table.size() == pq.ksub * pq.ksub * pq.M) {
68
80
  sdc = pq.sdc_table.data();
69
81
  } else {
70
82
  sdc = nullptr;
71
83
  }
72
- ndis = 0;
73
84
  }
74
85
 
75
86
  void set_query(const float* x) override {
@@ -9,15 +9,15 @@
9
9
 
10
10
  #define THE_SIMD_LEVEL SIMDLevel::AVX2
11
11
 
12
- // NOLINTNEXTLINE(facebook-hte-InlineHeader)
12
+ // NOLINTNEXTLINE(facebook-hte-InlineHeader,facebook-unused-include-check)
13
13
  #include <faiss/impl/pq_code_distance/pq_code_distance-avx2.h>
14
- // NOLINTNEXTLINE(facebook-hte-InlineHeader)
14
+ // NOLINTNEXTLINE(facebook-hte-InlineHeader,facebook-unused-include-check)
15
15
  #include <faiss/utils/hamming_distance/hamming_computer-avx2.h>
16
16
  // NOLINTNEXTLINE(facebook-hte-InlineHeader,facebook-unused-include-check)
17
17
  #include <faiss/impl/pq_code_distance/pq_scan_impl.h>
18
- // NOLINTNEXTLINE(facebook-hte-InlineHeader)
18
+ // NOLINTNEXTLINE(facebook-hte-InlineHeader,facebook-unused-include-check)
19
19
  #include <faiss/impl/pq_code_distance/PQDistanceComputer_impl.h>
20
- // NOLINTNEXTLINE(facebook-hte-InlineHeader)
20
+ // NOLINTNEXTLINE(facebook-hte-InlineHeader,facebook-unused-include-check)
21
21
  #include <faiss/impl/pq_code_distance/IVFPQScanner_impl.h>
22
22
 
23
23
  #endif // COMPILE_SIMD_AVX2